作业要求
完善以下函数:
rasterize_triangle(): 执行三角形栅格化算法
static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。作业实现
测试点是否在三角形内
把给出的点与三角形的三个顶点分别做叉乘,如果这三个叉乘的结果符号相同,那么这个点在三角形内。
static bool insideTriangle(const float x, const float y, const Vector3f *_v) {
const Eigen::Vector3f p(x, y, 1);
for (int i = 0; i < 3; i++) {
Eigen::Vector3f v0 = _v[i];
Eigen::Vector3f v1 = _v[(i + 1) % 3];
Eigen::Vector3f edge = v1 - v0;
if (Eigen::Vector3f vp = p - v0; edge.cross(vp).z() < 0) {
return false;
}
}
return true;
}三角形栅格化
-
计算三角形的包围盒
const int x_min = static_cast<int>(std::min(v[0].x(), std::min(v[1].x(), v[2].x()))); const int x_max = static_cast<int>(std::max(v[0].x(), std::max(v[1].x(), v[2].x()))); const int y_min = static_cast<int>(std::min(v[0].y(), std::min(v[1].y(), v[2].y()))); const int y_max = static_cast<int>(std::max(v[0].y(), std::max(v[1].y(), v[2].y()))); -
遍历包围盒内的每个像素,判断像素是否在三角形内,顺便加上MSAA
for (auto x = x_min; x <= x_max; x++) { for (auto y = y_min; y <= y_max; y++) { auto sample_count = 0; auto z_min = std::numeric_limits<float>::infinity(); auto vid = get_index(x, y) * 4; for (int i = 0; i < 4; i++) { if (insideTriangle(static_cast<float>(x) + axis_x[i], static_cast<float>(y) + axis_y[i], t.v)) { auto [alpha, beta, gamma] = computeBarycentric2D(static_cast<float>(x) + axis_x[i], static_cast<float>(y) + axis_y[i], 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; sample_count++; if (-z_interpolated < depth_sample_buf[vid + i]) { depth_sample_buf[vid + i] = -z_interpolated; frame_sample_buf[vid + i] = t.getColor() / 4; z_min = std::min(z_min, -z_interpolated); } } } if (sample_count > 0) { depth_buf[get_index(x, y)] = z_min; frame_buf[get_index(x, y)] = Eigen::Vector3f{0, 0, 0}; for (int i = 0; i < 4; i++) { frame_buf[get_index(x, y)] += frame_sample_buf[vid + i]; } set_pixel(Eigen::Vector3f(x, y, 0), frame_buf[get_index(x, y)]); } } }
插值深度时,需要先计算出插值的重心坐标,然后再计算插值的深度。
去黑边
-
在
rasterizer.hpp中添加两个成员变量,分别用于存储每个采样像素的颜色和深度。//rasterizer.hpp private: std::vector<Eigen::Vector3f> frame_sample_buf; std::vector<float> depth_sample_buf; -
在
rasterizer.cpp中增加对应的初始化和清空函数。//rasterizer.cpp rst::rasterizer::rasterizer(int w, int h) : width(w), height(h) { frame_buf.resize(w * h); depth_buf.resize(w * h); frame_sample_buf.resize(w * h * 4); depth_sample_buf.resize(w * h * 4); } void rst::rasterizer::clear(rst::Buffers buff) { if ((buff & rst::Buffers::Color) == rst::Buffers::Color) { std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0}); std::fill(frame_sample_buf.begin(), frame_sample_buf.end(), Eigen::Vector3f{0, 0, 0}); } if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth) { std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity()); std::fill(depth_sample_buf.begin(), depth_sample_buf.end(), std::numeric_limits<float>::infinity()); } }
