作业描述

填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个点 v0(2.0, 0.0, −2.0), v1(0.0, 2.0, −2.0), v2(−2.0, 0.0, −2.0), 你需要将这三个点的坐标变换为屏幕坐标,并在屏幕上绘制出对应的线框三角形 。

理解rasterizer.hpp,修改 main.cpp 中的函数:

  • get_model_matrix(float rotation_angle): 逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,只需要实现三维中绕 z 轴旋转的变换矩阵, 而不用处理平移与缩放。
  • get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar): 使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵。
  • [Optional] main(): 自行补充所需的其他操作。

附加题:

  • 默认使用 A 和 D 键将该三角形绕 z 轴旋转,实现将三角形绕任意过原点的轴旋转。

解题思路

  1. 修改get_model_matrix(float rotation_angle),题目只需要实现三维中绕 z 轴旋转的变换矩阵,矩阵如下: $$ R_z =\begin{pmatrix}cos\alpha &-sin\alpha &0 &0 \\sin\alpha &cos\alpha &0 &0 \\0 &0 &1 &0 \\0 &0 &0 &1 \\\end{pmatrix} $$

  2. 修改get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar),先做透视变换,再做正交变换,就能得到透视投影所需要的矩阵了。

    压缩截锥体空间为立方体空间

    正交投影的实现方式

    $$ \displaylines{M_{persp\rightarrow ortho} = \begin{pmatrix}n &0 &0 &0 \\0 &n &0 &0 \\0 &0 &n+f &-nf \\0 &0 &1 &0 \\\end{pmatrix}\\\\M_{orth} = \begin{bmatrix} \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{bmatrix}\begin{bmatrix} 1 &0 &0 &-\frac{r+l}{2} \\ 0 &1 &0 &-\frac{t+b}{2} \\ 0 &0 &1 &-\frac{n+f}{2} \\ 0 &0 &0 &1\end{bmatrix}= \begin{pmatrix}\frac{2}{r-l} &0 &0 &-\frac{r+l}{r-l} \\ 0 &\frac{2}{t-b} &0 &-\frac{t+b}{t-b} \\ 0 &0 &\frac{2}{n-f} &-\frac{n+f}{n-f} \\ 0 &0 &0 &1\end{pmatrix}\\\\M_{persp} = M_{ortho}M_{persp\rightarrow ortho}} $$

    题目没有直接给出l、r、b、t,但是提供了Field of view(fov)和Aspect ratio,可以利用它们求出l、r、b、t。

    fov和屏幕宽高比

    用fovY和宽高比得到l, r, b, t的值 $$ \displaylines{\tan\frac{fovY}{2} = \frac{t}{|n|} \\aspect\_ratio = \frac{width}{height} = \frac{2r}{2t} = \frac{r}{t} \\联立得:\\t = |n|\tan\frac{fovY}{2} \\r = aspect\_ratio * t \\b = -t \\l = -r} $$

  3. 提高:实现将三角形绕任意过原点的轴旋转需要使用罗德里格旋转公式(Rodrigues’ rotation formula), 设绕轴n旋转角度α:

    $$ \mathbf{R}(\mathbf{n}, \alpha)=\cos (\alpha) \mathbf{I}+(1-\cos (\alpha)) \mathbf{n} \mathbf{n}^{T}+\sin (\alpha) \underbrace{\left(\begin{array}{ccc}0 & -n_{z} & n_{y} \\n_{z} & 0 & -n_{x} \\-n_{y} & n_{x} & 0\end{array}\right)}_{\mathbf{N}} $$

代码实现

Eigen::Matrix4f get_model_matrix(float rotation_angle){
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();

    // DONE: Implement this function
    // Create the model matrix for rotating the triangle around the Z axis.
    // Then return it.
    Eigen::Matrix4f rotation;
    double angle = rotation_angle / 180.0 * MY_PI;

    rotation << 
       std::cos(angle), -std::sin(angle), 0, 0,
        std::sin(angle), std::cos(angle), 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1;

    model = rotation * model;

    return model;
}

Eigen::Matrix4f get_model_matrix(Vector3f axis, float rotation_angle){
    
   // DONE提高:绕任意轴旋转
    double angle = rotation_angle / 180.0 * MY_PI;
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
    Eigen::Matrix3f I = Eigen::Matrix3f::Identity();
    Eigen::Matrix3f M;
    Eigen::Matrix3f N;
  
    N << 0, -axis[2], axis[1],
        axis[2], 0, -axis[0],
        -axis[1], axis[0], 0;
  
    M = std::cos(angle) * I + (1 - std::cos(angle)) * axis * axis.adjoint() + std::sin(angle) * N;
  
    model << 
       M(0, 0), M(0, 1), M(0, 2), 0,
        M(1, 0), M(1, 1), M(1, 2), 0,
        M(2, 0), M(2, 1), M(2, 2), 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();

    // DONE: Implement this function
    // Create the projection matrix for the given parameters.
    // Then return it.

    Eigen::Matrix4f persp_to_ortho;
    Eigen::Matrix4f orth;

    float n = -zNear; // Z轴反向
    float f = -zFar; // Z轴反向
    float t = std::tan(eye_fov / 180 * MY_PI / 2) * std::abs(n);
    float r = aspect_ratio * t;
    float b = -t;
    float l = -r;


    persp_to_ortho << 
       n, 0, 0, 0,
        0, n, 0, 0,
        0, 0, n + f, -n * f,
        0, 0, 1, 0;

    orth << 
       2 / (r - l), 0, 0, -(r + l) / (r - l),
        0, 2 / (t - b), 0, -(t + b) / (t - b),
        0, 0, 2 / (n - f), -(n + f) / (n - f),
        0, 0, 0, 1;


    projection = orth * persp_to_ortho * projection;

    return projection;
}