為了更強大的功能和靈活性,我們有時需要直接操作矩陣。在OpenGL中4x4的矩陣用包含16個浮點數值的一維數組來表示,而不是用二維的4x4的數組來表示。OpenGL之所以這麼做,因為使用一維數組更高效。當然OpenGL也支持二維數組的表示方式。而且要特別注意的是在矩陣中是使用列主序遍歷數組的,即按列逐個遍歷數組中的元素。
事實上,這個矩陣裡的16個值代表著空間中的一個特定的位置和三個軸的朝向(相對於視點坐標系)。前3列是方向向量分別代表著3個軸的朝向(絕大多數情況下,這3個向量是正交的),第四列用於平移變換、glTranslate函數就是把數值填到這一列中。這個4x4的矩陣相當於一個坐標系統的位置和方向,如果把一個頂點(用列向量的形式)與這個矩陣相乘,得到的結果是一個變換到該坐標系統的新頂點。這意味著空間中的任意一個點和方向,能夠用唯一的4x4的矩陣表示。如果你把物體中的所有頂點都乘以這個矩陣,那麼你就是把整個物體變換到空間中指定的位置和朝向(我的理解是可以用這個矩陣所代表的坐標系統來表示你的整個物體)。
PS:注意最後一行的元素除了最後一個為1之外,其余為0。
你可以使用下面的兩個函數來加載你的列主序的矩陣到投影矩陣,模型視圖矩陣或者紋理矩陣棧中。
glLoadMatrixf(GLfloat* m);
glLoadMatrixd(GLdouble* m);
絕大多數的OpenGL的實現是使用單精度的浮點數來計算管道中的數據的。使用雙精度的形式會帶來一定的性能開銷。
下面的代碼相當於調用glLoadIdentity函數。
// 加載單位矩陣
GLfloat m[] = { 1.0f, 0.0f, 0.0f, 0.0f, // X 列
0.0f, 1.0f, 0.0f, 0.0f, // Y 列
0.0f, 0.0f, 1.0f, 0.0f, // Z 列
0.0f, 0.0f, 0.0f, 1.0f }; // 平移列
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(m);
相對應的OpenGL還提供了加載行主序的矩陣的兩個函數
void glLoadTransposeMatrixf(GLfloat *m);
void glLoadTransposeMatrixd(GLdouble *m);
一個高級的例子:
1: void RenderScene(void)
2: {
3: M3DMatrix44f transformationMatrix; //保存旋轉矩陣
4: static GLfloat yRot = 0.0f; // 旋轉的角度
5: yRot += 0.5f;
6:
7: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
8:
9: // 構造一個矩陣
10: m3dRotationMatrix44(transformationMatrix, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
11: transformationMatrix[12] = 0.0f;
12: transformationMatrix[13] = 0.0f;
13: transformationMatrix[14] = -2.5f;
14: //畫圓環
15: DrawTorus(transformationMatrix);
16:
17: glutSwapBuffers();
18: }其中m3dRotationMatrix44是構造一個旋轉的矩陣,相當於glRotatef(yRot, 0.0f, 1.0f, 0.0f)函數。
transformationMatrix[12] = 0.0f;
transformationMatrix[13] = 0.0f;
transformationMatrix[14] = -2.5f;
這三個是執行平移變換.相當於glTranslatef(0.0f, 0.0f, –2.5f); m3dRotationMatrix44函數如下
1: void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z)
2: {
3: float mag, s, c;
4: float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
5:
6: s = float(sin(angle));
7: c = float(cos(angle));
8:
9: mag = float(sqrt( x*x + y*y + z*z ));
10:
11: // Identity matrix
12: if (mag == 0.0f) {
13: m3dLoadIdentity44(m);
14: return;
15: }
16:
17: // Rotation matrix is normalized
18: x /= mag;
19: y /= mag;
20: z /= mag;
21:
22: #define M(row,col) m[col*4+row]
23:
24: xx = x * x;
25: yy = y * y;
26: zz = z * z;
27: xy = x * y;
28: yz = y * z;
29: zx = z * x;
30: xs = x * s;
31: ys = y * s;
32: zs = z * s;
33: one_c = 1.0f - c;
34:
35: M(0,0) = (one_c * xx) + c;
36: M(0,1) = (one_c * xy) - zs;
37: M(0,2) = (one_c * zx) + ys;
38: M(0,3) = 0.0f;
39:
40: M(1,0) = (one_c * xy) + zs;
41: M(1,1) = (one_c * yy) + c;
42: M(1,2) = (one_c * yz) - xs;
43: M(1,3) = 0.0f;
44:
45: M(2,0) = (one_c * zx) - ys;
46: M(2,1) = (one_c * yz) + xs;
47: M(2,2) = (one_c * zz) + c;
48: M(2,3) = 0.0f;
49:
50: M(3,0) = 0.0f;
51: M(3,1) = 0.0f;
52: M(3,2) = 0.0f;
53: M(3,3) = 1.0f;
54:
55: #undef M
56: }
最終的效果如下:
手動執行變換在碰撞檢測,平截頭體剔除,以及一些特效算法中會用到。
在場景中移動的物體成為角色,就像舞台劇上的演員一樣。角色有他們自己的變換,不僅僅是相對於世界坐標系(視點坐標系)的變換,也可以相對於其他角色坐標系的變換。每個角色都有自己的參考幀和自己的坐標系(物體坐標系)。在物體坐標系和世界坐標系之間的轉換是非常有用的。
一個簡單靈活的表示角色的方式是用一個包含一個空間中的位置,一個指向前面的向量以及一個指向上面的向量(第三個向量可以通過計算得到)。使用這些量就可以唯一地標識空間中一個特定的位置和方向。
typedef float M3DMatrix44f[16]; // A 4 X 4 matrix, column major (floats) - OpenGL style
class GLFrame
{
protected:
M3DVector3f vLoaction;
M3DVector3f vUp;
M3DVector3f vForward;
public:
…
};
使用這樣的一個參考幀來表示一個物體的位置和方向是非常有用的。我們可以使用這些數據直接創建一個4x4的變換矩陣。其中向上的向量代表y列向量,向前的向量代表z列向量,位置則代表移動列向量。這樣只缺少了x向量。因為我們知道這3個軸是互相垂直的,因此可以有由y和z向量的叉乘來計算x列向量。
void GLFrame::GetMatrix(M3DMatrix44f mMatrix, bool bRotationOnly = false)
{
//計算列向量,叉乘
M3DVector3f vXAxis;
m3dCorssProduct(vXAxis, vUp, vForward);
//把各個向量轉換為矩陣的列向量, X列
m3dSetMatrixColumn44(matrix, vXAxis, 0);
matrix[3] = 0.0f;
//y列
m3dSetMatrixColumn44(matrix, vUp, 1);
matrix[7] = 0.0f;
//z列
m3dSetMatrixColumn44(matrix, vForward, 2);
matrix[11] = 0.0f;
//只包含旋轉不移動
if(bRotationOnly = true)
{
matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
}
else
m3dSetMatrixColumn44(matrix, vOrigin, 3);
matrix[15] = 1.0f;
}
參考《3D數學基礎_圖形與游戲開發》
歐拉角的基本思想是講角位移分解為三個互相垂直軸的三個旋轉組成的序列。
以下都是使用左手法則。“heading-pitch-bank” heading為繞y軸的旋轉量,繞慣性坐標系y軸的旋轉。向右旋轉為正。pitch為繞x軸的旋轉量。物體坐標系的x軸,不是原慣性坐標系的x軸。向下旋轉為正方向。bank為繞z軸的旋轉量。物體坐標系的z軸。逆時針為正方向。
PS:當我們說的旋轉順序是"heading-pitch-bank”時,是指從慣性坐標系到物體坐標系。如果從物體坐標系變換到慣性坐標系,旋轉的順序就是相反的
基本問題
解決方法:講heading和bank限制在+180到-180之間,pitch限制在+90到-90之間。這種現象,角度為+-90的第二次旋轉將使得第一次和第三次旋轉的旋轉軸相同,稱作萬向鎖。為了消除限制歐拉角的這種別名現象,規定萬向鎖情況下由heading完成繞豎直軸的全部旋轉。
OpenGL中並不真正存在照相機變換。相機作為一個隱喻,幫助我們理解如何管理3D環境中的視點。照相機可以想象為一種物體,在空間中具有某個位置和特定方向。
應用照相機變換,我們要使用照相機的角色變換並進行反轉。這樣當我們把相機向後移時就相當於向前移動整個場景。向左旋轉相機則相當於向右旋轉整個場景。
glu庫中包含了一個函數用於創建相機變換,它使用的數據與上面定義的幀結構數據相同。
void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery, GLdouble centerz,
GLdouble upx, GLdouble upy, GLdouble upz);
這個函數接受一個觀察點的位置,一個在觀察點正前方的一個點,以及向上的方向向量。
渲染一個指定的場景如下圖:
OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm
OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm
OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm
Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm
OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm
Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm
如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm
更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581