【光能蜗牛的图形学之旅】OpenGL顶点变换全体流程以及投影矩阵
1.整体流程图
20140918202127843.jpg2.表格说明
备注> | 坐标系 | 变换 | <备注 |
---|---|---|---|
本地坐标系 | |||
模型变换 | |||
世界坐标系 | |||
视图变换 | 通常来说 视图变换所使用的 变换矩阵是摄像机矩阵 的逆矩阵,简单理解一下 ,比如摄像机往z正方向 移动,相当于物体本身 往负方向运动, 所以本质上来说,其实和 模型变换矩阵殊途同归 ,这很关键,别忽略 |
||
也就是摄像机角度 位置所在的坐标系 |
眼坐标系 (照相机坐标系) |
||
投影变换 | 一般分为 正交投影和透视投影 |
||
也就是说, 经过投影矩阵变换 后的坐标点,形如 (xz',yz',zz',wz') 的未经过齐次除法 的四维齐次坐标 关于齐次裁剪的详细介绍 |
齐次裁剪坐标系 | ||
透视除法 | 指将透视投影得到的 齐次坐标转化为三维坐标, 即除以齐次分量, 经过透视除法的点才是 在规范化裁剪立方体中 的点。 规范化裁剪立方体即cvv (canonical view volume) volume 体积; 卷; 音量; 量,大量; 关于透视除法的详细介绍, 经过这个除法之后的坐标系 转换到NDC坐标系 ,即范围[-1,1]的点 进入NDC坐标, 其他的点被裁剪 |
||
经过透视除法之后的坐标系 | 规范化设备坐标系 NDC坐标系 (Normalized Device Coordinates) |
||
视口变换 | 简单的说就是将cvv 中[-1,1]区域变换 到屏幕实际的 (x,y,width,height) 详细的过程请查看下面的连接 directx3d下的变化简介 opengl同理 |
||
这个不用说太多了吧, 其实就是你手上设备 的实际分辨率所使用的坐标系 |
屏幕坐标系 |
3.个人推导总结
上面的表格还请仔细阅读,尤其是齐次裁剪坐标系那一部分。
因为模型矩阵,视图矩阵相对来说都比较直观,可以说稍微看一下图形学变换就能够总结出来。
这里呢,主要讨论正交投影和透视投影的问题
参考文档在这里和这里
为了方便性,这里使用左手坐标系来推导
-
正交投影推导
20140407181049609.jpg
结合图,我们知道,正交投影其实就是将目标视域体[L,B,N]至[R,T,F]的区域
度量到规则视域体[-1,-1,0]至[1,1,1]的区域中,请仔细理解这句话,这意味着,我可以将很大的一个区域映射到的规则视域体cvv中,也可以将很小的区域映射到使视域体中,而这个区域的具体的大小是由L,B,N 至R,T,F的区域大小决定的。
下面是推导过程,假设有一个点(x,y,z)位于视域体[L,B,N]至[R,T,F]的区域中 ,那么x,y,z 分别满足如下不等式
同样,结合图形我们可以知道透视投影本质上是对视锥体的映射到cvv盒子的过程。
我们回想前面讲的正交投影,想一想透视投影会有什么不同。。
透视投影实际上是对现实世界近大远小的模拟,如果观察铁轨,你会发现两条平行轨道最终交汇于一点。即灭点。
但是真正推导这个矩阵你会发现并不是那么的符合你的常规认知。
怎样的推导会更加合情合理又接地气,这个地方我想了很久,借助一下前面正交投影的推导,我想到一种方式来拆分这个从视锥体到cvv的映射过程,
1.首先将视锥体转化为正交投影的vv体,注意下图中的蓝色盒子区域,
QQ截图20171201110233.png
2.接下来执行和正交投影一模一样的操作,即vv到cvv。
QQ截图20171201111933.png
这么一拆分是不是清晰明白了很多呢有木有。
接下来是验证过程,
首先是计算步骤1
观察一下这个视锥体
我们换个x0z切面视角 QQ截图20171201114647.png
要将视锥体转换成规则视域体,由上图根据相似三角形规则,我们有
整理得
同理可得y的关系式为
那z呢,这是最让人迷惑不解的地方,观察图像我们发现z0到z是一比一的映射关系,
我们轻易的得出
但当你试着用这三个等式尝试写成矩阵,然而,等等,好像哪里不对。
我们知道,线性代数的性质决定了我们的矩阵处理的均是线性变换,
在这个变换中x和1/z0是线性关系,y和1/z0是线性关系,既然x,y,z处于同一地位,那么我们必须让z和z0的线性关系转换为z和1/z0的关系,这样我们才能写出x,y,z的变换矩阵,于是我们假定如下关系式
这个关系式有两个限制条件帮助我们求出a,b的值。
1.当z0 = N时,z = N;
2.当z0 = F时,z = F;
将这两个条件代入
求出
也就是说
我们将三个式子以及w写在一起
左右同时乘以z0
写成矩阵形式
上述矩阵表明,视锥体内任意点的
经过左乘矩阵
之后会变成齐次坐标空间内的坐标
这个坐标分别除以齐次项z0就能得到规则视域体的点 .
请注意,这里仅仅是视锥体到规则视域体,并不是直接就到了cvv盒子体内,如果没看懂,请往回翻
以上是第一步骤部分,
那么接下来就简单了,
2.第二步骤,即vv到cvv的正交投影过程,当得到这个规则视域体中的点(x,y,z,1)之后,我们只需要对规则视域体进行正交投影,就能变成最终我们需要的标准cvv体
我们可以很简单的得到这个矩阵
使用
左乘正交投影矩阵,还记得吧,就是这个
(忘记的请往回翻)即
这便是最终的投影矩阵,从上述过程也可以看出投影矩阵本质上依然包含了正交投影部分的过程。有没有一种戛然而止的感觉,这便是透视投影矩阵的本质,即先进行视锥体到规则视域体的变换,然后规则视域体进行正交投影变换
需要强调的是,视锥体内的点(x0,y0,z0,1)经过投影矩阵变化后的点是在齐次裁剪空间的点
(x.z0,y.z0,z.z0,z0),如果需要真实的展示在cvv中,则需要先进行透视除法,即,各项除以z0,才能得到cvv中的(x,y,z,1),请结合前面的表格查看这一部分,就不多解释了。
相关问题说明,为何你发现很多地方的看到的投影矩阵有些微的差别,那么我分析主要有三个原因
- 上述推导使用的dirextx左手坐标系,所以你可能会发现N,F符号均是正的,那么如果你想在opengl使用,只需要将N改成-N,同时F写成-F,其他并没有什么变化
- 这里的cvv体是[-1,-1,0][1,1,1]这个区间内。而其他地方可能使用的是[-1,-1,-1][1,1,1]作为cvv盒子区间
- 很多矩阵你会发现,R+L部分写成0,T+B部分写成了0,那是因为他们默认R=-L,以及T=-B写成的简略模式所致,其实真实情况未必如此,R不一定非要等于-L,且T也不一定非要等于-B,那么在这里我还是更喜欢保留T+B,R+L等等,因为这才是投影矩阵本来的样子。
后记,我本身其实不太喜欢透视投影的z变换过程中的奇怪观感,这种奇怪的感觉我至今没有消失,因为似乎不是我想象中的那么完美,这有两个原因,一是我本身对数学的理解不到位,二是数学形式之所以这样存在,可能只是为了解决特定问题而采用的特定方式。
不管如何,我在想,是不是有什么更加直白暴力的美学方式呢,这提醒我继续前行。
以上~~