天天看點

ObjectARX中矩陣AcGeMatrix3d的使用介紹

1、介紹

  先介紹一下AcGeMatrix3d這個類,常見的點一般是Pt(X,Y,Z),相當于一個1×3矩陣,而矩陣相乘的話一般是第一個矩陣的列數要等于第二個矩陣的行數,是以我一開始天真的認為AcGeMatrix3d是個3×3矩陣,然而看過類介紹後沒有發現矩陣相加減這種情況,納悶平移是怎麼用乘法實作的,原來用齊次坐标的概念如下:https://wenku.baidu.com/view/4b0324eae009581b6bd9eb40.html?from=search

ObjectARX中矩陣AcGeMatrix3d的使用介紹
ObjectARX中矩陣AcGeMatrix3d的使用介紹

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.平移變換不改變坐标軸走向,但改變原點位置,兩個坐标系原點不再重合。

這樣就可以解釋問什麼縮放不能在旋轉之後,而縮放和旋轉都不能在平移之後了。 于是沒有問題的順序隻能是 縮放 -> 旋轉 -> 平移 。

繼續閱讀