GAMES101作业
lz最近在学习图形学Graphics,现已学完GAMES101课程,为回顾课程重要内容故将作业总结发布至此,欢迎各位同学交流。
作业0:虚拟机的使用
关于虚拟机如何搭建,请大家自行参考bilibili视频这里主要对知识点和作业做一个结合说明。
作业描述
给定一个点P=(2,1),将该点绕原点先逆时针旋转45°,再平移(1,2),计算出变换后点的坐标(要求用齐次坐标进行计算)
算法与理论
-
齐次坐标的表示
2D point = (x, y, 1)T
2D vector = (x, y, 0)T
所以,题目中的点P的齐次坐标形式表示为(2, 1, 1)T -
2D变换矩阵的表示(Scale, Ratate, Transformation)
可见第一行描述的是x方向,第二行描述的是y方向,第三行描述齐次坐标保持不变- Scale: 表示物体(向量、点)的放缩;
- Rotation: 表示物体(向量、点)的旋转;
- Translation: 表示物体(向量、点)的平移,可见向量平移前后不变,点平移后才变化。
作业源码
以下是实现作业0功能的源代码
main.cpp
#include<cmath>
#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Dense>
#include<iostream>
int main(){
// Init point
Eigen::Vector3f p(2.0f,1.0f,1.0f);
// Rotate and transformation matrix
Eigen::Matrix3f R, T;
float ftheta = sqrt(2.0f) / 2;
R << ftheta, -ftheta, 0,
ftheta, ftheta, 0,
0, 0, 1.0f;
T << 1.0f, 0, 1.0f,
0, 1.0f, 2.0f,
0, 0, 1.0f;
p = R * p;
std::cout << "After rotate \n"
std::cout << p << std::endl;
p = T * p;
std::cout << "After transformation \n"
std::cout << p << std::endl;
return 0;
}
结果展示
作业1:旋转与投影
作业描述
本次作业的任务是填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个点v0(2.0,0.0,−2.0)
v1(0.0,2.0,−2.0)
v2(−2.0,0.0,−2.0)
, 你需要将这三个点的坐标变换为屏幕坐标,并在屏幕上绘制出对应的线框三角形(在代码框架中,我们已经提供了 draw_triangle 函数,所以你只需要去构建变换矩阵即可)。
简而言之,我们需要进行模型、视图、投影、视口等变换来将三角形显示在屏幕上。在提供的代码框架中,我们留下了模型变换和投影变换的部分给你去完成。
核心任务
get_model_matrix(float rotation_angle)
: 实现返回三维绕z轴旋转的变换矩阵;get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
: 实现返回投影矩阵;
算法与理论
这里我们从作业要求出发,复习以下两个概念
3D Transformation
与2D类似,常见的矩阵变换包括:Scale(缩放)、Rotation(旋转)、Translation(平移),而Scale和Translation可以根据2D简单推演得到,此处重点复习Rotation。
-
绕坐标轴旋转
-
绕任意轴旋转
我们知道,任意的旋转都可以用坐标轴旋转的方法集成得到,因此我们可以推导下列公式:
Rxyz(α, β, γ) = Rx(α) Ry(β) Rz(γ)
对于任意的旋转角α,和任意的旋转轴n,可以使用Rodrigues’ Rotation Formula得到:
MVP Transformation
MVP变换即Model(模型)、View(视图)、Projection(投影)变换。
-
ModelView(模型视图)Transformation:把随机的相机摆放和朝向转换到相机在原点处、看向-z方向、向上方向为y。
- 相机位置从e移动到原点——T_view矩阵
- 朝向从g移动到-z,向上方向从t移动到y,所以g×t移动到x——R_view矩阵
M_view = R_view·T_view
-
Projection(投影)Transformation
-
Orthographic projection(正交投影) :从任意位置的[l,r]×[b,t]×[f,n]长方体映射为[-1,1]³的正方体,得到M_ortho
-
Perspective projection(透视投影),得到M_persp
-
M_persp=M_ortho·M_persp->ortho
-
当已知条件为aspect ratio(宽高比)和fovY(Y方向视角)时,我们可以将aspect和fovY转换到r、t上。
-
作业源码
get_model_matrix(float 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.
float rotation_angle_radian = rotation_angle * MY_PI / 180;
Eigen::Matrix4f rotation;
rotation << cos(rotation_angle_radian), -sin(rotation_angle_radian), 0, 0,
sin(rotation_angle_radian), cos(rotation_angle_radian), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
model = rotation * model;
return model;
}
get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float 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.
float eye_fov_radian = eye_fov * MY_PI / 180;
float t = tan(eye_fov_radian / 2) * abs(zNear);
float r = t * aspect_ratio;
float b = -t;
float l = -r;
Eigen::Matrix4f translation;
translation << 1, 0, 0, -(r+l)/2.0f,
0, 1, 0,-(t+b)/2.0f,
0, 0, 1, -(zNear+zFar)/2.0f,
0, 0, 0, 1;
Eigen::Matrix4f scale;
scale << 2.0f/(r-l), 0, 0, 0,
0, 2.0f/(t-b), 0, 0,
0, 0, 2.0f/(zNear-zFar), 0,
0, 0, 0, 1;
// Orthographic projection
projection = scale * translation;
Eigen::Matrix4f M_persp_ortho;
M_persp_ortho << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear+zFar, -zFar*zNear,
0, 0, 1, 0;
// Perspective projection
projection = projection * M_persp_ortho;
return projection;
}
get_rotation(Vector3f axis, float angle)
:绕着过原点的axis轴旋转angle角度
Eigen::Matrix4f get_rotation(Vector3f axis, float angle)
{
// rotation around axis and angle
float rotation_angle_radian = angle * MY_PI / 180;
Eigen::Matrix3f I = Eigen::Matrix3f::Identity();
Eigen::Matrix3f N; // axis product matrix
N << 0, -axis(2), axis(1),
axis(2), 0, -axis(0),
-axis(1), axis(0), 0;
Eigen::Matrix3f r3 = cos(rotation_angle_radian) * I
+ (1 - cos(rotation_angle_radian)) * (axis * axis.transpose())
+ sin(rotation_angle_radian) * N;
Eigen::Matrix4f rotation = Eigen::Matrix4f::Identity();
rotation.block(0,0,3,3) << r3;
return rotation;
}
结果展示
编译后执行
./Rasterizer -r 20
输出旋转20°到output.png
./Rasterizer -r 20 image.png
输出旋转20°到image.png
作业2:Triangles and Z-buffering
作业描述
本次作业主要目标是在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。需要自己填写并调用函数rasterize_triangle(const Triangle& t),该函数的内部工作流程如下:
核心任务:
rasterize_triangle()
: 执行三角形栅格化算法- 创建三角形的2维bounding box。
- 遍历此 bounding box 内的所有像素(使用其整数索引)然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
- 如果在内部,则将其位置处的插值深度值(interpolated depth value) 与深度缓冲区(depth buffer) 中的相应值进行比较。
- 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区(depthbuffer)。
static bool insideTriangle()
: 测试点是否在三角形内。
算法与理论
这里我们从作业要求出发,复习以下概念
判断点与三角形位置关系——Cross Product
Bounding box
深度测试(Z-Buffer/Depth-Buffer)
由于场景中的物体有远有近,Z-Buffer用于表示可见性以及遮挡的场景,先光栅化远的,后近的以覆盖近的物体。
- 画家算法(Painter’s Algorithm):先画场景中远的物体,后画近的以覆盖近的物体;即对物体进行深度排序,但对于相互覆盖的物体不适用。
- Z-Buffer:对每个像素最浅的z进行排序,借助于深度图(Z/Depth-Buffer).注意,我们同时存两个数据:Frame buffer(颜色数据),Z/Depth-Buffer(深度数据)
- 对于每一个obj及其每一个像素(Sampling)
- 判定z是否小于Z-Buffer[point]:是–更新Z-Buffer和颜色,否–Do nothing
*假定z是正数
反走样(Antialiasing)
先把三角形变模糊后采样结果,即对三角形的像素值在一个区域内取平均(卷积)。在实际中,我们采取Antialiasing By Supersampling(MSAA)方法来近似。
- Supersampling:将一个像素划分成若干个小像素(N×N),每一个小像素有一个中心,逐个判断是否在三角形内,然后将结果求平均。
- Sample:直接取每个像素的中心。
作业源码
结果展示
走样结果:可以看出明显的走样(锯齿)
MASS(反走样)后结果:可以看出没有锯齿,有清晰的边界
参考资料
MSAA反走样