关于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的矩阵 (转)的更多相关文章

  1. [OpenGL](翻译+补充)投影矩阵的推导

    1.简介 基本是翻译和补充 http://www.songho.ca/opengl/gl_projectionmatrix.html 计算机显示器是一个2D的平面,一个3D的场景要被OpenGL渲染必 ...

  2. OpenGL 模型视图投影矩阵 仿射矩阵

    矩阵基础知识 要对矩阵进行运算,必须先要了解矩阵的计算公式,这个知识的内容涉及到了线性代数. 我们知道在Cocos2dx中,有关于平移,旋转,缩放等等操作,都必须要进行矩阵的乘法. 只需要一张图就能理 ...

  3. openGL 旋转的图形 矩阵操作

    #include <windows.h> #ifdef __APPLE__ #include <GLUT/glut.h> #else #include <GL/glut. ...

  4. ogre, dx, opengl坐标矩阵

    opengl 右手坐标系 列向量 左乘 列主序存储矩阵osg   右手坐标系 行向量 右乘 行主序存储矩阵d3d       左手坐标系 行向量 右乘 行主序存储矩阵ogre    右手坐标系 列向量 ...

  5. OpenGL 坐标与矩阵转换

    1. OpenGL 渲染管线 OpenGL渲染管线分为两大部分,模型观测变换(ModelView Transformation)和投影变换(Projection Transformation).做个比 ...

  6. OpenGL矩阵类(C++)

    概述 创建&初始化 存取器 矩阵运算 变换函数 实例:模型视图矩阵 实例:投影矩阵 概述 OpenGL固定功能管线提供4个不同类型的矩阵(GL_MODELVIEW.GL_PROJECTION. ...

  7. 矩阵-DirectX与OpenGL的不同

    http://www.cnblogs.com/graphics/archive/2012/08/02/2616017.html 矩阵是三维图形学中不可或缺的部分,几乎所有和变换相关的操作都涉及矩阵,世 ...

  8. OpenGL中glRotatef()函数究竟对矩阵做了什么

    OpenGL中glRotatef()函数究竟对矩阵做了什么 我们知道OpenGL中维持着两套矩阵,一个是模型视图矩阵(model view matrix),另一个是投影矩阵(projection ma ...

  9. opengl的矩阵理解

    原文链接:http://blog.csdn.net/byhuang/article/details/1476199 矩阵真的是一个很神奇的数学工具, 虽然单纯从数学上看, 它并没有什么特别的意义, 但 ...

随机推荐

  1. (转)log4j:WARN No appenders could be found for logger 解决方案

    我们在使用Log4j的时候,总是出现: log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.Log ...

  2. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程08:虚拟键盘实现》--本系列完结

    8.虚拟键盘实现 概述: 硬键盘就是物理键盘,平时敲的那种.软键盘是虚拟的键盘,不是在键盘上,而是在"屏幕"上.虚拟按键就是虚拟键盘的一部分,根据功能需求,提供部分按键效果的UI可 ...

  3. Dreamweaver SSH Tunneling

  4. 第三百四十二天 how can I 坚持

    再问世间都去哪儿了,天气预报没搞完,计划没制定,又周三了. 今天回到家八点,吃完饭接近九点,和老妈开了会视频,这就九点半多了,发了呆洗了个碗就到这时候了,整天浑浑噩噩的,该如何是好. 又有点上火,舌头 ...

  5. 转】启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099的解决办法

    原博文出自于:http://www.cnblogs.com/xdp-gacl/p/5288399.html   感谢! 一.问题描述 今天一来公司,在IntelliJ IDEA 中启动Tomcat服务 ...

  6. Android内存管理之道

    相信一步步走过来的Android从业者,每个人都会遇到OOM的情况.如何避免和防范OOM的出现,对于每一个程序员来说确实是一门必不可少的能力.今天我们就谈谈在Android平台下内存的管理之道,开始今 ...

  7. log4j:ERROR LogMananger.repositorySelector was null likely due to error in class reloading, using NOPLoggerRepository.

    The reason for the error is a new listener in Tomcat 6.0.24. You can fix this error by adding this l ...

  8. ACM 数论小结 2014-08-27 20:36 43人阅读 评论(0) 收藏

    断断续续的学习数论已经有一段时间了,学得也很杂,现在进行一些简单的回顾和总结. 学过的东西不能忘啊... 1.本原勾股数: 概念:一个三元组(a,b,c),其中a,b,c没有公因数而且满足:a^2+b ...

  9. 理解js闭包(一)

    @(编程) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. ...

  10. RAID对硬盘的要求及其相关

    Raid 0:至少需要两块硬盘,磁盘越多,读写速度越快,没有冗余. Raid 1:只能用两块硬盘,两块硬盘的数据互为镜像(写慢,读快),一块磁盘冗余. Raid 5:至少需要3块硬盘,一块磁盘冗余. ...