问题描述
本次作业的任务是填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个点 v 0 (2.0, 0.0, −2.0), v 1 (0.0, 2.0, −2.0), v 2 (−2.0, 0.0, −2.0), 你需要将这三个点的坐标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形 (在代码框架中,我们已经提供了 draw_triangle 函数,所以你只需要去构建变换矩阵即可)。简而言之,我们需要进行模型、视图、投影、视口等变换来将三角形显示在屏幕上。在提供的代码框架中,我们留下了模型变换和投影变换的部分给你去完成。
问题分析
在这份作业中,需要完成两个函数:
- 根据给定角度(单位为度数,使用时需要转化一下)获得物体绕z轴旋转的旋转矩阵并返回。
Eigen::Matrix4f get_model_matrix(float rotation_angle)
- 根据宽高比(aspect_ratio)、垂直可视角度(eye_fov)、近平面距离(zNear)、远平面距离(zFar)获得投影矩阵并返回
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
问题解答
1> 直接根据下面的公式构造旋转矩阵
M x ( α ) = ( cos α − sin α 0 0 sin α cos α 0 0 0 0 1 0 0 0 0 1 ) M_x(\alpha)=\begin{pmatrix} \cos\alpha&-\sin\alpha&0&0\\ \sin\alpha&\cos\alpha&0&0\\ 0&0&1&0\\ 0&0&0&1\\ \end{pmatrix} Mx(α)=⎝⎜⎜⎛cosαsinα00−sinαcosα0000100001⎠⎟⎟⎞
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
//角度轉弧度
rotation_angle=rotation_angle/180.0*M_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;
}
2> 构造投影矩阵
这个函数我们要完成以下三件事:
- 首先需要将视锥的远平面拉伸( M p e r s p − > o r t h o M_{persp->ortho} Mpersp−>ortho)
- 然后要将立方体中心点平移到原点( M o r t h o M_{ortho} Mortho的右半部分矩阵)
- 最后把立方体([l,r] × [ b , t ] × [ f , n ] \times[b,t]\times[f,n] ×[b,t]×[f,n])映射到正则(canonical)立方体 [ − 1 , 1 ] 3 [-1,1]^3 [−1,1]3 上( M p e r s p − > o r t h o M_{persp->ortho} Mpersp−>ortho的左半部分矩阵)
M p e r s p = M o r t h o M p e r s p − > o r t h o M_{persp}=M_{ortho}M_{persp->ortho} \\ Mpersp=MorthoMpersp−>ortho
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->ortho}=\begin{pmatrix} n&0&0&0\\ 0&n&0&0\\ 0&0&n+f&-nf\\ 0&0&1&0\\ \end{pmatrix} Mpersp−>ortho=⎝⎜⎜⎛n0000n0000n+f100−nf0⎠⎟⎟⎞
M o r t h o = ( 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ) ( 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − n + f 2 0 0 0 1 ) M_{ortho}=\begin{pmatrix} \frac2{r-l}&0&0&0\\ 0&\frac2{t-b}&0&0\\ 0&0&\frac2{n-f}&0\\ 0&0&0&1\\ \end{pmatrix} \begin{pmatrix} 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{pmatrix} Mortho=⎝⎜⎜⎛r−l20000t−b20000n−f200001⎠⎟⎟⎞⎝⎜⎜⎛100001000010−2r+l−2t+b−2n+f1⎠⎟⎟⎞
以下代码将完成这三件事,但由于矩阵做叉积的意义是从右向左的,所以这三个变换我是按照以上步骤反着来的。
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();
//由zNear和eye_fov求高度
float height=tan(eye_fov/2)*abs(zNear)*2.0;
//由寬高比求寬度
float width = aspect_ratio*height;
//縮放到正則立方體
projection<<2.0/width,0,0,0,0,2.0f/height,0,0,0,0,2.0f/(zFar-zNear),0,0,0,0,1;
//將立方體中心平移到原點(xy方向無需平移)
Eigen::Matrix4f tmp=Eigen::Matrix4f::Identity();
tmp<<1,0,0,0,
0,1,0,0,
0,0,1,-(zNear+zFar)/2.0,
0,0,0,1;
projection=projection*tmp;
//進行遠平面的拉伸
tmp<<zNear,0,0,0,
0,zNear,0,0,
0,0,zNear+zFar,-zNear*zFar,
0,0,1,0;
projection=projection*tmp;
return projection;
}
运行效果
绕z轴旋转
旋转九十度
心得
- 这个代码不能在vscode直接编译运行,需要在命令行中使用CMake自行编译,然后再输入Rasterizer运行。
- 文件夹乱码是因为虚拟机不支持中文造成的。
- 第一个函数的参数是角度不是弧度。
- 个人认为直接求宽高就够了,平移用不着xy方向的移动,缩放指定宽高深度即可,不需要具体的tblr。
写在最后
刚学图形学,欢迎讨论指正。