1、介紹
先介紹一下AcGeMatrix3d這個類,常見的點一般是Pt(X,Y,Z),相當于一個1×3矩陣,而矩陣相乘的話一般是第一個矩陣的列數要等于第二個矩陣的行數,是以我一開始天真的認為AcGeMatrix3d是個3×3矩陣,然而看過類介紹後沒有發現矩陣相加減這種情況,納悶平移是怎麼用乘法實作的,原來用齊次坐标的概念如下:https://wenku.baidu.com/view/4b0324eae009581b6bd9eb40.html?from=search
2、使用例子
簡單說一下三種比較常用的:平移、縮放、旋轉;
2.1 平移
平移有兩個set矩陣的方法setTranslation()和setToTranslation(),試過後發現沒有差別;
為了友善debug調試,寫了個結構體來轉換出矩陣行列值檢視:
struct MatrixVector
{
int nM11;
int nM12;
int nM13;
int nM14;
int nM21;
int nM22;
int nM23;
int nM24;
int nM31;
int nM32;
int nM33;
int nM34;
int nM41;
int nM42;
int nM43;
int nM44;
MatrixVector()
{
nM11 = 1;
nM12 = 0;
nM13 = 0;
nM14 = 0;
nM21 = 0;
nM22 = 1;
nM23 = 0;
nM24 = 0;
nM31 = 0;
nM32 = 0;
nM33 = 1;
nM34 = 0;
nM41 = 0;
nM42 = 0;
nM43 = 0;
nM44 = 1;
};
};
MatrixVector ConverMatrix(AcGeMatrix3d matxExp)
{
MatrixVector stuMatrix;
stuMatrix.nM11 = matxExp.entry[0][0];
stuMatrix.nM12 = matxExp.entry[0][1];
stuMatrix.nM13 = matxExp.entry[0][2];
stuMatrix.nM14 = matxExp.entry[0][3];
stuMatrix.nM21 = matxExp.entry[1][0];
stuMatrix.nM22 = matxExp.entry[1][1];
stuMatrix.nM23 = matxExp.entry[1][2];
stuMatrix.nM24 = matxExp.entry[1][3];
stuMatrix.nM31 = matxExp.entry[2][0];
stuMatrix.nM32 = matxExp.entry[2][1];
stuMatrix.nM33 = matxExp.entry[2][2];
stuMatrix.nM34 = matxExp.entry[2][3];
stuMatrix.nM41 = matxExp.entry[3][0];
stuMatrix.nM42 = matxExp.entry[3][1];
stuMatrix.nM43 = matxExp.entry[3][2];
stuMatrix.nM44 = matxExp.entry[3][3];
return stuMatrix;
}
AcGeMatrix3d ConverMatrixVector(MatrixVector stuMatrix)
{
AcGeMatrix3d matxExp;
matxExp.entry[0][0] = stuMatrix.nM11;
matxExp.entry[0][1] = stuMatrix.nM12;
matxExp.entry[0][2] = stuMatrix.nM13;
matxExp.entry[0][3] = stuMatrix.nM14;
matxExp.entry[1][0] = stuMatrix.nM21;
matxExp.entry[1][1] = stuMatrix.nM22;
matxExp.entry[1][2] = stuMatrix.nM23;
matxExp.entry[1][3] = stuMatrix.nM24;
matxExp.entry[2][0] = stuMatrix.nM31;
matxExp.entry[2][1] = stuMatrix.nM32;
matxExp.entry[2][2] = stuMatrix.nM33;
matxExp.entry[2][3] = stuMatrix.nM34;
matxExp.entry[3][0] = stuMatrix.nM41;
matxExp.entry[3][1] = stuMatrix.nM42;
matxExp.entry[3][2] = stuMatrix.nM43;
matxExp.entry[3][3] = stuMatrix.nM44;
return matxExp;
}
下面是測試執行個體代碼:
AcGeMatrix3d matxTran; //平移矩陣
AcGeMatrix3d matxScale;// 縮放矩陣
AcGeMatrix3d matxRota;// 旋轉矩陣
matxRota.setToRotation(30,AcGeVector3d::kZAxis);
MatrixVector stuMatrix;
AcGePoint3d ptPos1 = AcGePoint3d(2,2,2);
AcGePoint3d ptPos2 = AcGePoint3d(2,2,2);
AcGeVector3d vecTran = AcGeVector3d(1,2,3);
matxTran.setToTranslation(vecTran);
stuMatrix = ConverMatrix(matxTran);
//此時平移矩陣matxTran
{
1 0 0 1
0 1 0 2
0 0 1 3
0 0 0 1
}
matxScale.setToScaling(3);
stuMatrix = ConverMatrix(matxScale);
//此時縮放矩陣matxScale
{
3 0 0 0
0 3 0 0
0 0 3 0
0 0 0 1
}
ptPos1.transformBy(matxTran);
ptPos1.transformBy(matxScale);
//--------
AcGeMatrix3d matrxTop = matxScale * matxTran; // 先平移再縮放矩陣
ptPos2.transformBy(matrxTop);
// 驗證結果ptPos1和ptPos2是一樣的等于(9,12,15);
int nTrans = 3;
AcGeMatrix3d matxExp1;
AcGeMatrix3d matxExp2;
matxExp2 = matxExp1.setTranslation(nTrans * AcGeVector3d::kZAxis);
matxExp2 = matxExp1.setToTranslation(nTrans * AcGeVector3d::kZAxis);
//兩個函數效果一樣,此時matxExp1和matxExp2的值是一樣的,都是
{
1 0 0 0
0 1 0 0
0 0 1 3
0 0 0 1
}
實作不同方向X Y Z 上縮放比例不同的情況,例如 X方向縮放1倍,Y方向縮放2倍,Z方向縮放3倍:
MatrixVector stuMatrix;
stuMatrix.nM11 = 1;
stuMatrix.nM22 = 2;
stuMatrix.nM33 = 3;
//X Y Z 方向縮放不同的矩陣
AcGeMatrix3d matxScale = ConverMatrixVector(stuMatrix);
AcGePoint3d ptPos1 = AcGePoint3d(2,2,2);
ptPos1.transformBy(matxScale);
ptPos1 = (2,4,6);
如何把矩陣轉換成對應的位移、旋轉角度和縮放比例?
AcGeMatrix3d tran = m_matrix;
CHCVec3d transLation;
CHCVec3d scale;
CHCVec4d rotate;
GetMatrixData(rotate,scale,transLation,tran);
bool GetMatrixData(CHCVec4d &rVec4d,CHCVec3d &scale3d,CHCVec3d &trans3d,::AcGeMatrix3d mat)
{
//平移------
trans3d.x = mat.entry[0][3];
trans3d.y = mat.entry[1][3];
trans3d.z = mat.entry[2][3];
//----------------------------------------
::AcGeVector3d vec1(mat.entry[0][0],mat.entry[1][0],mat.entry[2][0]);
::AcGeVector3d vec2(mat.entry[0][1],mat.entry[1][1],mat.entry[2][1]);
::AcGeVector3d vec3(mat.entry[0][2],mat.entry[1][2],mat.entry[2][2]);
//縮放---------------------------------------
scale3d.x = vec1.length();
scale3d.y = vec2.length();
scale3d.z = vec3.length();
//------------------------------------------------
if (scale3d.x != 0)
{
vec1.x = vec1.x/scale3d.x;
vec1.y = vec1.y/scale3d.x;
vec1.z = vec1.z/scale3d.x;
}
if (scale3d.y != 0)
{
vec2.x = vec2.x/scale3d.y;
vec2.y = vec2.y/scale3d.y;
vec2.z = vec2.z/scale3d.y;
}
if (scale3d.z != 0)
{
vec3.x = vec3.x/scale3d.z;
vec3.y = vec3.y/scale3d.z;
vec3.z = vec3.z/scale3d.z;
}
//旋轉-------------------------
AcGeMatrix2d mat2d; //旋轉矩陣
mat2d.entry[0][0] = vec1.x;
mat2d.entry[1][0] = vec1.y;
mat2d.entry[2][0] = vec1.z;
mat2d.entry[0][1] = vec2.x;
mat2d.entry[1][1] = vec2.y;
mat2d.entry[2][1] = vec2.z;
mat2d.entry[0][2] = vec3.x;
mat2d.entry[1][2] = vec3.y;
mat2d.entry[2][2] = vec3.z;
//-------------------------------------------------
rVec4d = FromRotationMatrix(mat2d);
return true;
}
CHCVec4d FromRotationMatrix (const AcGeMatrix2d& kRot)
{
CHCVec4d quaternion;
// Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
// article "Quaternion Calculus and Fast Animation".
double fTrace = kRot.entry[0][0]+kRot.entry[1][1]+kRot.entry[2][2];
double fRoot = 0.0;
if ( fTrace > 0.0 )
{
// |w| > 1/2, may as well choose w > 1/2
fRoot = sqrt(fTrace + 1.0f); // 2w
quaternion.w = 0.5f*fRoot;
fRoot = 0.5f/fRoot; // 1/(4w)
quaternion.x = (kRot.entry[2][1]-kRot.entry[1][2])*fRoot;
quaternion.y = (kRot.entry[0][2]-kRot.entry[2][0])*fRoot;
quaternion.z = (kRot.entry[1][0]-kRot.entry[0][1])*fRoot;
}
else
{
// |w| <= 1/2
static size_t s_iNext[3] = { 1, 2, 0 };
size_t i = 0;
if ( kRot.entry[1][1] > kRot.entry[0][0] )
i = 1;
if ( kRot.entry[2][2] > kRot.entry[i][i] )
i = 2;
size_t j = s_iNext[i];
size_t k = s_iNext[j];
fRoot = sqrt(kRot.entry[i][i]-kRot.entry[j][j]-kRot.entry[k][k] + 1.0f);
double* apkQuat[3] = { &quaternion.x, &quaternion.y, &quaternion.z };
*apkQuat[i] = 0.5f*fRoot;
fRoot = 0.5f/fRoot;
quaternion.w = (kRot.entry[k][j]-kRot.entry[j][k])*fRoot;
*apkQuat[j] = (kRot.entry[j][i]+kRot.entry[i][j])*fRoot;
*apkQuat[k] = (kRot.entry[k][i]+kRot.entry[i][k])*fRoot;
}
return quaternion;
}
3、世界坐标變換要先縮放、後旋轉、再平移的原因
一個三維場景中的各個模型一般需要各自模組化,再通過坐标變換放到一個統一的世界空間的指定位置上。 這個過程在 3D 圖形學中稱作“世界變換” 。 世界變換有三種,平移、旋轉和縮放 (實際還有不常用的扭曲和鏡像,它們不是affine變換)。 這三種變換按各種順序執行,結果是不同的。 可是實際的應用中一般按照 縮放 -> 旋轉 -> 平移的順序進行。 這樣做的原因是可以獲得最符合常理的變換結果。
比方說,通過世界變換希望獲得的結果可能是:
将一個放在原點的物體(比方說可樂罐)移動到(30,50),讓它自身傾斜 45 度,
再放大 2 倍。
而不希望的結果是:
1.和本地坐标軸成角度的縮放(會導緻扭曲,像踩扁的可樂罐)。
2.繞自己幾何中心以外位置的原點的旋轉 (地球公轉式) 和縮放。
而颠倒了上述變換順序就會得到這樣不自然的結果。
具體的說:
當縮放在旋轉之後進行時,會發生現象1。
當縮放和旋轉在平移之後進行時會發生現象2。
這是因為:在物體剛剛放入世界坐标系的時候使用的是本地坐标,也就是本地和全局坐标系的原點和坐标軸都是重合的(當然兩者分别使用了左右手坐标系時除外 - 那是BUG),此時所有物體都“把世界坐标系當做自己的本地坐标系”。
而經過了坐标變換之後:
1.縮放變換不改變坐标軸的走向,也不改變原點的位置,是以兩個坐标系仍然重合。
2.旋轉變換改變坐标軸的走向,但不改變原點的位置,是以兩個坐标系坐标軸不再處于相同走向。
3.平移變換不改變坐标軸走向,但改變原點位置,兩個坐标系原點不再重合。
這樣就可以解釋問什麼縮放不能在旋轉之後,而縮放和旋轉都不能在平移之後了。 于是沒有問題的順序隻能是 縮放 -> 旋轉 -> 平移 。