GAMES101-作业2
序
这一系列博客主要是为了记录自己对GAMES101课程的作业完成情况.
作业2: Triangles and Z-buffering, 栅格化一个三角形
作业描述
本次作业, 需要栅格化一个三角形.
上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)
。
但这一次,你需要自己填写并调用函数
rasterize_triangle(const Triangle& t)
。
该函数的内部工作流程如下:
创建三角形的 2 维 bounding box。
遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。
如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
作业分析
这次作业是上次作业框架的延续, 需要我们自己写出后续的画出三角形的操作, 主要涉及两个函数:
rasterize_triangle()
: 执行三角形栅格化算法, 按照作业描述中的流程即可static bool insideTriangle()
: 测试点是否在三角形内, 按照课上的步骤即可
注: 计算深度的部分不需要我们写, 作业中给出了框架
代码实现(包括提高题)
提高题:
2x2
的MSAA
其实就是将每一个像素分为4份.
然后对每一份进行计算, 后面对该像素进行平均.
rasterize_triangle() 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// Bounding Box
int widthMax = static_cast<int>(std::ceil(std::max(std::max(t.v[0][0], t.v[1][0]), t.v[2][0])));
int widthMin = static_cast<int>(std::floor(std::min(std::min(t.v[0][0], t.v[1][0]), t.v[2][0])));
int heightMax = static_cast<int>(std::ceil(std::max(std::max(t.v[0][1], t.v[1][1]), t.v[2][1])));
int heightMin = static_cast<int>(std::floor(std::min(std::min(t.v[0][1], t.v[1][1]), t.v[2][1])));
bool MSAA = true;
if(MSAA){
// MSAA Multi Simple AntiAliasing
// SuperSampling:
// Step1: Take N * N samples in each pixel
const std::vector<Eigen::Vector2f> pixRelaLoc = {
{0.25, 0.25},
{0.25, 0.75},
{0.75, 0.25},
{0.75, 0.75}
};
// 计算四个点是否三角形内
for(int x = widthMin; x < widthMax; ++x)
for(int y = heightMin; y < heightMax; ++y){
int count = 0;
float Zmin = FLT_MAX;
for(auto& i : pixRelaLoc)
if(insideTriangle(x+i[0], y+i[1], t.v)){
// If so, use the following code to get the interpolated z value.
auto[alpha, beta, gamma] = computeBarycentric2D(x+i[0], y+i[1], t.v);
float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
Zmin = std::min(z_interpolated, Zmin);
++count;
}
if(count > 0){
// 将其位置处的插值深度值 与 深度缓冲区 中的相应值比较
// 我们将 z 进行了反转, 保证其都是正数, 并且越大表示离视点越远
// 默认 z = 0; 表示 near
if(Zmin < depth_buf[get_index(x, y)]){
depth_buf[get_index(x, y)] = Zmin;
set_pixel(Vector3f(x, y, Zmin),t.getColor()*(count / 4.0));
}
}
}
}
else{
for(int x = widthMin; x < widthMax; ++x)
for(int y = heightMin; y < heightMax; ++y)
if(insideTriangle(x+0.5, y+0.5, t.v)){
auto[alpha, beta, gamma] = computeBarycentric2D(x+0.5, y+0.5, t.v);
float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
z_interpolated *= w_reciprocal;
if(z_interpolated < depth_buf[get_index(x, y)]){
depth_buf[get_index(x, y)] = z_interpolated;
set_pixel(Vector3f(x, y, z_interpolated),t.getColor());
}
}
}
}
static bool insideTriangle() 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static bool insideTriangle(int x, int y, const Vector3f* _v)
{
// TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
// 判断点(x, y)是否在三角形内
// ---> 转换为 三个向量是否同向
Vector2f PQ;
Vector2f PP;
float z[3];
for(int i = 0; i < 3; ++i){
PQ << x - _v[i](0), y - _v[i](1);
PP << _v[(i+1)%3](0) - _v[i](0),
_v[(i+1)%3](1) - _v[i](1);
z[i] = PQ(0) * PP(1) - PQ(1) * PP(0);
}
return (z[0] > 0 && z[1] > 0 && z[2] > 0) || (z[0] < 0 && z[1] < 0 && z[2] < 0);
}
问题解决
按照正常流程得到的图是反着的. 为了得到与作业要求中相同的答案,
可以对修改以下投影矩阵(其实任意多做一次投影矩阵就可) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
Eigen::Matrix4f projection;
eye_fov = eye_fov / 180.0 * acos(-1);
float yTop = zNear * tan(eye_fov / 2);
float xRight = yTop * aspect_ratio;
projection(0, 0) = zNear / xRight;
projection(1, 1) = zNear / yTop;
projection(2, 2) = (zNear + zFar) / (zNear - zFar);
projection(2, 3) = (-2.0 * zNear * zFar) / (zNear - zFar);
projection(3, 2) = 1;
projection(3, 3) = 0;
// 应对上下颠倒, 左右颠倒的问题
Eigen::Matrix4f inverseXY = Eigen::Matrix4f::Identity();
inverseXY(0, 0) = -1; inverseXY(1, 1) = -1;
projection = inverseXY * projection;
return projection;
}