文章目录
- 一、问题总览
- 二、作业参考
- 2.1 get_projection_matrix()函数
- 2.2 static bool insideTriangle()函数
- 2.3 rasterize_triangle()
- 三、附件
一、问题总览
- 在屏幕上画出一个实心三角形,换言之,栅格化一个三角形
- 实现并调用函数rasterize_triangle(const Triangle& t)
- 创建三角形的2维bounding box。
- 遍历此bounding box内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
- 如果在内部,则将其位置处的插值深度值(interpolated depth value) 与深度缓冲区(depth buffer) 中的相应值进行比较。
- 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区(depth buffer)。
- 需要修改的函数
- rasterize_triangle(): 执行三角形栅格化算法
- static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
- 对于三角形内部的像素,需要根据三角形顶点值进行插值,得到其深度值。插值的深度值被储存在变量z_interpolated中。
- 为了方便写代码,已将z进行了反转,保证都是正数,并且越大表示离视点越远。
- 在此次作业中,无需处理旋转变换,只需为模型变换返回一个单位矩阵。最后,提供了两个hard-coded三角形来测试实现,如果程序实现正确,将看到如下所示的输出图像:
二、作业参考
2.1 get_projection_matrix()函数
- 填充main.cpp下的get_projection_matrix()函数,该函数在第一次作业中已被实现
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f persp_matrix;//透视矩阵
Eigen::Matrix4f translate_matrix;//移动矩阵
Eigen::Matrix4f scale_matirx;//缩放矩阵
persp_matrix << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
float halfAngle = eye_fov / 2 / 180 * MY_PI;
float height = -2 * std::tan(halfAngle) * zNear;//没有负号则三角形上下颠倒
float width = height * aspect_ratio;
translate_matrix << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -(zNear + zFar) / 2,
0, 0, 0, 1;
scale_matirx << 2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, 2 / (zNear - zFar), 0,
0, 0, 0, 1;
projection = scale_matirx * translate_matrix * persp_matrix;
return projection;
}
2.2 static bool insideTriangle()函数
-
函数在rasterizer.cpp中
-
测试点是否在三角形内
- 三角形边向量, 三角形顶点与测试点向量 进行叉乘;若符号相同则测试点在三角形内部,否则在外部
- 注意,边向量要求是逆时针或者是顺时针
- 三角形边向量, 三角形顶点与测试点向量 进行叉乘;若符号相同则测试点在三角形内部,否则在外部
static bool insideTriangle(int x, int y, const Vector3f* _v)
{
bool sign[3];//记录三角形边向量和测试点顶点向量叉乘符号
for(int i = 0; i < 3; i++){
Vector3f vec1(x - _v[i].x(), y - _v[i].y(), 1);//测试点顶点向量
Vector3f vec2(_v[(i + 1) % 3].x() - _v[i].x(), _v[(i + 1) % 3].y() - _v[i].y(), 1);//边向量
sign[i] = vec1.cross(vec2).z() > 0;
}
if(sign[0] == sign[1] && sign[0] == sign[2]) return true;//符号相同,则在三角形内
return false;
}
2.3 rasterize_triangle()
- 函数在rasterizer.cpp中
- 执行三角形栅格化算法
- 使用重心坐标插值方法获得采样点深度
- alpha, beta, gamma求法如下
- 三个顶点的深度分别乘以alpha, beta, gamma,然后求和,就是该点的插值深度
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;
- 完整代码
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// TODO : Find out the bounding box of current triangle.
int bounding_box_x_left = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
int bounding_box_x_right = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
int bounding_box_y_left = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
int bounding_box_y_right = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));
// iterate through the pixel and find if the current pixel is inside the triangle
for(int x = bounding_box_x_left; x <= bounding_box_x_right; x++){
for(int y = bounding_box_y_left; y <= bounding_box_y_right; y++){
if(insideTriangle(x, y, t.v)){//如果该点在三角形内,则需要光栅化
// 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;//目前点的深度
// 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)]){//z保证都是正数,并且越大表示离视点越远
// 如果(x,y)离视点近,则需要重新绘制
depth_buf[get_index(x, y)] = z_interpolated;
set_pixel(Eigen::Vector3f(x, y, z_interpolated), t.getColor());
}
}
}
}
}
三、附件
作业2压缩包