GAMES101 作业1(附旋转矩阵和投影矩阵推导)

内容

第一个问题

问题2

第一个问题

题目: 返回一个绕 z 轴旋转给定转动角度(rotation_angle)的旋转矩阵。

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.
    return model;
}

解析:

这道题比较简单, 但有一个坑, 就是 rotation_angle 是角度, 需要将其转化为弧度, 也就是令rotation_angle = rotation_angle * PI / 180。

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.
    rotation_angle = rotation_angle / 180 * MY_PI;
    model << cos(rotation_angle), -sin(rotation_angle), 0, 0,
        sin(rotation_angle), cos(rotation_angle), 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1;
    return model;
}

个人觉得这个旋转矩阵的推导从基变换的角度更容易理解:

首先提取出旋转矩阵的第一列(cos(rotation_angle), sin(rotation_angle), 0, 0)T, 代码中是逆时针旋转的矩阵。可以想象一下,将基向量(1, 0, 0)T 绕 Z 轴逆时针旋转 rotation_angle°,由简单的三角函数推导,可以得到结果 (cos(rotation_angle), sin(rotation_angle), 0)T。同理可证得第二列。

问题2

题目: 给定视场角(eye_fov), 宽高比(aspect_ratio), 近平面位置(zNear), 远平面位置(zFar),

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.
    return projection;
}

解析:

1. 构造正交投影矩阵:

正交投影矩阵的作用就是将给定的空间中的长方体映射在一个[-1, 1]^{^{3}}的立方体中。因此首先需要做的就是将物体先平移再缩放, 而平移就是将长方体从它的中心移到原点。设长方体的边界为b(bottom), t(top), l(left), r(right), n(near), f(far), 可以得到平移矩阵:

\begin{pmatrix} 1 & 0 &0 &-(r + l) /2 \\ 0 & 1& 0 &-(b+t)/2 \\ 0& 0 & 1 &-(n+f)/2 \\ 0& 0 & 0 & 1 \end{pmatrix}

和缩放矩阵:

\begin{pmatrix} 2/(r-l) &0 &0 &0 \\ 0 &2/(t-b) &0 &0 \\ 0 & 0 & 2/(n-f)) &0 \\ 0 & 0 &0 &1 \end{pmatrix}

正交投影矩阵可以通过将缩放矩阵乘以平移矩阵来获得:

M_{ortho} = \begin{pmatrix} 2/(r-l) &0 &0 &(r+l)/(r-l) \\ 0 &2/(t-b) &0 & (t+b)/(t-b)\\ 0 &0 &2/(n-f) & (f+n)/(f-n)\\ 0 &0 &0 &1 \end{pmatrix}

2. 构造“压缩”矩阵M_{perst->ortho}

在该步骤中,为了将物体在远平面上的投影压缩到透视投影后的状态,然后进行正交投影,使得透视投影操作变得更加简单。

我们期待的是:假设原平面上有齐次坐标\begin{pmatrix} x &y &z &1 \end{pmatrix}^T,则在近平面上的齐次坐标根据相似三角形原理可得\begin{pmatrix} nx/z& ny/z& unknown&1 \end{pmatrix}^T, 又因为是齐次坐标,故同乘 z 后表示的坐标不变,可将其变换为\begin{pmatrix} nx & ny& unknown&z \end{pmatrix}^T。由此可以推得M_{perst->ortho}中的部分元素:

\begin{pmatrix} n &0 &0 &0 \\ 0 & n &0 &0 \\ A &B &C &D \\ 0 &0 &1 &0 \end{pmatrix}

(A, B, C, D为未知数), 由近平面(near)上的点在经过投影变换后不变, 可以设近平面上一点坐标\begin{pmatrix} x &y &n &1 \end{pmatrix}^T, 为了利用已经获得的部分元素, 将坐标乘以n, 得\begin{pmatrix} nx&ny &n^2 &n \end{pmatrix}^T, 因此有 A*x + B*y + C*n + D = n² , 由此可得 A = B = 0 , 则 C*n + D = n² 。再由远平面(far)上的中点不变, 可得\begin{pmatrix} 0 &0 &f &1 \end{pmatrix}^T, 可变换为\begin{pmatrix} 0 &0 &f^2 &f \end{pmatrix}^T, 同上, 有  A*x + B*y + C*f + D = f² , 已知 A = B = 0 , 则 C*f + D = f² 。联立 C*n + D = n² 与 C*f + D = f² , 最终得到:

M_{perst->ortho} = \begin{pmatrix} n &0 &0 &0 \\ 0 & n &0 &0 \\ 0 &0 &n+f &-nf \\ 0 &0 &1 &0 \end{pmatrix}

3. 代码:

投影矩阵是M_{persp} = M_{ortho}M_{perst->ortho},只要用代码实现即可:

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.

    eye_fov = eye_fov / 180 * MY_PI;        //将角度转为弧度
    float top = zNear * tan(eye_fov * 0.5); //需要变换的长方体的顶部
    float bottom = -top;                    //需要变换的长方体的底部
    float right = top * aspect_ratio;       //需要变换的长方体的右侧
    float left = -right;                    //需要变换的长方体的左侧

    Eigen::Matrix4f ortho = Eigen::Matrix4f::Identity();    //Mortho
    ortho <<    2 / (right - left), 0, 0, (right + left) / (right - left),
                0, 2 / (top - bottom), 0, (top + bottom) / (top - bottom),
                0, 0, 2 / (zNear - zFar), (zFar + zNear) / (zFar - zNear),
                0, 0, 0, 1;

    Eigen::Matrix4f squish = Eigen::Matrix4f::Identity();   //Msquish->ortho
    squish <<   zNear, 0, 0, 0,
                0, zNear, 0, 0,
                0, 0, zNear + zFar, -zNear * zFar,
                0, 0, 1, 0;

    projection = ortho * squish;
    return projection;
}

版权声明:本文为博主奔腾的CPU原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/m0_53161412/article/details/123020779

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年2月21日 下午4:07
下一篇 2022年2月22日 上午10:50

相关推荐