关于OPenGL和OSG的矩阵 (转)
关于OPenGL和OSG的矩阵
矩阵真的是一个很神奇的数学工具, 虽然单纯从数学上看, 它并没有什么特别的意义, 但一旦用到空间中的坐标变换,它就“一遇风云便成龙”, 大显神威了。简单的工具实现了复杂的功能,便预示着要理解它我们还是要花上点功夫的。下面就简单介绍一下OpenGL中的转换矩阵。
1 转换矩阵的原理
OpenGL中的转换矩阵是这样定义的:
[Xx, Yx, Zx, Tx]
[Xy, Yy, Zy, Ty]
M =
[Xz, Yz, Zz, Tz]
[0, 0, 0, 1 ]
其实我们可以这么理解这个变换矩阵, 它表示了一个局部坐标系, 这个局部坐标系,是把世界坐标系的原点移到(Tx, Ty,
Tz),把X轴转到(Xx, Xy, Xz), Y轴转到(Yx, Yy, Yz),Z轴转到(Zx, Zy,
Zz)而形成的。用它来变换一个世界坐标系中的点V, 就是得到这个局部坐标系中的点。
要证明这一点很容易, 我们从可以从更通用的方面来考虑,假设我们用矩阵Ma来表示坐标系a, Mb来表示坐标系b,
Mt表示从a到b的转换, 那么:
Mt * Ma = Mb
Mt * Ma * (Ma)^-1 = Mb * (Ma)^-1
矩阵虽然不符合乘法交换律,但其符合乘法结合律, 于是:
Mt* (Ma * (Ma)^-1) = Mb * (Ma)^-1
Mt = Mb * (Ma)^-1
这就是a到b转换矩阵的表达式,现在我们从世界坐标系转换到局部坐标系,a表示的世界坐标系是个单位矩阵,所以:
Mt = Mb
即局部坐标系的矩阵表示就是从世界坐标系到局部坐标系的转换矩阵。
我们再进一步分析,如果我们用这个矩阵来变换一个点V(Vx, Vy, Vz, 1),需要把这个点右乘变换矩阵
[Xx, Yx, Zx, Tx] [Vx]
[Xy, Yy, Zy, Ty] [Vy]
V' = M*T
=
[Xz, Yz, Zz, Tz] * [Vz]
[0, 0, 0, 1
] [1 ]
对于V变换后的x分量,Vx' = Xx*Vx + Yx*Vy + Zx*Vz +
Tx,我们可以发现影响V的x分量的只有X,Y,Z轴旋转的x分量和平移的x分量,对于V的y, z分量也是同样道理。
2 行主序, 列主序
OpenGL中推荐用一维数组来表示此转换矩阵 : typedef GLfloat Matrix16[16];
为了能快速的访问X轴, Y轴, Z轴, 该数组是按列主序来表示这个矩阵的:
[m0, m4, m8, m12]
[m1, m5, m9, m13]
[m2, m6, m10,m14]
[m3, m7, m11,m15]
这样, 为了访问X轴, 即访问m0, m1, m2,因为他们是连续的存储空间,所以速度比较快, 相反,
如果我们数组按行主序来表示这个矩阵:
[m0, m1, m2, m3 ]
[m4, m5, m6, m7 ]
[m8, m9, m10, m11]
[m12, m13, m14, m15]
我们发现为了访问X轴, 即m0, m4, m8, 是不连续的地址, 因此速度就慢了下来。
所以我们可以知道, OpenGL为什么采用列主序的矩阵, 那是因为其所定义的转换矩阵如果按列主序存入数组,
我们对X,Y,Z轴就可以有较快的访问速度。也就是说, 如果我非要把这个矩阵按列主序的方式存入数组也可以,
只不过速度慢了点而已。(当然, 我们要告诉OpenGL我们是按行主序表示的)。
其实, 如果我们换一种方式来表示转换矩阵:
[Xx, Xy, Xz, 0]
[Yx, Yy, Yz, 0]
M'
=
[Zx, Zy, Zz, 0]
[Tx, Ty, Tz, 1]
这个矩阵是是前一个转换矩阵的转置,我们把这个矩阵按行主序存入数组就比较划算了。原因很明显, 为了快速访问X轴,我们希望Xx,
Xy, Xz是连续存储的, 那么自然要按行存储了。
其实, 如果让我设计OpenGL,我会选择用第二种方式来表示转换矩阵,原因如下:
如果我要转换一个点V, 依次经过三个转换矩阵L, M, N的转换, 那么对于第一种方式:
V' = N*(M*(L*V)) = (N*M*L) * V
我们的组合转换矩阵是N*M*L, 与我们定义的转换过程刚好相反, 但是,
如果我们是第二种方式表示的话,我转换一个点是左乘转换矩阵而不是右乘了:
V' = ((V*L)*M)*N = V * (L*M*N)
组合转换矩阵是按我们变换的顺序组合起来的, 就比较直观了, 然后我们按行主序存储此矩阵, 速度依然。
3 二维数组存储矩阵
很多人有这样错误的认识, 就是在OpenGL中如果用二维数组来表示转换矩阵, 速度就比较慢,
而这种认识或多或少源于<<OpenGL超级宝典>>中的阐述。但是,
事实是这样吗?
二维数组如下:
typedef GLfloat Matrix44[4][4];
按我们理解的,逻辑上的二维数组, 其表示为:
[m00, m01, m02, m03]
[m10, m11, m12, m13]
[m20, m21, m22, m23]
[m30, m31, m32, m33]
因为这个逻辑模型, 导致我们产生那种错误的认识:
X轴是用m00, m10, m20表示的, 而他们是不连续的, 所以比较慢, 但是, 这只是其逻辑模型, 如果按逻辑模型去理解的话,
一维数组的逻辑模型是:
[m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14,
m15]
那我们是不是可以说, 一维数组根本不能用来表示矩阵? 当然不是。
其实, 不论是一维数组还是二维数组, 其在内存中的物理模型都是连续的16个float型的内存单元:
一维数组:[m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13,
m14, m15]
二维数组:[m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23,
m30, m31, m32, m33]
看到这里, 既然一维数组可以用列主序表示并很快, 为什么二维数组就不快了呢?他们除了访问时的名字不一样,
本质上并没有区别啊:
[m00, m10, m20, m30]
[m01, m11, m21, m31]
[m02, m12, m22, m32]
[m03, m13, m23, m33]
我们可以看到,二维数组按列主序表示的转换矩阵是这样的, 访问X轴即访问m00, m01, m02, 连续的, 一样快。
只不过, 这种表示方式和我们所理解的二维数组的逻辑模型不太统一,
有些不直观罢了。这一点在OpenGL红宝书的说的比较正确:二维数组的元素m[i][j]将位于OpenGL变换矩阵的第i列, 第j行,
因此容易产生行列混淆,为了避免行列混淆, 推荐用一维数组表示。 真正的原因是为了避免行列混淆, 而不是速度。
发现OSG对
矩阵的存储和矩阵变换的使用方式与OpenGL的用法有些不一致:
1. 在OpenGL中使用glMultMatrix/glLoadMatrix 设
置矩阵时,参数矩阵需要是列主序存储的;而OSG中的矩阵(Matrixd)却是按行主序存储的(仍然使用
glMultMatrix/glLoadMatrix 设置矩阵),二者互为转置。
2. 红宝书中讲到,OpenGL中对顶点坐
标应用矩阵变换,应该是左乘矩阵(v' = M × v
);然而,我在OSG中计算顶点坐标投影的视口坐标的时候,却需要使用右乘才能得到正确的结果,计算裁剪坐标的代码如下:
osg::Matrix
matMVP =
view->getCamera()->getViewMatrix() *
view->getCamera()->getProjectionMatrix();
v =
matMVP.preMult(v);
// 右乘矩阵
即 OpenGL中的矩阵变换为:矩阵×列向量
OSG中的矩阵变换为:行向量×矩阵
如果对于同一个向量v(x,y,z,w)(可以作为行向量也可以作为列向量),应用同一个矩阵变换M,使用左乘和使用右乘得到的结果显然是不同的;
单独考虑以上的两点,似乎都是不可理解的
但是考虑以下情况的结果:
[x,y,z,w]×[m0,m1,m2...m15](行主序)
与
[m0,m1,m2...m15](列主序)×[x,y,z,w]
(或者写作:
v×M
与
MT×v (MT为M的转置))
关于OPenGL和OSG的矩阵 (转)的更多相关文章
- [OpenGL](翻译+补充)投影矩阵的推导
1.简介 基本是翻译和补充 http://www.songho.ca/opengl/gl_projectionmatrix.html 计算机显示器是一个2D的平面,一个3D的场景要被OpenGL渲染必 ...
- OpenGL 模型视图投影矩阵 仿射矩阵
矩阵基础知识 要对矩阵进行运算,必须先要了解矩阵的计算公式,这个知识的内容涉及到了线性代数. 我们知道在Cocos2dx中,有关于平移,旋转,缩放等等操作,都必须要进行矩阵的乘法. 只需要一张图就能理 ...
- openGL 旋转的图形 矩阵操作
#include <windows.h> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut. ...
- ogre, dx, opengl坐标矩阵
opengl 右手坐标系 列向量 左乘 列主序存储矩阵osg 右手坐标系 行向量 右乘 行主序存储矩阵d3d 左手坐标系 行向量 右乘 行主序存储矩阵ogre 右手坐标系 列向量 ...
- OpenGL 坐标与矩阵转换
1. OpenGL 渲染管线 OpenGL渲染管线分为两大部分,模型观测变换(ModelView Transformation)和投影变换(Projection Transformation).做个比 ...
- OpenGL矩阵类(C++)
概述 创建&初始化 存取器 矩阵运算 变换函数 实例:模型视图矩阵 实例:投影矩阵 概述 OpenGL固定功能管线提供4个不同类型的矩阵(GL_MODELVIEW.GL_PROJECTION. ...
- 矩阵-DirectX与OpenGL的不同
http://www.cnblogs.com/graphics/archive/2012/08/02/2616017.html 矩阵是三维图形学中不可或缺的部分,几乎所有和变换相关的操作都涉及矩阵,世 ...
- OpenGL中glRotatef()函数究竟对矩阵做了什么
OpenGL中glRotatef()函数究竟对矩阵做了什么 我们知道OpenGL中维持着两套矩阵,一个是模型视图矩阵(model view matrix),另一个是投影矩阵(projection ma ...
- opengl的矩阵理解
原文链接:http://blog.csdn.net/byhuang/article/details/1476199 矩阵真的是一个很神奇的数学工具, 虽然单纯从数学上看, 它并没有什么特别的意义, 但 ...
随机推荐
- selenium python (七)层级定位(二次定位)
#!/usr/bin/python# -*- coding: utf-8 -*-__author__ = 'zuoanvip' #在实际测试过程中,一个页面可能有多个属性基本相同的元素,如果要定位到其 ...
- Excel的 OleDb 连接串的格式
Excel的 OleDb 连接串的格式(Provider=Microsoft.ACE.OLEDB)(2012-08-02 13:04:20) string strCon = "Provide ...
- 精选PSD素材下载周刊【Goodfav PSD 20130720】
我们每周精选来自Goodfav PSD的免费PSD素材,有兴趣的朋友尤其是做设计工作的,不妨收藏或者下载. 这些现成的PSD素材能给我们一些相关的灵感,从而提高工作的效率. 1.File Upload ...
- javascript常用的小知识
1. oncontextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键 <table border oncontextmenu ...
- c++ struct 使用
typedef与结构结合使用 typedef struct tagMyStruct{ int iNum; long lLength;} MyStruct; 这语句实际上完成两个操作: 1) 定义一 ...
- 【Hadoop代码笔记】Hadoop作业提交之Job初始化
一.概要描述 在上一篇博文中主要描述了JobTracker和其几个服务(或功能)模块的接收到提交的job后的一些处理.其中很重要的一部分就作业的初始化.因为代码片段图的表达问题,本应该在上篇描述的内容 ...
- 基于MapReduce的关系代数运算(2)
1.自然连接 Map函数:对于R中的每个元组(a,b),生成键值对(b,(R,a)),对于S中的每个元组(b,c),生成键值对(b,(S,c)) Reduce函数:每个键值b会与一系列对相关联,这些对 ...
- JQuery发送Put、Delete请求 - 摘自网络
DELETE: $.ajax({ url: '/script.cgi', type: 'DELETE', success: function(result) { // Do something wit ...
- php基础知识(1)
1.判断变量是否存在isset() $v1="a"; if(!isset($v1)){ echo "变量存在"; }else{ echo "变量不存在 ...
- 2016 ACM/ICPC 沈阳站 小结
铜铜铜…… 人呐真奇怪 铁牌水平总想着运气好拿个铜 铜牌水平总想着运气好拿个银 估计银牌的聚聚们一定也不满意 想拿个金吧 这次比赛挺不爽的 AB两道SB题,十分钟基本全场都过了 不知道出这种题有什么意 ...