在此记录自己学习投影矩阵中遇到的问题和想法。
最好请查阅关于opengl与d3d中投影矩阵推导的相关博客,再看此篇。(主要是记录给自己看,帮助理解,以供以后修正优化,举一反三之用,所以会有晦涩或者不详细之处,请谅解,如果能帮助到正好需要的小伙伴那就更好了,自己也是图形学的初学者,欢迎交流和指正)。
D3D中 投影矩阵的推导:http://blog.csdn.net/zhanghua1816/article/details/23121735
OpenGL学习脚印: 投影矩阵的推导 : http://blog.csdn.net/pizi0475/article/details/46794263
两者都是关于投影矩阵的推导思路和相关公式,区别是一个是opengl版本一个是d3d版本,具体差别是两者的默认坐标系(前者中的眼坐标系)不同(opengl是右手坐标系,d3d是左手坐标系),归一化设备坐标系(两者都是右手坐标系,所以数值中带负号的一般是opengl的投影矩阵,不带的就是D3D(其实这么说,很不完善。),两篇文章中都用列矩阵来表述投影矩阵,这点要注意)大小不同(主要是Z值的范围不同,opgl版本中称为 NDC(normalized device coordination),d3d版本中称为 规范视域体(canonical view volume)),所以就导致最后的成像投影矩阵略有差别(原理和过程其实相似,表述的差别而已)。
对了,插一个概念,由于投影变换,是与摄像头的方位都有关系的(设想一个旋转或者平移的摄像机,它所呈献的画面肯定是变化的),而这与投影矩阵没有关系,因为投影矩阵把这些条件先确定下来(比如始终位于原点,方向始终是Z轴平行(opengl中是Z轴负方向,d3d中是Z轴正方向))。而比如摄像机的方位对于最后成像的影响,在此投影矩阵得出后,再叉乘该摄像机方位矩阵,就可以了。(其实就是成像先后的关系和矩阵的相互关系思考清楚就可以了,投影矩阵是确定视椎体后的固有属性,与摄像机的方位无关,与成像有关。又可以说投影矩阵其实就是确认视椎体的形状,大小与NDC形状之间的对应关系而已。)
但是这边特地给出两个版本的链接,并不是要画蛇添足的介绍两次。其实是两者的推导略有不同。或者详略各有差异,结合来看才能更清楚,投影矩阵的原理和推导的过程。
推荐先看D3D版本的推导,因为其中先介绍了正交投影(Orthographic Projection)的推导过程及原理。正交投影就是将裁剪后的视域(称为view volume)坐标转换到NDC中,就是两个变换——平移(使得原点重合)和缩放(使得大小相同),由于是平移和缩放,变换顺序可以交换(矩阵叉乘的顺序是不能随便变换的,此处特殊(由矩阵叉乘的特性来决定的,详细的解释可以查阅相关资料))。
投影矩阵的推导原理,这边也要解释下这两篇文章没有详细说明的地方,就是通过射线投影的原理得到近界面的对应点,但是此近截面上的点并不是最后NDC中的最后目标点,但是可以根据此点的x,y坐标得到最后目标点的x,y坐标与原始x与y的关系,但是因为近截面的z值始终是n,也就是舍弃了沿着投影原点射出的射线上的Z值,要理清这个概念,那z值的对应关系怎么确认呢,就是通过齐次变化中的w值,来做减维处理(具体看opengl那篇文章,说的明白些,推荐结合3d基础数学:图形与游戏开发中的投影变换来理解)。(再插一句,就是要理解任何一个维度的点,都可以用更高维度的形式去表示,简单来说比如二维空间中的A点(x,y)到三维空间中就可以表示(x,y,1),可以理解为投射到z=1平面上的点((x,y,1)投影到z=1平面不就是它本身么?扩展开来说(x*z,y*z,z)这个集合里的点投影到z=1的平面上时,都与(x,y,1)重合,也就可以说是同一点。所以以此来理解4维甚至更高维度的现象(其实,我也很难想象更高维度的具体表现,我觉得是无法用这种3维坐标系表示图去展示4维甚至更高维投影到低维的表现,但是可以从数学层面去推。)同理,3维空间中的(x,y,z)点就是4维空间中的(x,y,z,1)点,也就是和(x*w,y*w,z*w,w)点关于w=1的平面上的投影点重合(其实这么说不对,应该说是w=1的3d坐标系重合)。这也是下面推导的思考基础。))
通过相似三角形原理和正交投影的公式和推导过程我们知道了,那怎么消除z值呢?怎么得到z'与z的关系呢?(这里暂时都用d3d公式)
貌似想不出来,只能按照文章中的方法,试图去求z'z的值,然后通过(x'z,y'z,z'z,z)投影到w = 1 的3d坐标系中不就是(x',y',z',1)该点么。该投影过程就是降维过程,也就是齐次变换。
那现在的问题就是怎么求得(x,y,z,1)到(x'z,y'z,z'z,z)的变换矩阵了。这就是透视投影矩阵~!
然后就是文章中的,根据先求的这个4x4矩阵的第一行,第二行(列向量),也就是[2n/(r-l),0,-(r+l)/(r-l),0]和[2n/(t-b),0,-(t+b)/(t-b),0].
然后第四行的[a4,b4,c4,d4]*[x,y,z,1] = z,因此第四行为[0,0,1,0]
那么只剩第三行[a3,b3,c3,d3]求值了。a3*x+b3*y+c3*z+d3 = z'z;其实x,y的值不能影响到z'与z的对应关系,所以其对应系数就应该是0,所以变成 c3*z+d3 = z'z,转成z‘=c3+d3/z;发现两者原来是倒数关系。(这也是z-fighting的原因,当然了只要精度足够大就没有z-fighting问题,但是问题就是浮点数的精度是有限制的。)然后带入两对已知值,z’=NDC中的最小值时,z也是近界面上的值,即z'=0,z=n。z’=NDC的最大值时,z也是远截面上的值,即z‘=1,z=f;
求得 第三行就是[0,0,f/(f-n),-fn/(f-n)]
因此
别忘了将结果做其次变换后得到(x’,y',z',1);