原文链接:http://blog.csdn.net/byhuang/article/details/1476199

矩阵真的是一个很神奇的数学工具, 虽然单纯从数学上看, 它并没有什么特别的意义, 但一旦用到空间中的坐标变换,它就“一遇风云便成龙”, 大显神威了。简单的工具实现了复杂的功能,便预示着要理解它我们还是要花上点功夫的。下面就简单介绍一下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行,
因此容易产生行列混淆,为了避免行列混淆, 推荐用一维数组表示。 真正的原因是为了避免行列混淆, 而不是速度。

opengl的矩阵理解的更多相关文章

  1. Opengl中矩阵和perspective/ortho的相互转换

    Opengl中矩阵和perspective/ortho的相互转换 定义矩阵 Opengl变换需要用四维矩阵.我们来定义这样的矩阵. +BIT祝威+悄悄在此留下版了个权的信息说: 四维向量 首先,我们定 ...

  2. OpenGL投影矩阵

    概述 透视投影 正交投影 概述 计算机显示器是一个2D平面.OpenGL渲染的3D场景必须以2D图像方式投影到计算机屏幕上.GL_PROJECTION矩阵用于该投影变换.首先,它将所有定点数据从观察坐 ...

  3. OpenGL投影矩阵【转】

    OpenGL投影矩阵 概述 透视投影 正交投影 概述 计算机显示器是一个2D平面.OpenGL渲染的3D场景必须以2D图像方式投影到计算机屏幕上.GL_PROJECTION矩阵用于该投影变换.首先,它 ...

  4. OpenGL的矩阵使用——绘制桌子

    其中最左边的桌子循环上移(即匀速上移到一定位置后回到原点继续匀速上移),中间的桌子不断旋转(即绕自身中间轴旋转),最右边的桌子循环缩小(即不断缩小到一定大小后回归原来大小继续缩小). 桌子的模型尺寸如 ...

  5. OpenGL坐标系的理解

    搬运自: https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/#3d 为了将坐标从一个坐 ...

  6. OPENGL之矩阵

    前面的若干重要概念中描述了OPENGL中的几个重要变换,而矩阵是线性代数中的重要数学工具,它被用来对这些变换进行数学上的实现. 矩阵主要有以下几种: 模型视图矩阵:模型视图矩阵是个4*4的矩阵,代表经 ...

  7. ogre, dx, opengl坐标矩阵

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

  8. opengl微发展理解

    1.什么是OpenGL? 一种程序,可以与界面和图形硬件交互作用.一个开放的标准 2.软件管道 请看上图 - Apllication层     表示你的程序(调用渲染命令.如opengl API) - ...

  9. OpenGL的状态机理解

    OpenGL是一种状态机模式,比如你用glEnable打开一个状态,在以后的绘图中将一直保留并应用这个状态,除非你调用glDisable及同类函数来改变该状态或程序退出.例如当前颜色是一个状态变量,可 ...

随机推荐

  1. Regression Analysis Using Excel

    Regression Analysis Using Excel Setup By default, data analysis add-in is not enabled. Follow the st ...

  2. 批处理学习笔记3 - 变量声明和goto代替while循环

    批处理中没有while循环,只能用goto代替.下面是代码 @echo off set /a i = 0 :again echo %i% set /a i= %i% + 1 if %i% lss 10 ...

  3. SQL中的LIKE中用参数化查询

    今天终于学会怎么在like中用参数化查询啦..哈哈..再也不用担心sql注入了...  

  4. ajax 兼容性问题解决 集锦

    这两天刚解决了ajax多浏览器兼容的问题,主要就针对Firefox的,开始还以为Firefox不支持ajax呢(别笑我呀,不怎么了解Firefox也没用过,呵呵),多亏看了下面的文章才让我了解ajax ...

  5. 利用U盘进行软件加密的方法

    利用U盘进行软件加密的方法 一般的U盘不具备加密的功能,虽然U盘和加密狗外形有一些相似,但是内部完全不一样的,U盘只是一个存储器芯片和简单的附属电路,而现在的智能卡加密狗都具有一个单独的CPU或者加密 ...

  6. python(28)获得网卡的IP地址,如何在其他文件夹中导入python模块

    获得第几块网卡的ip地址: 如何在其他文件夹中导入模块 import sys sys.path.append('/search/chen/tool')#你的代码存放的目录 from Get_Ip im ...

  7. .net 中异步SOCKET发送数据时碰到的内存问题

    做CS的开发一直都是这样的方式: server端用 C++编写,采用IOCP机制处理大量客户端连接.数据接收发送的问题 client端用 C++ 或C# 写,没什么特殊要求. 最近工作时间上比较宽裕, ...

  8. Web攻防系列教程之文件上传攻防解析(转载)

    Web攻防系列教程之文件上传攻防解析: 文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题.但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题.本文将对文件上传的 ...

  9. Makefile 编译动态库文件及链接动态库

    本文为原创文章,转载请指明该文链接 文件目录结构如下 dynamiclibapp.c Makefile comm/inc/apue.h comm/errorhandle.c dynamiclib/Ma ...

  10. @RequestMapping @ResponseBody 和 @RequestBody 注解的用法与区别

    1.@RequestMapping 国际惯例先介绍什么是@RequestMapping,@RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应 ...