GAMES101 作业2 ——三角形光栅化
作业任务:
填写并调用函数 rasterize_triangle(const Triangle& t)。
即实现光栅化
该函数的内部工作流程如下:
创建三角形的 2 维 bounding box。
遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中
心的屏幕空间坐标来检查中心点是否在三角形内。
如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。
如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
你需要修改的函数如下:
• rasterize_triangle(): 执行三角形栅格化算法
• static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函
数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
所以我们第一步实现static bool insideTriangle()
static bool insideTriangle(int x, int y, const Vector3f *_v) {
//已知测试点 point (x, y)
// 三角形顶点 _v[0], _v[1], _v[2]
// 向量叉乘公式(x1,y1)x(x2,y2) = x1*y2 - y1*x2
Eigen::Vector2f p;
p << x, y;
//1. 准备三角形各边的向量
Eigen::Vector2f AB = _v[1].head(2) - _v[0].head(2);
Eigen::Vector2f BC = _v[2].head(2) - _v[1].head(2);
Eigen::Vector2f CA = _v[0].head(2) - _v[2].head(2);
//2. 准备测量点和三角形各点连线的向量
Eigen::Vector2f AP = p - _v[0].head(2);
Eigen::Vector2f BP = p - _v[1].head(2);
Eigen::Vector2f CP = p - _v[2].head(2);
//3. 判断叉乘结果
float z1 = AB.x() * AP.y() - AB.y() * AP.x();
float z2 = BC.x() * BP.y() - BC.y() * BP.x();
float z3 = CA.x() * CP.y() - CA.y() * CP.x();
if ((z1 > 0 && z2 > 0 && z3 > 0) || (z1 < 0 && z2 < 0 && z3 < 0)) {
return true;
} else {
return false;
}
}
第二步:逐一扫描这个bounding box内的所有单元,以每个单元的中心位置代替此单元,将坐标传入insideTriangle函数中,如果不在,则扫描下一个,如果在,则以三角形三点做插值得到这个中心点的z值,进一步判断,这个z值是否比深度缓存中对应位置的值要小,不小则不处理,小则替换深度缓存中的值为当前值。随后设置这个单元的颜色值。
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle &t) {
auto v = t.toVector4(); //将3D坐标转为4D齐次坐标
// 1. 找到三角形的bounding box(最小包裹矩形框)的坐标
float min_x = std::min(v[0][0], std::min(v[1][0], v[2][0])); //获取三个顶点中最小的x坐标,这里也可以用v[0/1/2].x()表示
float max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));
float min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));
float max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));
// 2. 遍历三角形bbox中的所有测试点
for (int x = min_x; x <= max_x; x++) {
for (int y = min_y; y <= max_y; y++) {
//2.1 判断(x,y)是否在三角形内
if (insideTriangle(x, y, t.v)) {
float min_depth = FLT_MAX; //默认值为无穷大(远)
// 如果在三角形内部,计算该位置的插值深度值(interpolated depth value)
auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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;
min_depth = std::min(min_depth, z_interpolated);
// 与深度缓冲区(depth buffer)对应值进行比较
if (depth_buf[get_index(x, y)] > min_depth) {
// set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
Vector3f color = t.getColor();
Vector3f point;
point << x, y, min_depth;
depth_buf[get_index(x, y)] = min_depth;
set_pixel(point, color);
}
}
}
}
}
输出结果:
注意:如果遇到下图三角形旋转180°的情况,说明是Y轴反了,因为教程里的公式是向-Z看的,但程序里nera和far给的数值却是[0.1, 50]
更新get_projection_matrix
中的 上截面结果为负即可。
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
...
auto t = -zNear * tan(angle / 2); //上截面, 前面增加负号
...
- 点赞
- 收藏
- 关注作者
评论(0)