歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> OpenGL超級寶典學習筆記——操作矩陣

OpenGL超級寶典學習筆記——操作矩陣

日期:2017/3/1 9:32:28   编辑:Linux編程

為了更強大的功能和靈活性,我們有時需要直接操作矩陣。在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:     }

最終的效果如下:

手動執行變換在碰撞檢測,平截頭體剔除,以及一些特效算法中會用到。

使用相機和角色在OpenGL中移動

在場景中移動的物體成為角色,就像舞台劇上的演員一樣。角色有他們自己的變換,不僅僅是相對於世界坐標系(視點坐標系)的變換,也可以相對於其他角色坐標系的變換。每個角色都有自己的參考幀和自己的坐標系(物體坐標系)。在物體坐標系和世界坐標系之間的轉換是非常有用的。

角色幀

一個簡單靈活的表示角色的方式是用一個包含一個空間中的位置,一個指向前面的向量以及一個指向上面的向量(第三個向量可以通過計算得到)。使用這些量就可以唯一地標識空間中一個特定的位置和方向。

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”時,是指從慣性坐標系到物體坐標系。如果從物體坐標系變換到慣性坐標系,旋轉的順序就是相反的

關於歐拉角的其他約定

  • 一組常用的術語是roll-pitch-yaw,其中roll等價於bank, yaw基本上等價於heading。它的順序和heading-pitch-bank的順序相反。它定義了向量從物體坐標系到慣性坐標系的變換旋轉順序。(事實上,yaw和heading還是有技術上的差別,yaw是繞物體坐標系y軸的旋轉,heading是繞慣性坐標系y軸的旋轉。,因為這裡的旋轉是在物體坐標系y軸和慣性坐標系y軸重合是進行的,所以這個區別並不重要)
  • 任意三個坐標軸都能作為旋轉軸。
  • 決定每個旋轉的正方向時不一定必須遵守左手或右手法則。
  • 旋轉可以以不同的順序進行。但heading-pitch-bank順序最為實用。heading度量繞豎直軸的旋轉,它之所以有意義主要是因為我們所在的環境經常有某種形式的“地面”,一般料將繞慣性坐標系的x或z軸的旋轉沒有什麼意義。pitch度量水平方向的傾角,bank度量的是繞z軸的旋轉量

歐拉角的優點

  • 歐拉角容易使用,它用三個數來代表繞三個軸旋轉的角度。角度符合人類的思維習慣。heading-pitch-bank系統就能直接地描述出偏差的角度。當需要顯示方位或鍵盤輸入方位時,歐拉角是唯一的選擇。
  • 最簡潔的表達方式
  • 任意三個數都是合法的。

歐拉角的缺點

  • 給定方位的表達方式不唯一
  • 兩個角度間求插值非常困難

基本問題

  1. 將一個角度加上360的倍數,並不會改變方位。
  2. 由三個角度不互相獨立而導致。如先heading45再pitch90,這與先pitch90再bank45是等價的。

解決方法:講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

Copyright © Linux教程網 All Rights Reserved