透视投影矩阵的推导
上图是3D渲染过程中的空间变换过程,这里主要讨论观察空间到裁剪空间的转换。

由上图可知,经过观察变换后,空间变换为观察空间,也就是以摄像机坐标为原点的空间,这是接下来推导的前提。

下图是一个视锥体,在视锥体范围内的物体为可视的,不在视锥体内的为不可视。
透视投影矩阵的推导
我们有两个任务:
1、判断一个点是否在视锥体内(用于裁剪超出屏幕的点)。
2、计算顶点在裁剪空间上的坐标。
我们先忘掉矩阵的概念,只用基本的三维几何知识完成第一点。

先假定一个场景如下图:透视投影矩阵的推导
已知以下参数:
fov:视锥体上下两个截面的夹角,此参数由设定摄像机参数时给定了
n:近截面z轴坐标,此参数由设定摄像机参数时给定了
f:远截面z轴坐标,此参数由设定摄像机参数时给定了
aspect:屏幕宽高比,此参数由设定摄像机参数时给定了
H:平行于远截面,并且经过P点的平面的高的一半,可通过计算得出

由此,我们只需要判断P点的y轴坐标除以H是否大于1(当y为负时是否小于-1),即可知道P点是否在y轴方向上超出了视椎体。同理可求出x轴。

下面先求y方向。
H=ztan(fov/2)H=z*tan(fov/2)
y/H=y/ztan(fov/2)y/H=y/z*tan(fov/2)

接下来求x方向,假设经过此P点截面的宽度为W
W=aspectHW=aspect*H
W=aspectztan(fov/2)W=aspect*z*tan(fov/2)
x/W=x/(aspectztan(fov/2))x/W=x/(aspect*z*tan(fov/2))

最后求z方向
(zn)/(fn)(z-n)/(f-n)

我们已经获得计算点是否在视锥体内的计算方式,现在我们把它转换为矩阵的形式,设M为我们要求的转换矩阵,可得:
M[xyz1]=[xztan(fov/2)yaspectztan(fov/2)(zn)/(fn)1]M \cdot \left[\begin{matrix} x \\ y \\ z \\ 1 \end{matrix}\right] = \left[\begin{matrix} \frac{x}{z*tan(fov/2)} \\ \frac{y}{aspect*z*tan(fov/2)} \\ (z-n)/(f-n) \\ 1 \end{matrix} \right]
[m00m01m02m03m10m11m12m13m20m21m22m23m30m31m32m33][xyz1]=[xztan(fov/2)yaspectztan(fov/2)(zn)/(fn)1]\left[ \begin{matrix} m_{00} & m_{01} & m_{02} & m_{03}\\ m_{10} & m_{11} & m_{12} & m_{13} \\ m_{20} & m_{21} & m_{22} & m_{23} \\ m_{30} & m_{31} & m_{32} & m_{33} \end{matrix} \right] \cdot \left[\begin{matrix} x \\ y \\ z \\ 1 \end{matrix}\right] = \left[\begin{matrix} \frac{x}{z*tan(fov/2)} \\ \frac{y}{aspect*z*tan(fov/2)} \\ (z-n)/(f-n) \\ 1 \end{matrix} \right]

我们发现求解m00x+m01y+m02z+m03=1zaspecttan(fov/2)m_{00}*x + m_{01}*y + m_{02}*z + m_{03} = \frac{1}{z*aspect*tan(fov/2)}很难找出合适的m00m_{00}m02m_{02},因为左边x和z是以加法的形式相邻,右边z确成为了x的分母。

解决方法:将右边的齐次坐标每一项乘以z,所以有:
[m00m01m02m03m10m11m12m13m20m21m22m23m30m31m32m33][xyz1]=[xtan(fov/2)yaspecttan(fov/2)z(zn)/(fn)z]\left[ \begin{matrix} m_{00} & m_{01} & m_{02} & m_{03}\\ m_{10} & m_{11} & m_{12} & m_{13} \\ m_{20} & m_{21} & m_{22} & m_{23} \\ m_{30} & m_{31} & m_{32} & m_{33} \end{matrix} \right] \cdot \left[\begin{matrix} x \\ y \\ z \\ 1 \end{matrix}\right] = \left[\begin{matrix} \frac{x}{tan(fov/2)} \\ \frac{y}{aspect*tan(fov/2)} \\ z*(z-n)/(f-n) \\ z \end{matrix} \right]

所以可以求得矩阵为:
[1tan(fov/2)00001aspecttan(fov/2)0000m22m230010]\left[ \begin{matrix} \frac{1}{tan(fov/2)} & 0 & 0 & 0\\ 0 & \frac{1}{aspect*tan(fov/2)} & 0 & 0 \\ 0 & 0 & m_{22} & m_{23} \\ 0 & 0 & 1 & 0 \end{matrix} \right]

m22z+m23=z(zn)/(fn)m_{22}*z + m_{23} = z*(z-n)/(f-n)

我们知道,当z=nz=n时,(zn)/(fn)=1(z-n)/(f-n)=-1 所以 m22n+m23=nm_{22}*n + m_{23} = -n

z=fz=f时,当z=nz=n时,(zn)/(fn)=1(z-n)/(f-n)=1 所以 m22f+m23=fm_{22}*f + m_{23} = f

{m22n+m23=nm22f+m23=f联立 \begin{cases} m_{22}*n + m_{23} = -n \\ m_{22}*f + m_{23} = f \end{cases}

{m22=fnnfm23=2fnnf求得 \begin{cases} m_{22} = \frac{-f-n}{n-f} \\ m_{23} = \frac{2*f*n}{n-f} \end{cases}

最后求得投影矩阵为:
[1tan(fov/2)00001aspecttan(fov/2)0000fnnf2fnnf0010]\left[ \begin{matrix} \frac{1}{tan(fov/2)} & 0 & 0 & 0\\ 0 & \frac{1}{aspect*tan(fov/2)} & 0 & 0 \\ 0 & 0 & \frac{-f-n}{n-f} & \frac{2*f*n}{n-f} \\ 0 & 0 & 1 & 0 \end{matrix} \right]

注意,通过此矩阵转换后的坐标是齐次坐标,只要除以w分量,根据结果就可以判断是否在[-1,1]范围内,也就是该点是否在视椎体内。

透视投影矩阵只是为了获得一个齐次坐标,为投影做准备,实际不进行真正的投影,真正的屏幕投影是在下一个阶段执行的。

相较于https://www.cnblogs.com/bluebean/p/5276111.html,本文是用另外一种几何思路推导透视投影矩阵,可以互相参考。

相关文章: