原文链接:【GAMES101】透视投影
GAMES101作业1中编程实现一个三角形旋转投影的任务,期间配置环境、找Bug费了好几天的功夫。本期,推送一些注意的地方。
How to do perspective projection
First “squish” the frustum into a cuboid n → n , f → f n \rightarrow n, f \rightarrow f n→n,f→f ( M p e r s p → o r t h o M_{persp\rightarrow ortho} Mpersp→ortho)
Do orthographic projection ( M o r t h o M_{ortho} Mortho, already known!)
就是将Frustum拉成一个长方体,然后再利用正交投影进行变换。
注意:在右手系中,相机正对 − z -z −z方向。这是一个坑,在编程时,应该 n = − z N e a r n = -zNear n=−zNear。
由 △ O A C ∼ △ O B D \triangle OAC \sim \triangle OBD △OAC∼△OBD,有
{ y ′ = n z y x ′ = n z x 。 \left\{ \begin{aligned} y' = \frac{n}{z}y\\ x' = \frac{n}{z}x \end{aligned}\right.。 ⎩⎪⎨⎪⎧y′=znyx′=znx。
在齐次坐标系中,,有
( x y z 1 ) ⟹ ( n z x n z y unknown 1 ) ⟹ × z ( n x n y still unknown z ) \left ( \begin{matrix} x \\ y \\ z \\ 1 \end{matrix} \right ) \Longrightarrow \left ( \begin{matrix} \frac{n}{z}x \\ \frac{n}{z}y \\ \text{unknown} \\ 1 \end{matrix} \right ) \Longrightarrow^{\times z} \left ( \begin{matrix} nx \\ ny \\ \text{still unknown} \\ z \end{matrix} \right ) ⎝⎜⎜⎛xyz1⎠⎟⎟⎞⟹⎝⎜⎜⎛znxznyunknown1⎠⎟⎟⎞⟹×z⎝⎜⎜⎛nxnystill unknownz⎠⎟⎟⎞
因此
KaTeX parse error: No such environment: equation* at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲*̲}̲M_{persp\righta…
M p e r s p → o r t h o M_{persp\rightarrow ortho} Mpersp→ortho如下
M p e r s p → o r t h o = ( n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ) M_{persp\rightarrow ortho} = \left ( \begin{matrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{matrix}\right ) Mpersp→ortho=⎝⎜⎜⎛n0?00n?000?100?0⎠⎟⎟⎞
Observation: the third row is responsible for z ’ z’ z’
- Any point on the near plane will not change
- Any point’s z z z on the far plane will not change
M p e r s p → o r t h o ( x y n 1 ) = ( x y n 1 ) M_{persp\rightarrow ortho} \left ( \begin{matrix}x \\ y \\ n \\ 1\end{matrix}\right ) = \left ( \begin{matrix}x \\ y \\ n \\ 1\end{matrix}\right ) Mpersp→ortho⎝⎜⎜⎛xyn1⎠⎟⎟⎞=⎝⎜⎜⎛xyn1⎠⎟⎟⎞
所以 M p e r s p → o r t h o M_{persp\rightarrow ortho} Mpersp→ortho第三行为 ( 0 , 0 , A , B ) (0, 0, A, B) (0,0,A,B)
M p e r s p → o r t h o ( 0 0 f 1 ) = ( 0 0 f 1 ) M_{persp\rightarrow ortho}\left ( \begin{matrix}0 \\ 0 \\ f \\ 1\end{matrix}\right ) = \left ( \begin{matrix}0 \\ 0 \\ f \\ 1\end{matrix}\right ) Mpersp→ortho⎝⎜⎜⎛00f1⎠⎟⎟⎞=⎝⎜⎜⎛00f1⎠⎟⎟⎞
综上
{ A n 2 + B = n A f 2 + B = f 。 \left\{ \begin{aligned}A n^2 + B = n\\A f^2 + B = f\end{aligned}\right.。 {An2+B=nAf2+B=f。
解得
{ A = n + f B = − n × f 。 \left\{ \begin{aligned}A &= n + f\\ B &= - n \times f\end{aligned}\right.。 {AB=n+f=−n×f。
In general
- We want to map a cuboid [ l , r ] × [ b , t ] × [ f , n ] [l, r] \times [b, t] \times [f, n] [l,r]×[b,t]×[f,n] to the canonical cube [ − 1 , 1 ] 3 [-1,1]^3 [−1,1]3.
需要两部操作:
因此
M o r t h o = ( 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f ) 0 0 0 0 1 ) ( 0 0 0 − r + l 2 0 0 0 − t + b 2 0 0 0 − n + f 2 0 0 0 1 ) M_{ortho} = \left ( \begin{matrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & \frac{2}{n - f}) & 0\\ 0 & 0 & 0 & 1\end{matrix}\right ) \left ( \begin{matrix} 0 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 0 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 0 & -\frac{n + f}{2} \\ 0 & 0 & 0 & 1\end{matrix}\right ) Mortho=⎝⎜⎜⎛r−l20000t−b20000n−f2)00001⎠⎟⎟⎞⎝⎜⎜⎛000000000000−2r+l−2t+b−2n+f1⎠⎟⎟⎞
Filed of View如下
How to convert from f o v Y fovY fovY and aspect to l , r , b , t l, r, b, t l,r,b,t?
易得
{ t = tan ( f o v Y 2 ) ∣ n ∣ , b = − t r = a s p e c t × t , l = − r 。 \left\{ \begin{aligned}t &= \tan(\frac{fovY}{2}) | n |, &~b = -t\\ r &= aspect \times t, ~ &l = -r\end{aligned}\right.。 ⎩⎨⎧tr=tan(2fovY)∣n∣,=aspect×t, b=−tl=−r。
综上所述,透视投影矩阵
M p e r s p = M o r t h o × M p e r s p → o r t h o = ( 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f ) 0 0 0 0 1 ) ( 0 0 0 − r + l 2 0 0 0 − t + b 2 0 0 0 − n + f 2 0 0 0 1 ) ( n 0 0 0 0 n 0 0 0 0 n + f − n × f 0 0 1 0 ) \begin{aligned}M_{persp}=&M_{ortho} \times M_{persp\rightarrow ortho} \\=&\left ( \begin{matrix} \frac{2}{r - l} & 0 & 0 & 0 \\ 0 & \frac{2}{t - b} & 0 & 0 \\ 0 & 0 & \frac{2}{n - f}) & 0\\ 0 & 0 & 0 & 1\end{matrix}\right ) \\&\left ( \begin{matrix} 0 & 0 & 0 & -\frac{r + l}{2} \\ 0 & 0 & 0 & -\frac{t + b}{2} \\ 0 & 0 & 0 & -\frac{n + f}{2} \\ 0 & 0 & 0 & 1\end{matrix}\right ) \\&\left ( \begin{matrix}n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n + f & - n \times f \\ 0 & 0 & 1 & 0\end{matrix}\right )\end{aligned} Mpersp==Mortho×Mpersp→ortho⎝⎜⎜⎛r−l20000t−b20000n−f2)00001⎠⎟⎟⎞⎝⎜⎜⎛000000000000−2r+l−2t+b−2n+f1⎠⎟⎟⎞⎝⎜⎜⎛n0000n0000n+f100−n×f0⎠⎟⎟⎞
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the model matrix for rotating the triangle around the Z axis.
// Then return it.
double rotation_radian = rotation_angle * MY_PI / 180;
model << std::cos(rotation_radian), -std::sin(rotation_radian), 0, 0,
std::sin(rotation_radian), std::cos(rotation_radian), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
return model;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
// Students will implement this function
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the projection matrix for the given parameters.
// Then return it.
float n = -zNear;
float f = -zFar;
float t = std::abs(n) * std::tan(.5f * eye_fov * MY_PI / 180);
float b = -t;
float r = aspect_ratio * t;
float l = -r;
Eigen::Matrix4f perspective_to_orthogonal = Eigen::Matrix4f::Identity();
perspective_to_orthogonal << n, 0, 0, 0,
0, n, 0, 0,
0, 0, n + f, - n * f,
0, 0, 1, 0;
Eigen::Matrix4f orthogonal_projection = Eigen::Matrix4f::Identity();
orthogonal_projection << 2.f/(r-l), 0, 0, 0,
0, 2.f/(t-b), 0, 0,
0, 0, 2.f/(n-f), 0,
0, 0, 0, 1;
projection = orthogonal_projection * perspective_to_orthogonal;
return projection;
}