簡體   English   中英

查找iOS設備的法線向量

[英]Finding normal vector to iOS device

我想用CMAttitude來了解iPad / iPhone屏幕玻璃的正常向量(相對於地面)。 因此,我會得到以下矢量:

在此輸入圖像描述

請注意,這與方向不同,因為我不關心設備如何圍繞z軸旋轉。 因此,如果我把iPad放在我的頭頂朝下,它會讀取(0,-1,0),即使我將它旋轉到我頭頂上方(如直升機),它仍將繼續讀取(0, - 1,0):

在此輸入圖像描述

我覺得這可能很簡單,但由於我是四元數的新手,並且不完全理解設備運動的參考框架選項,所以它一整天都在躲避我。

  1. 在你的情況下,我們可以說設備的旋轉等於設備法線的旋轉(圍繞法線本身的旋轉就像你指定的那樣被忽略)
  2. CMAttitude你可以得到通過CMMotionManager.deviceMotion提供相對於參考系的轉動。 它的屬性四元數,旋轉矩陣和歐拉角只是不同的表示。
  3. 使用CMMotionManager的startDeviceMotionUpdatesUsingReferenceFrame方法啟動設備運動更新時,可以指定參考框架。 在iOS 4之前,你必須使用multiplyByInverseOfAttitude

將這些放在一起,當設備面朝上放在桌子上時,你必須以正確的方式將四元數與法線向量相乘。 現在我們需要這種表示旋轉的四元數乘法的正確方法 :根據旋轉向量,這可以通過以下方式完成:

n = q * e * q'其中q是由CMAttitude [w,(x,y,z)]傳遞的四元數, q'是它的共軛[w,( - x,-y,-z)], e是面向正常[4,(0,0,1)]的四元數表示。 不幸的是,Apple的CMQuaternion是struct,因此你需要一個小幫手類。

Quaternion e = [[Quaternion alloc] initWithValues:0 y:0 z:1 w:0];
CMQuaternion cm = deviceMotion.attitude.quaternion;
Quaternion quat = [[Quaternion alloc] initWithValues:cm.x y:cm.y z:cm.z w: cm.w];
Quaternion quatConjugate = [[Quaternion alloc] initWithValues:-cm.x y:-cm.y z:-cm.z w: cm.w];
[quat multiplyWithRight:e];
[quat multiplyWithRight:quatConjugate];
// quat.x, .y, .z contain your normal

Quaternion.h:

@interface Quaternion : NSObject {
    double w;
    double x;
    double y;
    double z;
}

@property(readwrite, assign)double w;
@property(readwrite, assign)double x;
@property(readwrite, assign)double y;
@property(readwrite, assign)double z;

Quaternion.m:

- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = w*q.w - x*q.x - y*q.y - z*q.z;
    double newX = w*q.x + x*q.w + y*q.z - z*q.y;
    double newY = w*q.y + y*q.w + z*q.x - x*q.z;
    double newZ = w*q.z + z*q.w + x*q.y - y*q.x;
    w = newW;
    x = newX;
    y = newY;
    z = newZ;
    // one multiplication won't denormalise but when multipling again and again 
    // we should assure that the result is normalised
    return self;
}

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 {
        if ((self = [super init])) {
            x = x2; y = y2; z = z2; w = w2;
        }
        return self;
}

我知道四元數在開始時有點奇怪,但是一旦你有了一個想法它們真的很棒。 它幫助我將四元數想象為圍繞向量(x,y,z)的旋轉,w是角度的(余弦)。

如果您需要對它們做更多的工作,請查看cocoamath開源項目。 Quaternion類及其擴展QuaternionOperations是一個很好的起點。

為了完整起見,是的,您也可以使用矩陣乘法:

n = M * e

但我更喜歡四元數方式,它可以為您節省所有三角函數的麻煩並且表現更好。

感謝凱為解決方案的起點。 以下是我需要它的人的實現。 我根據凱的建議為我的情況做了一些小小的調整。 作為一個抬頭,我正在使用僅限風景的演示文稿。 我有更新變量_isLandscapeLeft的代碼,以對向量的方向進行必要的調整。

Quaternion.h

    @interface Quaternion : NSObject{
    //double w;
    //double x;
    //double y;
    //double z;
}

@property(readwrite, assign)double w;
@property(readwrite, assign)double x;
@property(readwrite, assign)double y;
@property(readwrite, assign)double z;

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2;
- (Quaternion*) multiplyWithRight:(Quaternion*)q;
@end

Quaternion.m

#import "Quaternion.h"

@implementation Quaternion


- (Quaternion*) multiplyWithRight:(Quaternion*)q {
    double newW = _w*q.w - _x*q.x - _y*q.y - _z*q.z;
    double newX = _w*q.x + _x*q.w + _y*q.z - _z*q.y;
    double newY = _w*q.y + _y*q.w + _z*q.x - _x*q.z;
    double newZ = _w*q.z + _z*q.w + _x*q.y - _y*q.x;
    _w = newW;
    _x = newX;
    _y = newY;
    _z = newZ;
    // one multiplication won't denormalise but when multipling again and again
    // we should assure that the result is normalised
    return self;
}

- (id) initWithValues:(double)w2 x:(double)x2 y:(double)y2 z:(double)z2 {
    if ((self = [super init])) {
        _x = x2; _y = y2; _z = z2; _w = w2;
    }
    return self;
}


@end

我的游戲類使用四元數進行拍攝:

-(void)fireWeapon{
    ProjectileBaseClass *bullet = [[ProjectileBaseClass alloc] init];
    bullet.position = SCNVector3Make(0, 1, 0);
    [self.rootNode addChildNode:bullet];

    Quaternion *e = [[Quaternion alloc] initWithValues:0 x:0 y:0 z:1];
    CMQuaternion cm = _currentAttitude.quaternion;
    Quaternion *quat = [[Quaternion alloc] initWithValues:cm.w x:cm.x y:cm.y z:cm.z];
    Quaternion *quatConjugate = [[Quaternion alloc] initWithValues:cm.w x:-cm.x y:-cm.y z:-cm.z];
    quat = [quat multiplyWithRight:e];
    quat = [quat multiplyWithRight:quatConjugate];
    SCNVector3 directionToShoot;
    if (_isLandscapeLeft) {
        directionToShoot = SCNVector3Make(quat.y, -quat.x, -quat.z);

    }else{
        directionToShoot = SCNVector3Make(-quat.y, quat.x, -quat.z);

    }

    SCNAction *shootBullet = [SCNAction moveBy:directionToShoot duration:.1];
    [bullet runAction:[SCNAction repeatActionForever:shootBullet]];
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM