概述

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++)的更多相关文章

  1. OpenGL矩阵类(C++) 【转】

    http://www.cnblogs.com/hefee/p/3816727.html OpenGL矩阵类(C++) 概述 创建&初始化 存取器 矩阵运算 变换函数 实例:模型视图矩阵 实例: ...

  2. NPOI操作EXCEL(六)——矩阵类表头EXCEL模板的解析

    哈哈~~~很高兴还活着.总算加班加点的把最后一类EXCEL模板的解析做完了... 前面几篇文章介绍了博主最近项目中对于复杂excel表头的解析,写得不好,感谢园友们的支持~~~ 今天再简单讲诉一下另一 ...

  3. [Java]编写自己的Matrix矩阵类

    用java实现一个简单的矩阵类,可以实现简单的矩阵计算功能. class Matrix 1.向量点乘 public static double dot(double[] x,double[] y) 2 ...

  4. 精解Mat类(一):基本数据类型-固定大小的 矩阵类(Matx) 向量类(Vector)

    一.基础数据类型 1.(基础)固定大小矩阵类 matx 说明: ①    基础矩阵是我个人增加的描述,相对于Mat矩阵类(存储图像信息的大矩阵)而言. ②    固定大小矩阵类必须在编译期间就知晓其维 ...

  5. 矩阵类的python实现

    科学计算离不开矩阵的运算.当然,python已经有非常好的现成的库:numpy. 我写这个矩阵类,并不是打算重新造一个轮子,只是作为一个练习,记录在此. 注:这个类的函数还没全部实现,慢慢在完善吧. ...

  6. C++实现矩阵类和向量类

    C++期末作业内容,写完之后觉得过于臃肿,又重新搞了个新的.新的当作业交,旧的拿来给同学参考. [问题描述]请仿照复数类,设计一个矩阵类,设计矩阵类的构成元素 1.编写构造函数完成初始化 2.编写成员 ...

  7. opengl矩阵向量

    如何创建一个物体.着色.加入纹理,给它们一些细节的表现,但因为它们都还是静态的物体,仍是不够有趣.我们可以尝试着在每一帧改变物体的顶点并且重配置缓冲区从而使它们移动,但这太繁琐了,而且会消耗很多的处理 ...

  8. 实用矩阵类(Matrix)(带测试)

    引言: 无意间看到国外一个网站写的Matrix类,实现了加减乘除基本运算以及各自的const版本等等,功能还算比较完善,,于是记录下来,以备后用: #ifndef MATRIX_H #define M ...

  9. [Android] 使用Matrix矩阵类对图像进行缩放、旋转、对照度、亮度处理

        前一篇文章讲述了Android拍照.截图.保存并显示在ImageView控件中,该篇文章继续讲述Android图像处理技术,主要操作包含:通过打开相冊里的图片,使用Matrix对图像进行缩放. ...

随机推荐

  1. 原生Ajax讲解

    典型的http通信:浏览器向服务器发出请求,服务器向客户端返回响应,浏览器重新加载页面,这种不连续的页面加载方式导致用户的体验变得杂乱,缺乏连贯性. 如: 在一般的web应用程序中,用户填写表单字段然 ...

  2. hdu3228Island Explorer

    链接 给你两条线及两条线上的点,求最小生成树. 可以挨个枚举一条线上的点,三分出另一条线上离他最近的点进行连边. 注意N.M可能为0 debug了1天半,至今不知道原始二分版本错在哪里.. #incl ...

  3. golang 文件读取

    Golang 的文件读取方法很多,刚上手时不知道怎么选择,所以贴在此处便后速查. 一次性读取 小文件推荐一次性读取,这样程序更简单,而且速度最快. 复制代码 代码如下: func ReadAll(fi ...

  4. SAP的物料归档

    我们在对前台对物料进行删除时,是物理删除,也就是打个删除标志,并没有正真的从数据库里删除,在前台还是可以看到的,下面介绍一下SAP的归档处理可以 把已删除的物料在前台删除掉,注意:项目里根据情况得到领 ...

  5. 基础知识复习(一)——C语言位运算符详解

    常用的位运算符:与(&),取反(~),或(|),异或(^),左移(«),右移(») 1. 与(&)操作符,按位与,全为1 时,结果取1 11001 &10011 结果:1000 ...

  6. Laravel 流程分析——整体概论

    从整体上来看(不考虑细节),Laravel流程相当简单,我们分析一下index.php文件(下面的第几行为实际代码,不是指文件的行) 第一行定义自动加载 require __DIR__.'/../bo ...

  7. 【修改端口号】linux下修改apache,nginx服务端口号

    一.linux下修改apache端口号 yum安装后,apache配置文件: /etc/httpd/conf/httpd.conf 找到apache目录下的 httpd.conf, 使用vi 打开,找 ...

  8. 导出Excel 有身份证时注意

    if (this.GridView1.Rows.Count != 0)            {                HttpContext.Current.Response.Clear() ...

  9. html5的spellcheck属性(拼写、文法检查)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  10. 编写windows版ANE

    1.编写WinANE.dll: #include <windows.h> #include <stdlib.h> #include <FlashRuntimeExtens ...