为了更强大的功能和灵活性,我们有时需要直接操作矩阵。在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;}elsem3dSetMatrixColumn44(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

本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-02/113995.htm

OpenGL超级宝典学习笔记——操作矩阵相关推荐

  1. OpenGL超级宝典学习笔记:着色器存储区块、原子内存操作、内存屏障

    前言 本篇在讲什么 本篇为蓝宝书学习笔记 着色器存储区块 原子内存操作 内存屏障 本篇适合什么 适合初学Open的小白 本篇需要什么 对 C++语法有简单认知 对 OpenGL有简单认知 最好是有 O ...

  2. 深入理解linux white函数,OpenGL超级宝典学习笔记——曲线和曲面(一)

    内部支持的表面 GLU库中提供了一些二次曲面的支持.这些二次方程可以渲染球体,圆柱体,圆盘.这些函数有很大的灵活性,我们可以指定圆柱体的一端的半径,然后让另一端的半径为0,这样的话就能构建一个圆锥.我 ...

  3. (第四章)OpenGL超级宝典学习:必要的数学知识

    必要的数学知识 前言 在本章当中,作者着重介绍了几个和3D图形学重要的数学知识,线性代数基础好的同学可以直接绕过本章,说实话这篇博客写到这里,我是非常犹豫的,本章节的内容可以说是很基础,但是相当的枯燥 ...

  4. OpenGL超级宝典(第7版)笔记20 统一变量 一致区块 uniform相关内容 清单5.9-5.28

    OpenGL超级宝典(第7版)笔记20 统一变量 一致区块 uniform相关内容 清单5.9-5.28 文章目录 OpenGL超级宝典(第7版)笔记20 统一变量 一致区块 uniform相关内容 ...

  5. OpenGL超级宝典(第7版)笔记13 前三章实例 下个五子棋 (上)

    OpenGL超级宝典(第7版)笔记13 前三章实例 下个五子棋 (上) 文章目录 OpenGL超级宝典(第7版)笔记13 前三章实例 下个五子棋 (上) 前言 1 初构建 2 构建数据结构 3 绘制 ...

  6. OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13

    OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13 文章目录 OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13 1 帧缓存运算 1.1 裁剪测 ...

  7. OpenGL超级宝典(第7版)笔记4 渲染管线介绍 清单2.3-2.7

    OpenGL超级宝典(第7版)笔记4 渲染管线介绍 清单2.3-2.7 文章目录 OpenGL超级宝典(第7版)笔记4 渲染管线介绍 清单2.3-2.7 1 OpenGL简介 2 OpenGL渲染管线 ...

  8. OpenGL超级宝典(第五版)环境配置

    本文转自:http://blog.csdn.net/sunny_unix/article/details/8056807,感谢作者分享. OpenGL超级宝典(第五版)环境配置 Vs2008+winX ...

  9. OpenGL超级宝典(第五版)环境配置【转】

    OpenGL超级宝典(第五版)环境配置 Vs2008+winXP  后续会整理Ubuntu 12.04LTS下的配置作者:sunny_unix 1.各种库的配置 (1)glew 下载:https:// ...

最新文章

  1. 左牵Uber右联大众,黄教主带领320家车企一统自动驾驶江湖
  2. SQL Server中使用正则表达式
  3. UA OPTI512R 傅立叶光学导论9 卷积基础
  4. jQuery里面的addClass讲解
  5. return view前端怎么获取_前端判断上传图片格式
  6. mysql b tree图_MySQL索引--B-Tree(B+Tree)图文详解
  7. vfs管理下的linux文件系统
  8. isscalar matlab,大数的素性检测(用MATLAB仿真)
  9. Nginx平滑添加模块
  10. 31岁零基础转行软件测试,现已成功入职月薪14K+
  11. python求最大值最小值求和_python3.2求和与最值
  12. java汉字的编码_Java中文编码问题小结
  13. 小程序项目:基于微信小程序的每日签到打卡——计算机毕业设计
  14. 嵩天《Python数据分析与展示》实例1:图像的手绘效果
  15. push_back()函数的用法
  16. Stealing Machine Learning Models via Prediction APIs研读报告
  17. C语言 - 计算n的阶乘(n!)
  18. 虚拟相机IOS手机端安装教程
  19. 2022年PMP考试题型都有什么?
  20. 模糊聚类FCM的MATLAB实现

热门文章

  1. 程序员必备网站Collection~
  2. 电脑操作系统 GUI 38年进化史
  3. 关于 618的前世今生,我帮东哥告诉你
  4. Mybatis Plus 代码生成器-让上班划水不再是梦
  5. 搜狗输入法--自定义短语配置文件
  6. 【项目配置学习笔记】启动Tomcat的常出现的错误及其解决办法
  7. 这回稳了!广和通4G低功耗摄像头解决方案全新来袭
  8. 文件服务器迁移多种方案
  9. 典型的多层神经网络模型,多层变量神经网络分析
  10. Qt 给QWidget添加工具栏