热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenGL系列教程之五:OpenGL矩阵类

转自:https:blog.csdn.netu012501459articledetails12945149#creation相关主题:OpenGL变

转自:https://blog.csdn.net/u012501459/article/details/12945149#creation

相关主题:OpenGL变换,OpenGL投影矩阵,四元数

下载:matrix.zip, matrix_rowmajor.zip

 


  • 概述
  • 构造&初始化
  • 存取操作
  • 矩阵算法
  • 变换函数
  • 例子:模型视图矩阵
  • 例子:投影矩阵

 



 

概述

 

OpenGL为渲染管线准备了4种不同类型的矩阵(GL_MODELVIEW,GL_PROJECTION, GL_TEXTURE and GL_COLOR)并且为这些矩阵提供了变换的操作:glLoadIdentity(),glTranslatef(),glRotatef(),glScalef(),glMultMatrixf(),glFrustum()和glOrtho().

这些内置的矩阵和操作对于开发简单的OpenGL应用程序非常有用并且非常有利于理解矩阵变换。但是当你的应用程序变的复杂的时候,最好是自己为所有需要移动的对象实现你自己的矩阵和操作。除此之外,你也不可以在可编程的管线(GLSL),像OpenGL v3.0+, OpenGL ES v2.0+ 和 WebGL v1.0+中使用这些内置的矩阵和操作。你必须实现你自己的矩阵并且将矩阵中的数据传递到着色器中。

这篇文章提供了一种使用C++编写的独立,通用的的4*4矩阵类Matrix4,并且描述了如果将这个类集成到OpenGL应用程序中。这个类只依赖于定义在Vectors.h中的Vector3和Vector4。这些向量类也包含在 matrix.zip中。

 

 



 

构造&初始化

 

Matrix4类包含一个16个浮点型元素的数组来存储4*4的矩阵,它有3个构造函数来实例化这个Matrix4类的对象。

 

以行为主的Matrix4

 

OpenGL中使用的以列为主的矩阵

 

注意这个Matrix4类使用以行为主的标记次序而不是像OpenGL那样使用以列为主的标记次序。然儿,以行为主的次序和以列为主的次序只是两种不同的将多维数组中的数据存储的一维数组中的方式,这对矩阵的算法和矩阵的操作结果是没有影响的。

缺省的构造函数(没有参数)将会创建一个单位矩阵。其他两个构造函数接受16个参数或者包含16个参数的数组。你也可以使用复制构造或赋值操作符(=)来初始化一个Matrix4类的实例。

顺便说一下,复制构造和赋值操作符(=)会由C++编译器自动生成。

下面是一个使用不同方式构造Matrix4对象的例子。首先,需要在使用Matrix4类的文件中包含Matrices.h头文件。

 


  1. #include "Matrices.h" // 为 Matrix2, Matrix3, Matrix4准备的

  2. ...

  3.  
  4. // 使用缺省的构造函数构造一个单位矩阵

  5. Matrix4 m1;

  6.  
  7. // 使用16个元素构造一个矩阵

  8. Matrix4 m2(1, 1, 1, 1, // 第一行

  9. 1, 1, 1, 1, // 第二行

  10. 1, 1, 1, 1, // 第三行

  11. 1, 1, 1, 1); // 第四行

  12.  
  13. // 使用一个数组够造一个矩阵

  14. float a[16] = {2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2};

  15. Matrix4 m3(a);

  16.  
  17. //使用复制构造和赋值操作符构造一个矩阵

  18. Matrix4 m4(m3);

  19. Matrix4 m5 = m3;


 



 

Matrix4存取操作(Setters/Getters)

 



 

Setters

Matrix4类提供set()函数来设置所有的16个元素。


  1. Matrix4 m1;

  2.  
  3. // 使用16个元素来设置矩阵

  4. m1.set(1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1);

  5.  
  6. // 使用数组来设置矩阵

  7. float a1[] = {2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2};

  8. m1.set(a1);

 

你也可以使用setRow()和setColumn()函数一次来设置一行或一列的值,setRow()和setColumn()函数的第一个参数是一个基于0的索引,第二个参数是指向数组的指针。


  1. Matrix4 m2;

  2. float a2[4] = {2,2,2,2};

  3.  
  4. // 使用索引和数组设置一行的值

  5. m2.setRow(0, a2); // 1st row

  6.  
  7. // 使用索引和数组设置一列的值

  8. m2.setColumn(2, a2); // 3rd column


 



单位矩阵

Matrix4类有一个特殊的setter,identify()函数用来生成一个单位矩阵


  1. // 设置一个单位矩阵

  2. Matrix4 m3;

  3. m3.identity();


 



Getters

Matrix4::get()方法返回一个指向拥有16个元素的数组。getTranspose()返回转置后的矩阵元素。它专门用来将矩阵中的数据传递到OpenGL中,更多详细的细节参考例子。


  1. // 得到矩阵中的元素并赋值个一个数组

  2. Matrix4 m4;

  3. const float* a = m4.get();

  4.  
  5. //将矩阵传递给OpenGL

  6. glLoadMatrixf(m4.getTranspose());


 



访问单个元素

矩阵中的单个元素可以通过[]操作符来访问:


  1. Matrix4 m5;

  2. float f = m5[0]; // 得到第一个元素

  3.  
  4. m5[1] = 2.0f; // 设置第二个元素


 



打印Matrix4

Matrix4也提供了一个便利的打印输出函数std::cout<<来方便调试&#xff1a;


  1. Matrix4 m6;

  2. std::cout <


 



 

矩阵算法

 

Matrix4类提供了两个矩阵之间的基本算法。

 



加法和减法

你可以将两个矩阵相加和相减


  1. Matrix4 m1, m2, m3;

  2.  
  3. //相加

  4. m3 &#61; m1 &#43; m2;

  5. m3 &#43;&#61; m1; //等价于: m3 &#61; m3 &#43; m1

  6.  
  7. // subtract

  8. m3 &#61; m1 - m2;

  9. m3 -&#61; m1; // 等价于: m3 &#61; m3 - m1


 



乘法

你可以将两个矩阵相乘&#xff0c;也有与3维和4维向量相乘以便使用矩阵将向量进行变换。注意矩阵相乘不满足交换律。


  1. Matrix4 m1, m2, m3;

  2.  
  3. // 矩阵相乘

  4. m3 &#61; m1 * m2;

  5. m3 *&#61; m1; // 等价于: m3 &#61; m3 * m1

  6.  
  7. // 缩放操作

  8. m3 &#61; 2 * m1; // 将所有元素缩放

  9.  
  10. // 与向量相乘

  11. Vector3 v1, v2;

  12. v2 &#61; m1 * v1;

  13. v2 &#61; v1 * m1; // 前乘


 



比较

Matrix4类提供了比较操作符来比较两个矩阵中的所有元素&#xff1a;

 


  1. Matrix4 m1, m2;

  2.  
  3. //精确比较

  4. if(m1 &#61;&#61; m2)

  5. std::cout <<"equal" <

  6. if(m1 !&#61; m2)

  7. std::cout <<"not equal" <

 

 

 



 

Matrix4变换函数

 

OpenGL有几个变换函数&#xff1a;glTranslatef(),glRotatef()和glScalef()。Matrix4也提供了几个相同的函数来进行矩阵变换&#xff1a;translate(),rotate()andscale()。除此之外&#xff0c;Matrix4还提供了invert()函数计算矩阵的转置矩阵。

 



Matrix4::translate(x,y,z)

平移矩阵

 

translate()函数产生经过(x,y,z)变换后的矩阵。首先&#xff0c;它创建一个变换矩阵MT&#xff0c;然后乘以当前矩阵来产生最终的矩阵&#xff1a;

注意这个函数等价于OpenGL中的glTranslatef()&#xff0c;但是OpenGL使用后乘而不是前乘&#xff1a;&#xff0c;如果你执行多个变换&#xff0c;结果将会不一样因为矩阵相乘不满足交换律。


  1. // M1 &#61; Mt * M1

  2. Matrix4 m1;

  3. m1.translate(1, 2, 3); // 移动到(x, y, z)


 



Matrix4::rotate(angle,x,y,z)

旋转矩阵

 

rotate()可以被用来通过指定一个轴&#xff08;x&#xff0c;y&#xff0c;z&#xff09;和绕轴旋转的角度来旋转三维模型。这个函数生成一个旋转矩阵MR&#xff0c;然后乘以当前矩阵生成一个最终经过旋转变换后的矩阵&#xff1a;

它等价于glRotatef(),但是OpenGL使用后乘操作来生成最终的变换后的矩阵&#xff1a;

rotate()可以用来绕任意的轴旋转。Matrix4类提供了额外的3个函数绕指定轴旋转rotateX(),rotateY(),rotateZ()。

 


  1. // M1 &#61; Mr * M1

  2. Matrix4 m1;

  3. m1.rotate(45, 1,0,0); // 绕X轴旋转45度

  4. m1.rotateX(45); // 和rotate(45, 1,0,0)一样


 



Matrix4::scale(x,y,z)

缩放矩阵

 

scale()在每一个轴上产生一个不均等的缩放效果的矩阵&#xff1a;

注意&#xff1a;OpenGL中glScalef()执行后乘操作&#xff1a;

Matrix4类也提供了均等的缩放函数。

 

 


  1. // M1 &#61; Ms * M1

  2. Matrix4 m1;

  3. m1.scale(1,2,3); // 非均等的缩放

  4. m1.scale(4); // 均等的缩放&#xff0c;在所有轴上是一样的

 

 

 



Matrix::invert()

 

invert()函数计算当前矩阵的反转矩阵&#xff0c;这个反转矩阵主要用来将法向量从物体坐标系变换到人眼坐标系中。法向量和顶点的变换不一样。法向量变换是使用GL_MODELVIEW的反转乘以法向量&#xff1a;详细的细节参考这儿。

如果矩阵只是欧式变换&#xff08;旋转和平移&#xff09;&#xff0c;或者放射变换&#xff08;旋转&#xff0c;平移&#xff0c;缩放&#xff0c;裁剪&#xff09;&#xff0c;反转矩阵的计算会很简单。Matrix4::invert()将会为你决定何时的反转方式&#xff0c;但是如果你明确地调用了一个反转函数:invertEuclidean(),invertAffine(),invertProjective()o或invertGeneral()。请阅读 Matrices.cpp中的详细细节。

 

 


  1. Matrix4 m1;

  2. m1.invert(); // 反转矩阵

 

 

 



 

例子&#xff1a;模型视图矩阵

 

下载源文件和二进制文件&#xff1a;matrix.zip

这个例子显示了怎么将Matrix4类集成到OpenGL程序中。GL_MODELVIEW联合和视图矩阵和模型矩阵&#xff0c;但是我们将它们分开并传递两个矩阵给OpenGL的模型视图矩阵。


  1. Matrix4 matModel, matView, matModelView;

  2. glMatrixMode(GL_MODELVIEW);

  3. ...

  4.  
  5. // 视图变换

  6. matView.identity(); // 变换次序:

  7. matView.rotate(-camAngleY, 0,1,0); // 1: 绕Y轴旋转

  8. matView.rotate(-camAngleX, 1,0,0); // 2: 绕X轴旋转

  9. matView.translate(0, 0, -camDist); // 3: 沿Z轴平移

  10.  
  11. //模型变换

  12. // 沿Y轴旋转45度&#xff0c;然后向上平移两个单位

  13. matModel.identity();

  14. matModel.rotate(45, 0,1,0); // 第一个变换

  15. matModel.translate(0, 2, 0); // 第二次变换

  16.  
  17. //构造模型视图矩阵: Mmv &#61; Mv * Mm

  18. matModelView &#61; matView * matModel;

  19.  
  20. // 将模型视图矩阵传递给OpenGL

  21. // 注意&#xff1a;需要将矩阵转置

  22. glLoadMatrixf(matModelView.getTranspose());

  23.  
  24. // 绘制

  25. ...

 

等价的OpenGL实现如下&#xff1a;

 


  1. //注意&#xff1a;变换的次序是相反的

  2. //因为OpenGL使用的是后乘

  3. glMatrixMode(GL_MODELVIEW);

  4. glLoadIdentity();

  5.  
  6. // 视图变换

  7. glTranslatef(0, 0, -camDist); // 3: 沿Z轴平移

  8. glRotatef(-camAngleX, 1,0,0); // 2: 绕X轴旋转

  9. glRotatef(-camAngleY, 0,1,0); // 1: 绕Y轴旋转

  10.  
  11. // 模型变换

  12. // 先绕Y轴旋转45度再向上平移2个单位

  13. glTranslatef(0, 2, 0); // 2&#xff1a;平移

  14. glRotatef(45, 0,1,0); // 1&#xff1a; 旋转

  15.  
  16. // 绘制

  17. ...

 

模型视图矩阵的反转用来将法向量从物体坐标系变换到人眼坐标系中。在可编程的渲染管线中&#xff0c;你需要将它传递给GLSL着色器。

 


  1. //为法向量构造矩阵: (M^-1)^T

  2. Matrix4 matNormal &#61; matModelView; // 得到模型视图矩阵

  3. matNormal.invert(); // 得到反转矩阵

  4. matNormal.transpose(); // 将矩阵转置

 

 



 

例子&#xff1a;投影矩阵

 

这个例子显示了如何创建投影矩阵&#xff0c;等价于glFrustum()和glOrtho()。更多细节查看source codes。

 


  1. // 设置投影矩阵并将其传递给OpenGL

  2. Matrix4 matProject &#61; setFrustum(-1, 1, -1, 1, 1, 100);

  3.  
  4. glMatrixMode(GL_PROJECTION);

  5. glLoadMatrixf(matProject.getTranspose());

  6. ...

  7.  
  8. ///

  9. // glFrustum()

  10. ///

  11. Matrix4 setFrustum(float l, float r, float b, float t, float n, float f)

  12. {

  13. Matrix4 mat;

  14. mat[0] &#61; 2 * n / (r - l);

  15. mat[2] &#61; (r &#43; l) / (r - l);

  16. mat[5] &#61; 2 * n / (t - b);

  17. mat[6] &#61; (t &#43; b) / (t - b);

  18. mat[10] &#61; -(f &#43; n) / (f - n);

  19. mat[11] &#61; -(2 * f * n) / (f - n);

  20. mat[14] &#61; -1;

  21. mat[15] &#61; 0;

  22. return mat;

  23. }

  24.  
  25. ///

  26. // gluPerspective()

  27. ///

  28. Matrix4 setFrustum(float fovY, float aspect, float front, float back)

  29. {

  30. float tangent &#61; tanf(fovY/2 * DEG2RAD); // 视角一半的切

  31. float height &#61; front * tangent; // 近平面高度的一半

  32. float width &#61; height * aspect; // 近平面宽度的一半

  33.  
  34. // 参数: left, right, bottom, top, near, far

  35. return setFrustum(-width, width, -height, height, front, back);

  36. }

  37.  
  38. ///

  39. // glOrtho()

  40. ///

  41. Matrix4 setOrthoFrustum(float l, float r, float b, float t, float n, float f)

  42. {

  43. Matrix4 mat;

  44. mat[0] &#61; 2 / (r - l);

  45. mat[3] &#61; -(r &#43; l) / (r - l);

  46. mat[5] &#61; 2 / (t - b);

  47. mat[7] &#61; -(t &#43; b) / (t - b);

  48. mat[10] &#61; -2 / (f - n);

  49. mat[11] &#61; -(f &#43; n) / (f - n);

  50. return mat;

  51. }

  52.  

推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
author-avatar
nuabolalalala4_135
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有