关于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 矩阵真的是一个很神奇的数学工具, 虽然单纯从数学上看, 它并没有什么特别的意义, 但 ...
随机推荐
- 【Unity入门】场景、游戏物体和组件的概念
版权声明:本文为博主原创文章,转载请注明出处. 游戏和电影一样,是通过每一个镜头的串联来实现的,而这样的镜头我们称之为“场景”.一个游戏一般包含一个到多个场景,这些场景里面实现了不同的功能,把它们组合 ...
- linux系统中内存爆满之后会如何?
在使用python写程序的时候,发现一个可以无限迭代的迭代器,从而可以直接将系统中的内存占满,那么占满之后会发生什么呢? 1. 创建无限迭代,生成列表,如下: [root@python ~]# pyt ...
- 闲谈Future模式-订蛋糕
一. Future模式简介 Future有道翻译:n. 未来:前途:期货:将来时.我觉得用期货来解释比较合适.举个实际生活中例子来说吧,今天我女朋友过生日,我去蛋糕店准备给女朋友定个大蛋糕,超级大的那 ...
- Hibernate中openSession() 与 getCurrentSession()的区别
1 getCurrentSession创建的session会和绑定到当前线程,而openSession每次创建新的session. 2 getCurrentSession创建的线程会在事务回滚或事物提 ...
- LeetCode(5) - Longest Palindromic Substring
这道题要求的是给你一个string, 如“adcdabcdcba",要求返回长度最大的回文子字符串.这里有两个条件,一是子字符串,而是回文.用纯暴力搜索的话,需要用到O(n^3)的时间,必然 ...
- cocos2d-html5将js编译为jsc
在d:\DevTool\cocos2d-x-2.2.2\cocos2d-x-2.2.2\tools\cocos2d-console\console 有 cocos2d_jscompile.py coc ...
- 关于文章“cocos2dx移植android平台-我的血泪史”需要注意事项
关于文章"cocos2dx移植android平台-我的血泪史"需要注意事项 在上次转载的这篇文章中,按照配置一步一步的下去.发现工程中在Android.mk中有一处错误.直接bui ...
- 使用IIS6.0遇到问题后,常用的几种解决方法
1.检查 .Net Framework,是否安装完全,不确定的情况下使用:aspnet_regiis.exe -i 或者 aspnet_regiis.exe -r 2.检查 IIS 6.0 其它相关配 ...
- OC动态特性
今天是2.15周日,快要过年了,我以一个实习生的身份在公司工作了快要两个月了吧,什么是北漂,北漂就是感觉生活节奏变了,以前困了可以上课睡觉,累了可以回家休息数周,人际交往乏了,可以躲起来看着窗外的雨或 ...
- LightOJ 13361336 - Sigma Function (找规律 + 唯一分解定理)
http://lightoj.com/volume_showproblem.php?problem=1336 Sigma Function Time Limit:2000MS Memory L ...