OpenGL矩阵类(C++)
概述
OpenGL固定功能管线提供4个不同类型的矩阵(GL_MODELVIEW、GL_PROJECTION、GL_TEXTURE与GL_COLOR),并且为这些矩阵提供变换函数:glLoadIdentity()、glTranslatef()、glRotatef()、glScalef()、glMultiMatrixf()、glFrustum()与glOrtho()。
这些内置矩阵与函数对于开发简单的OpenGL程序与理解矩阵变换很有帮助。不过,一旦你的应用程序变得复杂起来,更好的选择是为每一个可移动模型对象管理自己的矩阵实现。此外,在OpenGL可编程管线(GLSL)中,你不能够使用这些内置矩阵函数。如OpenGL v3.0+、OpenGL ES v2.0+与WebGL v1.0+。你必须拥有属于自己的矩阵实现,并且向OpenGL着色语言传递该矩阵数据。
该篇文章提供一个独立运行的、通用4×4矩阵类----C++写的Matrix4,并且描述如何将该矩阵类整合到OpenGL程序中。该矩阵类只依赖于相似的矩阵:定义于Vector.h的Vector3与Vector4。这些向量也包含在matrix.zip。
Matrix4创建&初始化

Matrix4使用行优先

OpenGL使用列优先
Matrix4类包含用于存放4×4方阵的16个元素的浮点数据类型数组,同时拥有3个构造函数用以实例化Matrix4类对象。
注意,Matrix4类使用行优先标记法,而不是OpenGL使用的列优先顺序。不过,行优先与列优先顺序只是将多维数组保存在线性(1D)内存中的不同方式,矩阵算法与操作并没有不同。
使用默认构造函数(不带任何参数的),Matrix4对象创建一个单位矩阵。另外3个构造函数使用16个参数或一个具有16个元素的数组。你也可以使用拷贝构造函数与赋值操作符(=)初始化Matrix4对象。
顺便说一下,Matrix4对象的拷贝构造函数与赋值构造函数将由C++编译器自动为你产生。
下面为以多种方式构造Matrix4对象的示例代码。首先,在使用Matrix4.h类的代码中包含Matrices.h文件。
#include "Matrices.h" // 为了使用 Matrix2, Matrix3, Matrix4
.. // 使用默认构造函数创建一个单位矩阵
Matrix4 m1; // 使用16个元素创建一个矩阵
Matrix4 m2(1, 1, 1, 1, // 第一行
1, 1, 1, 1, // 第二行
1, 1, 1, 1, // 第三行
1, 1, 1, 1); // 第四行 // 使用数组创建一个矩阵
float a[16] = {2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2};
Matrix4 m3(a); // 使用构造函数创建一个矩阵
Matrix4 m4(m3);
Matrix4 m5 = m3;
...
Matrix4存取器(赋值与取值)
赋值
Matrix4类提供set()方法赋值所有16个元素。
Matrix4 m1;
// 使用16个浮点数给矩阵赋值
m1.set(1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1); // 使用数组给矩阵赋值
float a1[] = {2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2};
m1.set(a1);
你也可以每次使用setRow()或setColumn()设置行或列元素。setRow()与setColumn()的第一个参数以0为基准的(0,1,2或3)。第二个参数为数组指针。
Matrix4 m2;
float a2[4] = {2,2,2,2}; // 使用行索引与数组设置一行
m2.setRow(0, a2); // 第一行 // 使用列索引与数组设置一列
m2.setColumn(2, a2); // 第三列
单位矩阵
Matrix4类拥有一个特殊的赋值函数identity()以创建一个单位矩阵。
// 设置单位矩阵
Matrix4 m3;
m3.identity();
获取
Matrix4::get()方法返回16个元素数组的指针。getTranspose()返回转置后的矩阵元素。它可用于向OpenGL传递矩阵数据。更详细信息请参考实例。
// 以数组指针方式获取矩阵元素
Matrix4 m4;
const float* a = m4.get(); // 向OpenGL传递矩阵
glLoadMatrixf(m4.getTranspose());
访问单独的元素
矩阵的独立元素也可以通过下标操作符[]访问。
Matrix4 m5;
float f = m5[0]; // 获取第一个元素 m5[1] = 2.0f; // 设置第2个元素
打印Matrix4
为了调试,Matrix4也提供一个使用std::ostream操作符<<的便捷打印函数。
// 打印矩阵
Matrix4 m6;
std::cout << m6 << std::endl;
矩阵运算
Matrix4提供在2个矩阵间的基本算术运算。
加法&减法
你可以进行两个矩阵相见与相加。
Matrix4 m1, m2, m3; // 加法
m3 = m1 + m2;
m3 += m1; // m3 = m3 + m2的简洁操作 // 减法
m3 = m1 - m2;
m3 -= m1; // m3 = m3 - m1的简洁操作
乘法
你可以将2个矩阵乘在一起,也可以进行3D/4D向量的乘法,用于使用矩阵对向量进行变换。注意,矩阵乘法不可交换。
Matrix4 m1, m2, m3; // 矩阵乘法
m3 = m1 * m2;
m3 *= m1; // m3 = m3 * m1的简洁写法 // 标量乘法
m3 = 2 * m1; // 缩放所有元素 // 向量乘法
Vector3 v1, v2;
v2 = m1 * v1;
v2 = v1 * m1; // 前向乘
比较
Matrix4类提供比较2个矩阵所有元素的操作。
Matrix4 m1, m2; // 绝对比较
if(m1 == m2)
std::cout << "equal" << std::endl;
if(m1 != m2)
std::cout << "not equal" << std::endl;
矩阵变换函数
OpenGL为矩阵变换提供许多函数:glTranslatef()、glRotatef()与glScalef()。Matrix4类提供变换矩阵的等价函数:translate()、rotate()与scale()。此外,Matrix4提供invert()用于计算逆矩阵。
Matrix4::translate(x,y,z)
translate()将当前矩阵平移(x,y,z)。首先,它生成一个平移矩阵M,接着将它与当前矩阵对象相乘产生最终变换矩阵:M=M•M
注意,该函数等价于OpenGL的glTranslatef(),不过OpenGL使用右乘而不是左乘(平移矩阵又乘回当前矩阵。)M=M•MT。如果你进行多次变换,你会发现结果会有很明显的不同,因为矩阵乘法是不可互换的。
对于应用多个变换,请参考“ModelView矩阵的更多实例”。
// M1 = Mt * M1
Matrix4 m1;
m1.translate(1, 2, 3); // move to (x, y, z)
Matrix4::rotate(angle, x, y, z)
rotate()用于将3D模型绕轴(x、y、z)旋转任意度数(角度)。该函数生成一旋转矩阵MR,然后将它与当前矩阵对象相乘,生成最终旋转变换矩阵:M=MR•M。
它等价于glRotatef(),使用右乘生成最终变换矩阵:M=M•MR
rotate()可以在任意轴上旋转。Matrix4类提供额外的3个函数进行基准轴旋转操作:rotateX()、rotateY()与rotateZ()。
// M1 = Mr * M1
Matrix4 m1;
m1.rotate(45, 1,0,0); // 绕X轴旋转45度
m1.rotateX(45); // 与rotate(45, 1, 0, 0)相同
Matrix4::scale(x,y,z)
scale()通过将当前矩阵对象与缩放矩阵相乘:M=Ms•M,在轴(x,y,z)上生成一个不均匀的缩放变换矩阵。
再次注意,OpenGL的glScalef()执行右乘:M=M•Ms。
Matrix4类也提供均匀缩放函数。
// M1 = Ms * M1
Matrix4 m1;
m1.scale(1,2,3); // 非均匀缩放
m1.scale(4); // 均匀缩放 (所有轴都一样)
Matrix::invert()
invert()函数计算当前矩阵的逆矩阵。该你矩阵通常用于将法向量从模型对象空间变换到观察空间。法向量与顶点的变换不同。法向量通过乘以GL_MODELVIEW矩阵的逆矩阵变换到观察空间:n'=nTM-1 = (M-1)Tn。详细解释参考这里。
如果矩阵仅仅为欧氏变换(旋转与平移)或仿射变换(再加上缩放与剪切),逆矩阵的变换就非常简单。Matrix4::invert()将判断合适的求逆方法,不过你可以调用更具体的求逆函数:invertEuclidean()、invertAffine()、invertProjective()或invertGeneral()。请参考Matrices.cpp中的详细描述。
Matrix4 m1;
m1.invert(); // 矩阵求逆
实例:模型视图矩阵
下载源文件与二进制文件:matrix.zip。
该实例展示如何将Matrix4类应用到OpenGL中。GL_MODELVIEW矩阵是视图矩阵与模型矩阵的组合,不过我们单独保存它们,并且在需要时向OpenGL的GL_MODELVIEW传递二者的乘积。
Matrix4 matModel, matView, matModelView;
glMatrixMode(GL_MODELVIEW);
... // orbital camera (view)
matView.identity(); // 变换顺序
matView.rotate(-camAngleY, 0,1,0); // 1: 绕Y轴旋转
matView.rotate(-camAngleX, 1,0,0); // 2: 绕X轴旋转
matView.translate(0, 0, -camDist); // 3: 沿Z轴移动 // 模型变换:绕Y轴旋转45度,然后向上移动2个单位
matModel.identity();
matModel.rotate(45, 0,1,0); // 第一次变换
matModel.translate(0, 2, 0); // 第二次变换 // 生成模型视图矩阵: Mmv = Mv * Mm
matModelView = matView * matModel; // 在绘制之前传给OpenGL
// 注意: 需要进行转置操作
glLoadMatrixf(matModelView.getTranspose()); // 绘制
...
等价OpenGL实现如下。结果与上面的相同
//注意: 变换顺序相反,因为OpenGL使用右乘
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); // 相机轨迹(视图)
glTranslatef(0, 0, -camDist); // 3: 沿Z轴移动
glRotatef(-camAngleX, 1,0,0); // 2: 绕X轴旋转
glRotatef(-camAngleY, 0,1,0); // 1: 绕Y轴旋转 // 模型变换: 绕Y轴旋转45度,然后向上移动2个单位
glTranslatef(0, 2, 0); // 第二次变换
glRotatef(45, 0,1,0); // 第一次变换 // 绘制
...
模型视图矩阵的逆矩阵用于从模型对象空间到观察空间的法向量变换。在可编程渲染管线中,你需要向GLSL着色器传递它。
// 为法向量构建逆矩阵: (M^-1)^T
Matrix4 matNormal = matModelView; // 获取模型视图矩阵
matNormal.invert(); // 获取法向量变换的逆矩阵
matNormal.transpose(); // 转置矩阵
实例:投影矩阵
该实例展示如何产生投影矩阵,等价于glFrustum()与glOrtho()。更详细描述请参考源代码。
// 设置投影矩阵并传递到OpenGL
Matrix4 matProject = setFrustum(-1, 1, -1, 1, 1, 100); glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matProject.getTranspose());
... ///////////////////////////////////////////////////////////////////////////////
// glFrustum()
///////////////////////////////////////////////////////////////////////////////
Matrix4 setFrustum(float l, float r, float b, float t, float n, float f)
{
Matrix4 mat;
mat[0] = 2 * n / (r - l);
mat[2] = (r + l) / (r - l);
mat[5] = 2 * n / (t - b);
mat[6] = (t + b) / (t - b);
mat[10] = -(f + n) / (f - n);
mat[11] = -(2 * f * n) / (f - n);
mat[14] = -1;
mat[15] = 0;
return mat;
} ///////////////////////////////////////////////////////////////////////////////
// gluPerspective()
///////////////////////////////////////////////////////////////////////////////
Matrix4 setFrustum(float fovY, float aspect, float front, float back)
{
float tangent = tanf(fovY/2 * DEG2RAD); // fovY的半角
float height = front * tangent; // 近平面半高
float width = height * aspect; // 近平面半宽 // 参数: left, right, bottom, top, near, far
return setFrustum(-width, width, -height, height, front, back);
} ///////////////////////////////////////////////////////////////////////////////
// glOrtho()
///////////////////////////////////////////////////////////////////////////////
Matrix4 setOrthoFrustum(float l, float r, float b, float t, float n, float f)
{
Matrix4 mat;
mat[0] = 2 / (r - l);
mat[3] = -(r + l) / (r - l);
mat[5] = 2 / (t - b);
mat[7] = -(t + b) / (t - b);
mat[10] = -2 / (f - n);
mat[11] = -(f + n) / (f - n);
return mat;
}
...
英文原文:http://www.songho.ca/opengl/gl_matrix.html
OpenGL矩阵类(C++)的更多相关文章
- OpenGL矩阵类(C++) 【转】
http://www.cnblogs.com/hefee/p/3816727.html OpenGL矩阵类(C++) 概述 创建&初始化 存取器 矩阵运算 变换函数 实例:模型视图矩阵 实例: ...
- NPOI操作EXCEL(六)——矩阵类表头EXCEL模板的解析
哈哈~~~很高兴还活着.总算加班加点的把最后一类EXCEL模板的解析做完了... 前面几篇文章介绍了博主最近项目中对于复杂excel表头的解析,写得不好,感谢园友们的支持~~~ 今天再简单讲诉一下另一 ...
- [Java]编写自己的Matrix矩阵类
用java实现一个简单的矩阵类,可以实现简单的矩阵计算功能. class Matrix 1.向量点乘 public static double dot(double[] x,double[] y) 2 ...
- 精解Mat类(一):基本数据类型-固定大小的 矩阵类(Matx) 向量类(Vector)
一.基础数据类型 1.(基础)固定大小矩阵类 matx 说明: ① 基础矩阵是我个人增加的描述,相对于Mat矩阵类(存储图像信息的大矩阵)而言. ② 固定大小矩阵类必须在编译期间就知晓其维 ...
- 矩阵类的python实现
科学计算离不开矩阵的运算.当然,python已经有非常好的现成的库:numpy. 我写这个矩阵类,并不是打算重新造一个轮子,只是作为一个练习,记录在此. 注:这个类的函数还没全部实现,慢慢在完善吧. ...
- C++实现矩阵类和向量类
C++期末作业内容,写完之后觉得过于臃肿,又重新搞了个新的.新的当作业交,旧的拿来给同学参考. [问题描述]请仿照复数类,设计一个矩阵类,设计矩阵类的构成元素 1.编写构造函数完成初始化 2.编写成员 ...
- opengl矩阵向量
如何创建一个物体.着色.加入纹理,给它们一些细节的表现,但因为它们都还是静态的物体,仍是不够有趣.我们可以尝试着在每一帧改变物体的顶点并且重配置缓冲区从而使它们移动,但这太繁琐了,而且会消耗很多的处理 ...
- 实用矩阵类(Matrix)(带测试)
引言: 无意间看到国外一个网站写的Matrix类,实现了加减乘除基本运算以及各自的const版本等等,功能还算比较完善,,于是记录下来,以备后用: #ifndef MATRIX_H #define M ...
- [Android] 使用Matrix矩阵类对图像进行缩放、旋转、对照度、亮度处理
前一篇文章讲述了Android拍照.截图.保存并显示在ImageView控件中,该篇文章继续讲述Android图像处理技术,主要操作包含:通过打开相冊里的图片,使用Matrix对图像进行缩放. ...
随机推荐
- DEELX 正则表达式引擎(v1.2)
DEELX 正则表达式引擎(v1.2) 简介见文末. 选择使用deelx的理由:全部代码位于一个头文件(.h)中, 比任何引擎都使用简单和方便. 利用分组从字符串当中提取出化学元素英文名.比如 Ag, ...
- 面试题2:BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇
BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇 Html篇: 1.你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? IE: trident内核 Firefox:gecko内 ...
- Bootstrap结合BootstrapTable的使用,分为两种模试显示列表。 自适应表格
引用的css: <link href="@Url.Content("~/Css/bootstrap.min.css")" rel="styles ...
- 在centos6.5中安装scp和lrzsz
简介 scp用于在两台centos中传输文件用的,lrzsz用于在xshell上传输本地文件到远程centos服务器上用的 1.安装scp [root@localhost ~]# scp -ba ...
- vue学习笔记之v-for与-repeat
今天看到一个v-repeat的例子 <body> <ul id="tags"> <li v-repeat="tags"> { ...
- ListView.DragEnter触发不了
经过千百度的搜索之后,终于找到了一点线索,原文是:https://msdn.microsoft.com/en-us/magazine/mt185571.aspx 有能力的可以参阅原文,想省事的可以等待 ...
- jQuery经典面试题及答案精选(转)
jQuery是一款非常流行的Javascript框架,如果你想要从事Web前端开发这个岗位,那么jQuery是你必须掌握而且能够熟练应用的一门技术.本文整理了一些关于jQuery的经典面试题及答案,分 ...
- user-select : 保护版权内容的简单方案
有的适合我们需要保护我们页面的内容,为了版权或者安全等原因,这个适合我们可以使用 user-select 这个CSS属性,简单易用. 嗯,这个属性不麻烦,而且也不是 CSS 3 / CSS 4 的新属 ...
- C#压缩文件夹
using System;using System.Collections.Generic;using System.Text; ///第三方dllusing ICSharpCode.SharpZip ...
- mysql之存储过程
一.存储过程 迄今为止,使用的大多数 SQL语句都是针对一个或多个表的单条语句.并非所有操作都这么简单,经常会有一个完整的操作需要多条语句才能完成.例如,考虑以下的情形. 1. ...