GAMES101 | 作业1:旋转与投影


1.基础部分

1.1任务描述

给定三维下三个点\(v_0(2.0, 0.0,?2.0)\), \(v_1(0.0, 2.0,?2.0)\), \(v_2(?2.0, 0.0,?2.0)\), 需要将这三个点的坐标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形。

涉及作业框架中以下两个函数的编写:
get_model_matrix(float rotation_angle): 逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,你只需要实现三维中绕z轴旋转的变换矩阵,而不用处理平移与缩放。
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar): 使用给定的参数逐个元素地构建透视投影矩阵并返回
该矩阵。

1.2任务分析

1.2.1坐标旋转

get_model_matrix中应用了齐次坐标下绕坐标轴的旋转矩阵的构造

Eigen::Matrix4f get_model_matrix(float rotation_angle)
{  //绕z轴旋转
    Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
    float cosa = cos(rotation_angle/180.0*acos(-1));
    float sina = sin(rotation_angle/180.0*acos(-1));
    model(0,0) = cosa;
    model(0,1) = -sina;
    model(1,0) = sina;
    model(1,1) = cosa;
    return model;
}

1.2.2投影变换

Projection Transformation投影变换分为两步:

Perspective Projection 透视投影

处理后的物体拥有了近大远小的透视关系,坐标为zNear、zFar的远近平面上的点变换后z坐标保持不变(后续推导会用到这个性质)。

变换推导

变换公式

\[①\begin{bmatrix} x'\\ y'\\ z'\\ 1 \end{bmatrix}=M_p*\begin{bmatrix} x\\ y\\ z\\ 1 \end{bmatrix} \]

由三角形的相似得

\[②y'=yn/z\\ ③x'=xn/z \]

将②、③带入①得

\[M_p*\begin{bmatrix} x\\ y\\ z\\ 1 \end{bmatrix}=\begin{bmatrix} xn/z\\ yn/z\\ unknow\\ 1 \end{bmatrix}==\begin{bmatrix} xn\\ yn\\ unknow'\\ z \end{bmatrix} \]

推得

\[M_p=\begin{bmatrix} n&0&0&0\\ 0&n&0&0\\ ?&?&?&?\\ 0&0&1&0 \end{bmatrix} \]

为确定\(M_p\)第三行的值,应用z轴坐标为n的近平面上的点变换后坐标保持不变这个性质构造方程组

\[M_p*\begin{bmatrix} x\\ y\\ n\\ 1 \end{bmatrix}=\begin{bmatrix} x\\ y\\ n\\ 1 \end{bmatrix}==\begin{bmatrix} xn\\ yn\\ n^2\\ n \end{bmatrix} \]

\(M_p\)第三行为

\[\begin{bmatrix} 0&0&A&B\\ \end{bmatrix} \]

\[\begin{bmatrix} 0&0&A&B\\ \end{bmatrix}*\begin{bmatrix} x\\ y\\ n\\ 1 \end{bmatrix}=n^2\\ \]

得到式④

\[An+B=n^2 \]

又有性质z轴坐标为z的远平面上的点变换后z坐标保持不变,取点\((0,0,z)\)

\[M_p*\begin{bmatrix} 0\\ 0\\ z\\ 1 \end{bmatrix}=\begin{bmatrix} 0\\ 0\\ z\\ 1 \end{bmatrix}==\begin{bmatrix} 0\\ 0\\ z^2\\ z \end{bmatrix} \]

那么有

\[\begin{bmatrix} 0&0&A&B\\ \end{bmatrix}*\begin{bmatrix} 0\\ 0\\ z\\ 1 \end{bmatrix}=z^2\\ \]

得到式⑤

\[Az+B=z^2 \]

联立④、⑤解得

\[A=z+n\\ B=-zn \]

Orthographics Projection 正交投影

将任意空间\([l,r]*[b,t]*[f,n]\)(此处为上一步透视投影得到的结果)映射到\([-1,1]^3\)单位空间中。

构造齐次坐标下的平移矩阵和变形矩阵

\[M_o=\begin{bmatrix} 2/w&0&0&0\\ 0&2/h&0&0\\ 0&0&2/(n-f)&0\\ 0&0&0&1 \end{bmatrix}\begin{bmatrix} 1&0&0&-(l+r)/2\\ 0&1&0&-(b+t)/2\\ 0&0&1&-(n+f)/2\\ 0&0&0&1 \end{bmatrix}\\ \]

函数传入了四个参数

  • eye_fov 视场角
  • aspect_ratio 宽高比
  • zNear 近平面坐标
  • zFar 远平面坐标

计算得

\[w=2*tan(eye\_fov/2)/zNear\\ h=w/aspect\_ratio \]

由于视场角的中心轴沿着z轴,所以x、y方向的平移量为0。

代码

Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,float zNear, float zFar)
{
    Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
    Eigen::Matrix4f ortho = Eigen::Matrix4f::Identity();
    Eigen::Matrix4f persp = Eigen::Matrix4f::Identity();
    persp(0,0) = zNear;
    persp(1,1) = zNear;
    persp(3,2) = 1.0;
    persp.row(2) = Eigen::Vector4f(0,0,zNear+zFar,-zNear*zFar); 
    float w = 2*tan(eye_fov/2.0)*zNear;
    float h = w/aspect_ratio;
    ortho << 2.0/w, 0, 0, 0,
             0, 2.0/h, 0, 0,
             0, 0, 2.0/(zNear - zFar), -(zNear+zFar)/(zNear-zFar),
             0, 0, 0, 1;
    projection = ortho*persp;
    return projection;
}

2.提高部分

将三角形绕任意过原点的轴旋转。
此处用到了罗德里格斯旋转公式:(证明还没看懂TAT)

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

    float cosa = cos(rotation_angle/180.0*acos(-1));
    float sina = sin(rotation_angle/180.0*acos(-1));

    Eigen::Vector3f n;
    n << 1.0f,1.0f,-1.0f; //旋转轴的向量表示
    n.normalize(); //单位化
    tmp <<    0.0f, -n[2],  n[1],
              n[2],  0.0f, -n[0],
             -n[1],  n[0],  0.0f;
    model.block<3,3>(0,0) = (cosa * myI) + ((1.0f-cosa)*n*n.transpose()) + (sina * tmp);
   return model;
}

3.运行结果