OpenGL技術之矩陣構造方式

作者:i_dovelemon

日期:2016/06/10

來源:CSDN

主題:OpenGL,glUniformMatrix4fv



引言



        今天,在搭建Graphics Lab OpenGL版本的實驗框架的時候,需要編寫一套矩陣運算函數,在實際測試的時候遇到了一些問題。這個問題是我以前寫OpenGL程序的時候也遇到過,並且解決了,但是今天又一次花了很長時間才搞明白,所以,特地在此處記錄下來,免得以後又忘記了!!!


問題描述



        我在寫Matrix這個類的時候,需要提供一些用於構造縮放,旋轉和平移矩陣的方法,我知道在OpenGL中使用的是column-major的矩陣形式,所以編寫了如下的三個函數:

void Matrix::MakeScaleMatrix(float sx, float sy, float sz) {
	float m[4][4] = {
		sx, 0.0f, 0.0f, 0.0f,
		0.0f, sy, 0.0f, 0.0f,
		0.0f, 0.0f, sz, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f
	};
	memcpy(&m_Matrix, m, sizeof(m));
}

void Matrix::MakeRotateZMatrix(float rotate_degree) {
	float ran = rotate_degree / 180.0f * 3.1415927f;
	float m[4][4] = {
		cos(ran), -sin(ran), 0.0f, 0.0f,
		sin(ran), cos(ran), 0.0f, 0.0f,
		0.0f, 0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f,
	};
	memcpy(&m_Matrix, m, sizeof(m));
}

void Matrix::MakeTranslateMatrix(float tx, float ty, float tz) {
	float m[4][4] = {
		1.0f, 0.0f, 0.0f, tx,
		0.0f, 1.0f, 0.0f, ty,
		0.0f, 0.0f, 1.0f, tz,
		0.0f, 0.0f, 0.0f, 1.0f,
	};
 	memcpy(&m_Matrix, m, sizeof(m));
}

        恩,好了column-major形式的矩陣全部構造完畢。拿這些矩陣去實際的測試了一下。調用如下函數來講這些矩陣傳遞到Vertex Shader中去:

int32_t ShaderProgram::UpdateUniformMatrix(const char* name, float* v) {
	int32_t result = 0;

	if(name != NULL && v != NULL) {

		// Check if this is the first time to update uniform
		int32_t location = 0;
		for(int32_t i = 0; i < m_UniformMap.size(); i++) {
			if(!strcmp(m_UniformMap[i].m_Name, name)) {
				location = m_UniformMap[i].m_Location;
				break;
			}
		}

		if(location == 0) {
			
			// Yes, get the location of the uniform variable
			location = glGetUniformLocation(m_Program, name);
			
			if(location == -1) { // Doesn't get the uniform's location
				result = -1;
				location = 0;
			} else { // Do get the uniform's location and save it
				UniformMap uniform_map(location, name);
				m_UniformMap.push_back(uniform_map);
			}
		}

		// Update the uniform matrix
		if(location != 0) {
			<strong>glUniformMatrix4fv(location, 1, false, v); </strong>
		}
	} else {
		GLB_SAFE_ASSERT(false);
	}

	return result;
}


        上面這個函數,將我先前創建的矩陣,以數組的形式傳遞過去。主要的是調用glUniformMatrix4fv這個函數,將矩陣傳遞到Shader中。它的參數分別爲:下標位置,矩陣數量,是否進行轉置,矩陣。從中可以看出,我沒有對對傳遞進去的矩陣進行轉置操作,因爲我覺得在外部我已經構造了column-major形式的矩陣了。

        準備工作完畢之後,我就用這些矩陣分別做了些測試,以下是測試的結果:

        1.縮放矩陣:成功將三角形縮放

        2.旋轉矩陣:調用MakeRoateZMatrix(30.0f),創建一個旋轉30度的矩陣,實驗“成功”,將三角形順時針旋轉30度

        3.平移矩陣:調用MakeTranslateMatrix(0.5f, 0.0f, 0.0f),實驗失敗,原先的三角形圖像被拉扯成爲一個奇怪的圖像。


問題解決



        上面的結果讓我十分奇怪,爲什麼失敗了?我將構造的函數檢查了幾遍,發現沒有錯誤啊!使用gDEBugger觀察,的確將數據傳遞進去了啊,而且的確是我要的column-major矩陣啊?爲什麼會失敗了?

        在嘗試了其他的操作之後,都失敗,抱着僥倖的想法,將平移矩陣,改成了row-major的形式,如下所示:

void Matrix::MakeTranslateMatrix(float tx, float ty, float tz) {
	float m[4][4] = {
		1.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 1.0f, 0.0f,
		tx, ty, tz, 1.0f,
	};
 	memcpy(&m_Matrix, m, sizeof(m));
}

        然後在進行嘗試,意外的成功了!WTF,什麼情況啊這是?不是說要column-major的嗎?爲什麼會是row-major的成功了?

        我就這樣糾結了一會兒之後,突然想到我以前也是遇到了這個問題,後來發現:

        OpenGL內部使用的的確是column-major的矩陣形式,在Shader中你也必須要用Matrix*Vertex這樣的順序來進行運算,但是它在內存中保存矩陣的方式和我的不一樣。

        OpenGL在內部保存的是column-major的矩陣,這沒有錯,但是它是這樣組織數據的:

        Mem Start [Col1,Col2,Col3,Col4] Mem End

        而我構造的column-major矩陣,保存的方式是這樣的:

        Mem Start[Row1, Row2, Row3, Row4] Mem End

        也就是說,OpenGL在使用column-major矩陣的時候,使用的很徹底,不僅運算上面使用的是column-major的形式,在內存中也是以column-vector數組的形式保存的。而我構造的雖然是column-major形式的矩陣,但是在內存中保存的實際上還是以row-vector的形式保存的。這就導致了我的矩陣和OpenGL實際需要的矩陣是轉置的關係。這也就解釋了爲什麼,前面的Translate實驗沒有成功,而另外兩個Scale和RotateZ的卻成功了。這是因爲Scale矩陣的轉置和它本身是一樣的,而RotateZ矩陣是正交的,它的轉置相當於它的逆矩陣,也就是說實際上上面旋轉的實驗應該也是錯誤的,正確的結果應該是逆時針旋轉30度。而由於Translate不是正交矩陣,它的轉置矩陣導致的結果就將圖像拉扯變形了。所以,最終矩陣依然保持以row-vector形式進行保存,這是因爲在我們去觀察和構造column-major矩陣的時候,這中形式保存的矩陣能夠很直觀,然後我將對函數glUniformMatrix4fv的調用改爲:

        glUniformMatrix4fv(location, 1, true, v)


總結



        OpenGL在內部和GLSL中運算的形式都是column-major的,並且最終在內存中保存的形式也是以column-vector數組的形式進行保存的。

        
發佈了92 篇原創文章 · 獲贊 95 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章