视图变换
在opengl中,视图变换的输入是:
(1)眼睛位置(或者说相机位置)eys;
(2)眼睛朝向的中心center,(就是眼睛朝哪里看);
(3)头的方向up。
任何一点经过视图变换后都会转化到眼睛坐标系下。具体地说,眼睛坐标系的三个轴分别是:
(1)z轴: F=center-eye;(要归一化)
(2)x轴: S=cross(F,up);(这里是叉乘,也要归一化)
(3)y轴: U=cross(S,F)。
此时,eye的位置就是原点了。那么对于任意一点P(px,py,pz),在新坐标下的三个点分别是:
px=dot(p-eye,S);(这里是点乘)
py=dot(p-eye,U);
pz=dot(p-eye,F)
所以可得变换矩阵为:
不过在opengl的坐标系下。z轴的方向其实是垂直屏幕向外的,所以与我们上面的F是相反的。因此实际的变换矩阵是下面这样子:
这样的变换矩阵,其实对于真正能看到的点,变换后它的z坐标应该是一个负的值。
透视投影
透视投影的输入为
(1)宽度width
(2)高度height
(3)近裁面near
(4)远裁面far。
经过透视投影变换后的点的坐标,xyz都在[-1,1]。(不在这个范围内的将被opengl裁剪掉)。而且这里输入的点一般是经过视图变换的点,因此点的z坐标是个负值。
如上图所示,设一个点的坐标是(x,y,z),变换之后的坐标为$(x_{proj},y_{proj},z_{near})$。那么由三角形相似可得:
$frac{z_{near}}{-z}=frac{y_{proj}}{y}=frac{x_{proj}}{x}$
所以
$y_{proj}=frac{z_{near}cdot y}{-z}$
$x_{proj}=frac{z_{near}cdot x}{-z}$
然后将$y_{proj},x_{proj}$变换到[-1,1]之间,即$y_{proj}=frac{z_{near}cdot y}{-zcdot width/2}$,$x_{proj}=frac{z_{near}cdot x}{-zcdot height/2}$。
下面我们来讨论变换后的z坐标。由于在远裁面将被映射到1,近裁面将被映射到-1,设z的变换公式为$f(z)=frac{Az+B}{-z}$,$-z$的意思是最后的齐次坐标是$-z$,归一化的时候用的是$-z$。那么有:$left{egin{matrix}frac{A(-cdot z_{near})+B}{-(-z_{near})}=-1\ frac{A(-cdot z_{far})+B}{-(-z_{far})}=1end{matrix}
ight.$
解得:$left{egin{matrix}A=-frac{z_{near}+z_{far}}{z_{far}-z_{far}}\ B=-frac{2z_{near}z_{far}}{z_{far}-z_{near}}end{matrix}
ight.$
令$X=frac{z_{near}}{width/2} $,$Y=frac{z_{near}}{height/2} $最后得到变换矩阵为:
$egin{bmatrix}X & 0 & 0 & 0 \ 0 & Y & 0 & 0 \ 0 & 0 & A & -1\ 0 & 0 & B & 0end{bmatrix}$
另外,有时候计算透视投影时输入是长宽比aspect和视野的角度Fov(y轴的角度)。这里可以想办法计算出width和height的表达式,带入上面的变换矩阵。过程略去了。最后计算的$X=frac{1}{aspectcdot tan(frac{Fov}{2})}$,$Y=frac{1}{tan(frac{Fov}{2})}$
正交投影
正交投影的输入为left,right,top,bottom,far,near。
对于x方向来说,设插值方程为$fx(x)=Ax+B$,那么有:$left{egin{matrix}A cdot left +B=-1\ A cdot right +B=1end{matrix}
ight.$,解得:$left{egin{matrix}A=frac{2}{right-left}\ B=-frac{right+left}{right-left}end{matrix}
ight.$
y方向同理。
对于z方向来说,$fz(-near)=-1,fz(-far)=1$。
最后得到变换矩阵为:
$egin{bmatrix}Ax & 0 & 0 &0 \ 0 & Ay & 0 &0 \ 0 & 0 & Az & 0\ Bx & By& Bz &1 end{bmatrix}$
其中
$Ax=frac{2}{right-left}$
$Bx=-frac{right+left}{right-left}$
$Ay=frac{2}{top-bottom}$
$By=-frac{top+bottom}{top-bottom}$
$Az=-frac{2}{far-near}$
$Bz=-frac{far+near}{far-near}$