··

Games101 作业1 - 光栅化

Tip

通过本文,你将了解:

  • MVP变换的基本实现(作业1)
Note

Ubuntu安装OpenCV教程将在后续给出。

前言

最近在备战复试项目,感觉图形学和我比较契合,遂努力速通GAMES101,最好能把202也学一些吧。

作业1

作业要求实现get_model_matrixget_projection_matrix,即实现Model矩阵与Projection矩阵。

Model矩阵实现

Model矩阵实现较为简单,直接给出:

Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
model << std::cos(rotation_angle/180*std::acos(-1)), -std::sin(rotation_angle/180*std::acos(-1)), 0, 0,
std::sin(rotation_angle/180*std::acos(-1)), std::cos(rotation_angle/180*std::acos(-1)), 0,0,
0,0,1,0,
0,0,0,1;
return model;
}
Tip

需注意,对于角度计算,我们需要将rotation_angle从弧度制转化为角度制rotation_angle/180*std::acos(-1)。其中std::acos(-1)=π\pi

Projection矩阵实现

在实现Projection矩阵之前,我们先回顾一下在进行Projection有关知识点。

Projection有两种,分别为正交投影(orthogonal projection)透视投影(prespective projection)。在实际投影过程中,如果是透视投影我们会先通过一个矩阵Mpresp.ortho.M_{presp.\longrightarrow ortho.}将透视投影变化为正交投影再按正交投影处理。

先从一般的正交投影开始讲起,它可以分为平移与缩放两步,Mortho.=SorthoTorthoM_{\text{ortho.}}=S_{\text{ortho}}\cdot T_{\text{ortho}},其中有:

Sortho=[2/(rl)2/(tb)2/(nf)1]S_{\text{ortho}} = \begin{bmatrix} 2/(r-l) & & & \\ & 2/(t-b) & & \\ & & 2/(n-f) & \\ & & & 1 \end{bmatrix} T_ortho=[1(l+r)21(t+b)21(n+f)21][10101(n+f)21](相机在原点)T\_{\text{ortho}} = \begin{bmatrix} 1 & & & -\frac{(l+r)}{2} \\ & 1 & & -\frac{(t+b)}{2}\\ & & 1 & -\frac{(n+f)}{2}\\ & & & 1 \end{bmatrix} \sim \begin{bmatrix} 1 & & & 0 \\ & 1 & & 0\\ & & 1 & -\frac{(n+f)}{2}\\ & & & 1 \end{bmatrix}{\small (相机在原点)}

需要注意的是,我们要先做平移再做放缩。


接下来是透视投影部分。上文已经讲过,对于透视投影,我们只需要将其转化为正交投影即可,因此,我们只需在Mortho.M_{\text{ortho.}}右乘一个变换矩阵Mpresp.ortho.M_{\text{presp.} \to \text{ortho.}}即可。具体推导看视频。

Mpresp.ortho.=[znear0000znear0000znear+zfarznear\*z_far0010](右手系)M*{\text{presp.} \to \text{ortho.}} = \begin{bmatrix} z*{\text{near}} & 0 & 0 & 0 \\ 0 & z*{\text{near}} & 0 & 0 \\ 0 & 0 & z*{\text{near}} + z*{\text{far}} & -z*{\text{near}} \* z\_{\text{far}} \\\\ 0 & 0 & 1 & 0 \end{bmatrix}{\small (右手系)}
Warning

在OpenGL中,默认使用左手系,此时需将其改为如下形式:

Mpresp.ortho.=[znear0000znear0000znearzfarznear\*z_far0010](左手系)M*{\text{presp.} \to \text{ortho.}} = \begin{bmatrix} z*{\text{near}} & 0 & 0 & 0 \\ 0 & z*{\text{near}} & 0 & 0 \\ 0 & 0 & -z*{\text{near}} - z*{\text{far}} & z*{\text{near}} \* z\_{\text{far}} \\ 0 & 0 & -1 & 0 \end{bmatrix}{\small (左手系)}

作业2中我们很快就将看到。

故,我们可以得到代码如下:

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.
Eigen::Matrix4f M_proj_to_ortho, M_ortho, S_ortho, T_ortho;
M_proj_to_ortho <<
zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear+zFar, -zNear*zFar,
0,0,1,0;
float t,r;
t = std::abs(zNear * std::tan(eye_fov/180*std::acos(-1) / 2.0f));
float b = -t;
r = t * aspect_ratio;
float l = -r;
// S_ortho << 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1;
S_ortho <<
2.0f/(r-l),0,0,0,
0,2.0f/(t-b),0,0,
0,0,2.0f/(zNear-zFar),0,
0,0,0,1;
T_ortho <<
1,0,0,-(r+l)/2,
0,1,0,-(t+b)/2,
0,0,1,-(zNear+zFar)/2.0f,
0,0,0,1;
M_ortho = S_ortho * T_ortho;
projection = M_ortho * M_proj_to_ortho;
return projection;
}

对应完整公式:

Mproj.=Mortho.Mpresp.ortho.=Sortho.Tortho.Mpresp.ortho. M*{\text{{proj.}}} = M*{\text{ortho.}} \cdot M*{\text{presp.} \to \text{ortho.}} = S*{\text{ortho.}} \cdot T*{\text{ortho.}} \cdot M*{\text{presp.} \to \text{ortho.}}
Note

插一句题外话,课程中的思考“z是变近还是变远”,其答案是变远。

我们假设z距离不变,并计算实际的坐标,有:

Passume=[zxzyz2z],Preal=[znearxznearyz(znear+zfar)z_nearz_farz]P*{\text {assume}} = \begin{bmatrix} zx \\ zy \\ z^2 \\ z \end{bmatrix}, P*{\text {real}} = \begin{bmatrix} z*{near}x \\ z*{near}y \\ z*(z*{near}+z*{far})-z\_{near}*z\_{far} \\ z \end{bmatrix}

对assume和real的z坐标进行联立,可得z1=znear,z2=zfarz_1=z_{\text{near}}, z_2=z_{\text{far}},在区间中,实际坐标大于假设(不变)。

作业1提高项部分过于简单,故不在此提供。

结语

作业1属于热身性质,别忘了概念就行。