通常在蘋果的 ARKit 和 SceneKit 中,用到的矩陣有三種SCNMatrix4
,simd_float4x4
和GLKMatrix4
。node
GLKMatrix4
是從 OpenGL 框架 GLKit 中帶過來的,各類函數很全面。
SCNMatrix4
最初是給 SceneKit 使用的,後來擴展到 ARKit 中,矩陣操做函數不夠全面,好比沒有轉置函數 transpose。
simd_float4x4
看名字就知道是 simd 類型的,是後來爲了取代SCNMatrix4
來推出的,各類函數也很全面。數組
上面三種類型本質都是結構體,互相轉換的方法也在蘋果文檔中給出了,分別爲:bash
SCNMatrix4 SCNMatrix4FromMat4(simd_float4x4 m) {
return *(SCNMatrix4 *)&m;
}
simd_float4x4 SCNMatrix4ToMat4(SCNMatrix4 m) {
return (simd_float4x4){
.columns[0] = simd_make_float4(m.m11, m.m12, m.m13, m.m14),
.columns[1] = simd_make_float4(m.m21, m.m22, m.m23, m.m24),
.columns[2] = simd_make_float4(m.m31, m.m32, m.m33, m.m34),
.columns[3] = simd_make_float4(m.m41, m.m42, m.m43, m.m44)
};
}
GLKMatrix4 SCNMatrix4ToGLKMatrix4(SCNMatrix4 mat);//未公開方法實現的代碼,不過都是結構體,估計是直接強制轉換的
SCNMatrix4 SCNMatrix4FromGLKMatrix4(GLKMatrix4 mat);//未公開方法實現的代碼
複製代碼
通常在蘋果的 Demo 和說明中,用到的矩陣SCNMatrix4
,simd_float4x4
和GLKMatrix4
都是列主序的。
框架
好比在 SCNMatrix4 結構體的定義,及相關平移方法SCNMatrix4MakeTranslation
中就能看出,平移向量被賦值.m41 = tx, .m42 = ty, .m43 = tz
,就說明被賦值的是第四列的第 1,2,3 個元素:函數
//m11 指第一列第一個元素
typedef struct SCNMatrix4 {
float m11, m12, m13, m14;
float m21, m22, m23, m24;
float m31, m32, m33, m34;
float m41, m42, m43, m44;
} SCNMatrix4;
SCNMatrix4 SCNMatrix4MakeTranslation(float tx, float ty, float tz) {
return (SCNMatrix4){
.m11 = 1.f, .m12 = 0.f, .m13 = 0.f, .m14 = 0.f,
.m21 = 0.f, .m22 = 1.f, .m23 = 0.f, .m24 = 0.f,
.m31 = 0.f, .m32 = 0.f, .m33 = 1.f, .m34 = 0.f,
.m41 = tx, .m42 = ty, .m43 = tz, .m44 = 1.f
};
}
複製代碼
而 simd_float4x4 更爲直接,採用了名爲columns[4]
的數組,這就告訴了咱們,這個矩陣是列主序的工具
/*! @abstract A matrix with 4 rows and 4 columns.*/
typedef struct { simd_float4 columns[4]; } simd_float4x4;
複製代碼
用SCNMatrix4ToMat4()
函數將 SCNMatrix4 矩陣轉換成 simd_float4x4 矩陣,也會看到 .m12
處數值對應的是 columns[0][1]
位置。這也說明了 SCNMatrix4 是列主序的。ui
若是咱們須要對矩陣進行變換,從行主序變爲列主序,或從列主序變成行主序,那就須要轉置 transpose:spa
SCNMatrix4 scnMat;
simd_transpose(SCNMatrix4ToMat4(scnMat));
GLKMatrix4Transpose(SCNMatrix4ToGLKMatrix4(scnMat));
複製代碼
通常何時須要轉置呢?
主要看用途:通常線性代數教材中的公式,都是行主序的,因此常常會出如今工具方法中,爲了更好的抄公式採用了行主序,最後再轉置爲列主序矩陣。code
還有一種常見的是:利用正交矩陣的特性,用矩陣轉置來代替矩陣求逆運算。好比,矩陣 A 表明將一個小球從 a 點變換(只有平移和旋轉)到 b 點,矩陣 B 則相反,表明將一個小球從 b 點變換(只有平移和旋轉)到 a 點。因爲 A 很差求解,那隻須要求出 B 矩陣,轉置後就獲得了 A 矩陣。須要注意的是,在求解 B 的過程當中,若是進行平移,應操做.m14 = tx, .m24 = ty, .m34 = tz
,即當作行主序矩陣來操做,最後轉置後纔是正確的結果。orm
附蘋果文檔中的轉置方法代碼:
static simd_float4x4 SIMD_CFUNC simd_transpose(simd_float4x4 __x) {
#if defined __SSE__
simd_float4 __t0 = _mm_unpacklo_ps(__x.columns[0],__x.columns[2]);
simd_float4 __t1 = _mm_unpackhi_ps(__x.columns[0],__x.columns[2]);
simd_float4 __t2 = _mm_unpacklo_ps(__x.columns[1],__x.columns[3]);
simd_float4 __t3 = _mm_unpackhi_ps(__x.columns[1],__x.columns[3]);
simd_float4 __r0 = _mm_unpacklo_ps(__t0,__t2);
simd_float4 __r1 = _mm_unpackhi_ps(__t0,__t2);
simd_float4 __r2 = _mm_unpacklo_ps(__t1,__t3);
simd_float4 __r3 = _mm_unpackhi_ps(__t1,__t3);
return simd_matrix(__r0,__r1,__r2,__r3);
#else
return simd_matrix((simd_float4){__x.columns[0][0], __x.columns[1][0], __x.columns[2][0], __x.columns[3][0]},
(simd_float4){__x.columns[0][1], __x.columns[1][1], __x.columns[2][1], __x.columns[3][1]},
(simd_float4){__x.columns[0][2], __x.columns[1][2], __x.columns[2][2], __x.columns[3][2]},
(simd_float4){__x.columns[0][3], __x.columns[1][3], __x.columns[2][3], __x.columns[3][3]});
#endif
}
GLKMatrix4 GLKMatrix4Transpose(GLKMatrix4 matrix)
{
#if defined(__ARM_NEON__)
float32x4x4_t m = vld4q_f32(matrix.m);
return *(GLKMatrix4 *)&m;
#else
GLKMatrix4 m = { matrix.m[0], matrix.m[4], matrix.m[8], matrix.m[12],
matrix.m[1], matrix.m[5], matrix.m[9], matrix.m[13],
matrix.m[2], matrix.m[6], matrix.m[10], matrix.m[14],
matrix.m[3], matrix.m[7], matrix.m[11], matrix.m[15] };
return m;
#endif
}
複製代碼
SceneKit 和 ARKit 中採用的是右手系。
在項目中無論是對世界座標原點矩陣仍是 SCNNode 的矩陣進行操做,若是反轉 x,y,z 任意一根座標軸,矩陣就會改變手性。
SCNNode若是被放在左手系下,那其中的 3D 模型極可能會顯示不正常,表面顯示黑色或破損狀。這是由於法向量也隨着座標系的變換而改變了,致使的顯示不正常。
教材中對此理論進行了說明:
在三維空間中,由3D向量V1,V2和V3組成的座標系的基B具備偏手性,
當(V1 × V2) ∙ V3 >0時,B稱爲右手螺旋基。
在右手座標系中,向量V1和V2外積的方向遵循右手螺旋定律,
與向量V3的方向之間的夾角爲銳角。
若是B爲右手螺旋正交基,則V1 × V2=V3。
反之,當(V1 × V2) ∙ V3<0時,B稱爲左手螺旋基。
奇數次反射變換將改變基B的偏手性,偶數次反射變換老是等效於旋轉變換,所以一系列任意多的反射變換能夠當作一個旋轉變換加最多一個反射變換。
經過計算一個3X3矩陣的行列式能夠判斷該矩陣是否包含反射變換,
若是一個3X3矩陣M的行列式是負值,則該矩陣包含一個反射變換,則矩陣M將改變它所變換的基向量集的偏手性,
反之,若是矩陣M的行列式是正值,該矩陣將保持變換基向量集的偏手性。
正交矩陣M的行列式的值只能爲1或者-1,若是 detM=1, 則矩陣M表示一個純粹的旋轉變換,若是detM=-1, 則矩陣M表示一個旋轉變換和一個反射變換。
複製代碼
因此,要檢查 AR 中世界座標系的手性,能夠直接設置ARSCNDebugOptionShowWorldOrigin
顯示世界座標軸,直接目視判斷。也能夠輸出其矩陣,計算 3x3 行列式的值,進行判斷。
SCNNode 中提供了兩個矩陣轉換的 API,用來將矩陣變換從一個座標系下轉換到另外一個 Node 的座標系下。
//SCNNode 中的對象方法,做用是將 self(即一個 SCNNode 對象)上的某個矩陣,轉換到另外一個 Node 的座標系中。
- (SCNMatrix4)convertTransform:(SCNMatrix4)transform toNode:(nullable SCNNode *)node;
//SCNNode 中的對象方法,做用是將另外一個 Node 座標系下的某個矩陣,轉換到 self(即 SCNNode 對象)的座標系中。
- (SCNMatrix4)convertTransform:(SCNMatrix4)transform fromNode:(nullable SCNNode *)node;
複製代碼