[3D基础]投影矩阵的推导(1)
转眼我做游戏行业已经八个月了,游戏行业入门门槛低,所以还算学习得比较轻松,总结了当初自己迷惑的几个知识点,本来想写出来给初学者解惑,无赖我是一个懒散的人,一直拖到现在,终于决心白纸黑字的搬到Blog上来,希望大家喜欢。
投影变换:我觉得这个是3D到2D变换中最让初学者头晕的问题,但又是最重要的。
请看上面这张我用爪子抓出来的图。这个坐标系是DX的左手坐标系,Y向上,X向右,Z向内,几何坐标已经经过了相机坐标系的变换,相机位置为(0,0,0),假设远裁减面距离为f,近裁减面距离为n,近裁减面左边为l,右边为r,上为t,下为b。要投影的2个顶点A和B坐标分别为A(Xa,Ya,Za),B (Xb,Yb,Zb),现在我们要求他们的投影点坐标A0和B0,这里我以B点为例子,讲解投影最简单的几何知识。
首先我们来计算B0点的X坐标,(虚线都是辅助线),我们看三角形B C O和三角形B0 K O,利用初中几何知识可得他们是相似的,由相似三角形定理可知,B0 K = B C * ( n / C O );而B C等于Xb, C O等于Zb;所以Xb0 = Xb * n / Zb; 同理Yb0 = Yb * n / Zb;我们知道DX把3D坐标映射到了一个X(-1,1) Y(-1,1) Z(0,1)的一个立方体内。那现在我们就需要对投影后的坐标进行一维映射了,(其实投影就是映射,数学形式为函数,将一个值域一一映射到另外一个值域),现在在X轴上,我们要把l - r这个值域中的值一一映射到-1 - 1之间,what should we do?很显然我们有一个等式 ( x - l ) / ( r - l ) = ( x0 - ( -1) ) / ( 1 - ( -1 )); x是l - r之间的一个值,x0是-1 - 1之间的一个值,我们得到 x0 = (2x - ( r + l )) / ( r - l );这里的x换成Xb0得:x0 = (2n * Xb) / (Zb*( r - l )) - ( r + l )/( r - l );同理:y0 = (2n * Yb) / (Zb*( t - b )) - ( t + b )/( t - b ); Zb映射最简单,将n - f之间的值映射到0 - 1之间, z0 = Zb / ( f - n ) - n / ( f- n );现在我们把这些四则运算用矩阵形式来表示:
[x, y, z, 1] * [ 2n/(r-l) , 0 , 0 , 0 ]
[ 0, , 2n/(t-b) , 0 , 0 ]
[ -(r+l)/(r-l), -(t+b)/(t-b), z/(f-n) , 1 ]
[ 0, , 0 , -z*n/(f-n), 0 ]
得到的结构应该是[x0, y0, z0, w]->[x0/w, y0/w, z0/w, 1]
但为了3D引擎后期光栅化时方便的对中间象素的Z值进行线性插值,最好直接保存顶点的1/Z值,然后将1/Z嵌位到0~1之间。
[x, y, z, 1] * [ 2n/(r-l) , 0 , 0 , 0 ]
[ 0, , 2n/(t-b) , 0 , 0 ]
[ -(r+l)/(r-l), -(t+b)/(t-b), 0 , 1 ]
[ 0, , 0 , 1 , 0 ] //直接保存1/Z
将1/Z嵌位到0~1之间(线形映射) z0 = (1/Bz - 1/n)/(1/f - 1/n); [1/n->0, 1/f->1]
转换先形式使矩阵理解更加明了:z0 = f/(f-n) - f*n/((f-n)*z)
[x, y, z, 1] * [ 2n/(r-l) , 0 , 0 , 0 ]
[ 0, , 2n/(t-b) , 0 , 0 ]
[ -(r+l)/(r-l), -(t+b)/(t-b), f/(f-n) , 1 ]
[ 0, , 0 , -f*n/(f-n) , 0 ]
我看到实时计算机图形学上写出的答案与我推导出来的有点不一样,红色部分符号相反,颇为不解,不知道我错在哪里,希望明白的朋友指点迷津。