本文为闫令琪老师的GAMES 101课程的作业2的个人实现与一些简单的思考,文中如有错漏欢迎指出。
作业要求
本次作业的任务是在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。需要自己填写并调用函数 rasterize_triangle(const Triangle& t)
。
该函数的内部工作流程如下:
- 创建三角形的 2 维 bounding box。
- 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
- 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。
- 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
代码实现
insideTriangle
测试某个点是否在三角形内部,使用叉积进行判断
static bool insideTriangle(int x, int y, const Eigen::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]
auto v0p = Eigen::Vector2f(x-_v[0].x(), y-_v[0].y());
auto v1p = Eigen::Vector2f(x - _v[1].x(), y - _v[1].y());
auto v2p = Eigen::Vector2f(x - _v[2].x(), y - _v[2].y());
auto v0v1 = Eigen::Vector2f(_v[1].x() - _v[0].x(), _v[1].y() - _v[0].y());
auto v1v2 = Eigen::Vector2f(_v[2].x() - _v[1].x(), _v[2].y() - _v[1].y());
auto v2v0 = Eigen::Vector2f(_v[0].x() - _v[2].x(), _v[0].y() - _v[2].y());
auto z0 = v0p.x() * v0v1.y() - v0v1.x() * v0p.y();
auto z1 = v1p.x() * v1v2.y() - v1v2.x() * v1p.y();
auto z2 = v2p.x() * v2v0.y() - v2v0.x() * v2p.y();
bool all_negative = z0 < 0 && z1 < 0 && z2 < 0;
bool all_positive = z0 > 0 && z1 > 0 && z2 > 0;
return all_negative || all_positive;
}
rasterize_triangle
按照流程的指引编写即可
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// TODO : Find out the bounding box of current triangle.
float xmin = min(v[0].x(), min(v[1].x(), v[2].x()));
float xmax = max(v[0].x(), max(v[1].x(), v[2].x()));
float ymin = min(v[0].y(), min(v[1].y(), v[2].y()));
float ymax = max(v[0].y(), max(v[1].y(), v[2].y()));
xmin = (int)floor(xmin);
xmax = (int)ceil(xmax);
ymin = (int)floor(ymin);
ymax = (int)ceil(ymax);
// iterate through the pixel and find if the current pixel is inside the triangle
// If so, use the following code to get the interpolated z 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;
for (int x = xmin; x <= xmax; x++)
{
for (int y = ymin; y <= ymax; 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;
// printf("%f %f\n", v[0].z(), t.getColor().x());
// TODO : set the current pixel (use the set_pixel function) to the color of the triangle
// (use getColor function) if it should be painted.
if (z_interpolated < depth_buf[get_index(x, y)])
{
depth_buf[get_index(x, y)] = z_interpolated;
auto color = t.getColor();
auto point = Eigen::Vector3f(x, y, 0);
set_pixel(point, color);
}
}
}
}
}
绘制结果
简单的思考
绘制时发现z值为-5的蓝色三角形被绘制在了z值为-2的绿色三角形前面,因为在这里计算z值时,把z值转成正的后,按照原来的计算方法,-5转成的正值比-2更小,这里只要改一改计算方法,使蓝色三角形的z值比绿色三角形更大就可以了。
//Viewport transformation
for (auto & vert : v)
{
vert.x() = 0.5*width*(vert.x()+1.0);
vert.y() = 0.5*height*(vert.y()+1.0);
vert.z() = -vert.z() * f1 + f2; // 修改后的计算方法
}