光线投射(raycast)公式中的far-clip
光线投射公式
从相机发出一条光线,穿过与相机距离为1的屏幕上的某点,已知相机参数和点在屏幕空间上的位置(sx,sy)
,计算世界空间中投影到该点的对应点的坐标的公式为:
\[
P = ViewMat^{-1} * ProjMat^{-1} * ((sx, sy, 1, 1) * farClip).
\]
使用下面的公式得到的结果实际上是一样的。 \[ P = ViewMat^{-1} * ProjMat^{-1} * (sx, sy, 1, 1), \] 但是需要对向量进行规范化,即:
\[ P=[p.x,p.y,p.z,p.w]/p.w. \]
证明
投影公式ProjMat
为: \[
P=\begin{bmatrix}
\frac{1}{A\tan(FOV/2)} & 0 & 0 & 0 \\
0 & \frac{1}{\tan(FOV/2)} & 0 & 0 \\
0 & 0 & \frac{f}{f-n} & \frac{-fn}{f-n}\\
0 & 0 & 1 & 0
\end{bmatrix}.
\]
因为ViewMat
是齐次矩阵,不修改w,所以只考虑投影公式。首先计算它的逆。
因为左下角与右上角为0,可以视为\(2\times
2\)的分块矩阵,计算逆时只需要计算右下角的逆即可。左上角为对角矩阵,逆是对角元素的倒数。2维矩阵较小,可以用伴随矩阵计算逆。伴随矩阵公式为:
\[
adj({ {\begin{bmatrix}{a}&{b}\\{c}&{d}\end{bmatrix} }})={
{\begin{bmatrix}\,\,\,{d}&\!\!{-b}\\{-c}&{a}\end{bmatrix} }}.
\] 关于n×n矩阵A,有 \[
\mathbf{A}\, \mathrm{adj}(\mathbf{A}) = \mathrm{adj}(\mathbf{A})\,
\mathbf{A} = \det(\mathbf{A})\, \mathbf{I}.
\] 即 \[
\mathbf{A}^{-1} = \det(\mathbf{A})^{-1}\, \mathrm{adj}(\mathbf{A})
\] 令 \[
A=\begin{bmatrix}
\frac{f}{f-n} & \frac{-fn}{f-n}\\
1 & 0
\end{bmatrix}
\]
则 \[
det(A)=\frac{fn}{f-n}.\\
A^{-1}=\frac{1}{\frac{fn}{f-n} }\begin{bmatrix}
0 & \frac{fn}{f-n}\\
-1 & \frac{f}{f-n}
\end{bmatrix}=\begin{bmatrix}
0 & 1\\
-\frac{f-n}{fn} & \frac{1}{n}
\end{bmatrix}
\] 因此投影矩阵的逆为: \[
P^{-1}=\begin{bmatrix}
{A\tan(FOV/2)} & 0 & 0 & 0 \\
0 & {\tan(FOV/2)} & 0 & 0 \\
0 & 0 & 0 & 1\\
0 & 0 & -\frac{f-n}{fn} & \frac{1}{n}
\end{bmatrix}
\] 根据公式: \[
\begin{bmatrix}
sx\\ sy\\ 1\\ 1
\end{bmatrix}
\times farClip=
\begin{bmatrix}
sx\times farClip\\
sy\times farClip\\
farClip\\
farClip
\end{bmatrix},
\] \[
P^{-1}\begin{bmatrix}
sx\times farClip\\
sy\times farClip\\
farClip\\
farClip
\end{bmatrix}=
\begin{bmatrix}
{A\tan(FOV/2)}sx\times farClip\\
{\tan(FOV/2)}sy\times farClip\\
farClip\\
1
\end{bmatrix}
\] 对比直接相乘的结果: \[
P^{-1}\begin{bmatrix}
sx\\
sy\\
1\\
1
\end{bmatrix}=
\begin{bmatrix}
{A\tan(FOV/2)}sx\\
{\tan(FOV/2)}sy\\
1\\
1
\end{bmatrix}=
\begin{bmatrix}
{A\tan(FOV/2)}sx\\
{\tan(FOV/2)}sy\\
1\\
\frac{1}{f}
\end{bmatrix}
\]
可以看出,无论乘不乘farClip
规范化后的结果确实是一样的。但是因为glm并没有提供我们需要的规范化的函数,所以通过乘farClip
来省去手动规范化的步骤是可以提高效率的。
光线投射(raycast)公式中的far-clip