Featured image of post Games 101 Assignment 1

Games101 作业1 - 光栅化

Tip

通过本文,你将了解:

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

Note

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

前言

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

作业1

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

Model矩阵实现

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

1
2
3
4
5
6
7
8
9
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)。在实际投影过程中,如果是透视投影我们会先通过一个矩阵$M_{presp.\longrightarrow ortho.}$将透视投影变化为正交投影再按正交投影处理。

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

$$ S_{\text{ortho}} = \begin{bmatrix} 2/(r-l) & & & \\ & 2/(t-b) & & \\ & & 2/(n-f) & \\ & & & 1 \end{bmatrix} $$

$$ 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 (相机在原点)} $$

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


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

$$ 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中,默认使用左手系,此时需将其改为如下形式:

$$ 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中我们很快就将看到。

故,我们可以得到代码如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
}

对应完整公式:

$$ 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距离不变,并计算实际的坐标,有: $$ 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坐标进行联立,可得$z_1=z_{\text{near}}, z_2=z_{\text{far}}$,在区间中,实际坐标大于假设(不变)。

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

结语

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