1. 作业简介
该次作业考察的是课程《Lecture 04 Transformation Cont》的内容:
- 绕 x x x 轴旋转的旋转变换矩阵
- 正交投影矩阵
- 透视投影矩阵
1.1 作业要求
给定三维下的三个点 v 0 ( 2.0 , 0.0 , − 2 , 0 ) v_0(2.0, 0.0, -2,0) v0(2.0,0.0,−2,0) , v 0 ( 0.0 , 2.0 , − 2 , 0 ) v_0(0.0, 2.0, -2,0) v0(0.0,2.0,−2,0) , v 0 ( − 2.0 , 0.0 , − 2 , 0 ) v_0(-2.0, 0.0, -2,0) v0(−2.0,0.0,−2,0) ,你需要将这三个点的坐标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形。
1.2 相关函数
需要在文件 main.cpp
中修改下列函数:
get_model_matrix(float rotation_angle);
- 逐个元素地构建模型变换矩阵并返回该矩阵。在此函数中,你只需要实现三维中绕 z z z 轴旋转的变换矩阵,而不用处理平移与缩放。
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar);
- 使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵。
[Optional] main();
- 自行补充你所需的其他操作。
2. 作业代码分析
2.1 get_model_matrix()
// 实现三维中绕 z 轴旋转的变换矩阵
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
Eigen::Matrix4f rotateZ;
float theta = rotation_angle / 180.0 * MY_PI;
//绕 z 轴的旋转矩阵
rotateZ << cos(theta), -sin(theta), 0, 0,
sin(theta), cos(theta), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = rotateZ * model;
return model;
}
给定一个旋转的角度,实现绕 z z z 轴旋转的旋转矩阵:
R z = [ cos α − sin α 0 0 sin α cos α 0 0 0 0 1 0 0 0 0 1 ] R_z = \begin{bmatrix} \cos{\alpha} & - \sin{\alpha} & 0 & 0 \\ \sin{\alpha} & \cos{\alpha} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rz=⎣⎢⎢⎡cosαsinα00−sinαcosα0000100001⎦⎥⎥⎤
2.2 get_projection_matrix()
参 数 | 含 义 |
---|---|
eye_fov | 垂直可视角 |
aspect_ratio | 宽高比 |
zNear | 距离 Camera 较近的边界 |
zFar | 距离 Camera 较远的边界 |
// 使用给定的参数逐个元素地构建透视投影矩阵并返回该矩阵
// eye_fov : 垂直可视角
// aspect_ratio : 宽高比
// zNear : 距离 Camera 较近的边界
// zFar : 距离 Camera 较远的边界
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
// 返回的是透视投影矩阵
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// 构造 透视投影 to 正交投影 的矩阵
Eigen::Matrix4f M_PerspToOrtho = Eigen::Matrix4f::Identity();
M_PerspToOrtho << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, - zFar * zNear,
0, 0, 1, 0;
// 垂直可视角的一半
float halfEyeAngelRadian = (eye_fov / 2.0) * (MY_PI / 180.0);
// top y轴的最高点
float t = zNear * std::tan(halfEyeAngelRadian);
// right x轴的最大值
float r = t * aspect_ratio;
//left x轴最小值
float l = - r;
//bottom y轴的最大值
float b = - t;
// 构造矩阵的第一部分:先将中心点平移至原点
Eigen::Matrix4f M_Ortho1 = Eigen::Matrix4f::Identity();
M_Ortho1 << 1, 0, 0, (-1)* (r + l) / 2,
0, 1, 0, - (t + b) / 2,
0, 0, 1, - (zNear + zFar) / 2,
0, 0, 0, 1;
// 构造矩阵的第二部分:再将 length/width/height 缩放至 2
Eigen::Matrix4f M_Ortho2 = Eigen::Matrix4f::Identity();
M_Ortho2 << 2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (zNear - zFar), 0,
0, 0, 0, 1;
Eigen::Matrix4f M_Ortho = M_Ortho2 * M_Ortho1;
projection = M_Ortho * M_PerspToOrtho;
return projection;
}
透视投影矩阵 的实现:
- 透视投影到正交投影的“挤压”矩阵的构造
M P e r s p → O r t h o = [ n 0 0 0 0 n 0 0 0 0 n + f − n f 0 0 1 0 ] M_{Persp \to Ortho} = \begin{bmatrix} n & 0 & 0 & 0 \\[1.5ex] 0 & n & 0 & 0 \\[1.5ex] 0 & 0 & n + f & -nf \\[1.5ex] 0 & 0 & 1 & 0 \end{bmatrix} MPersp→Ortho=⎣⎢⎢⎢⎢⎢⎡n0000n0000n+f100−nf0⎦⎥⎥⎥⎥⎥⎤
- 正交投影矩阵 构造的第一部分:先将中心点平移至原点
M O r t h o 1 = [ 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − n + f 2 0 0 0 1 ] M_{Ortho1} = \begin{bmatrix} 1 & 0 & 0 & -\frac{r+l}{2} \\[1.5ex] 0 & 1 & 0 & -\frac{t+b}{2} \\[1.5ex] 0 & 0 & 1 & -\frac{n+f}{2} \\[1.5ex] 0 & 0 & 0 & 1 \end{bmatrix} MOrtho1=⎣⎢⎢⎢⎢⎢⎡100001000010−2r+l−2t+b−2n+f1⎦⎥⎥⎥⎥⎥⎤
- 正交投影矩阵 构造的第二部分:将 length/width/height 缩放至 2
M O r t h o 2 = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] M_{Ortho2} = \begin{bmatrix} \frac{2}{r-l} & 0 & 0 & 0 \\[1.5ex] 0 & \frac{2}{t-b} & 0 & 0 \\[1.5ex] 0 & 0 & \frac{2}{n-f} & 0 \\[1.5ex] 0 & 0 & 0 & 1 \end{bmatrix} MOrtho2=⎣⎢⎢⎢⎢⎢⎡r−l20000t−b20000n−f200001⎦⎥⎥⎥⎥⎥⎤
- 透视投影 的构造
M P e r s p = M O r t h o 2 ⋅ M O r t h o 1 ⋅ M P e r s p → O r t h o M_{Persp} = M_{Ortho2} \cdot M_{Ortho1} \cdot M_{Persp \to Ortho} MPersp=MOrtho2⋅MOrtho1⋅MPersp→Ortho
NOTE :
- 矩阵的具体推导过程见笔记《04 - 视图变换》
3. 测试样例
- 初始渲染
- 按
a
键进行逆时针旋转
- 按
d
键进行顺时针旋转