games101 第二次作业 反走样与Zbuffer
1 总览
在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是
那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,
换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数
rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用
函数 rasterize_triangle(const Triangle& t)。
该函数的内部工作流程如下:
1. 创建三角形的 2 维 bounding box。
2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中
心的屏幕空间坐标来检查中心点是否在三角形内。
3. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。
4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
你需要修改的函数如下:
• rasterize_triangle(): 执行三角形栅格化算法
• static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函
数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
因为我们只知道三角形三个顶点处的深度值,所以对于三角形内部的像素,
我们需要用插值的方法得到其深度值。我们已经为你处理好了这一部分,因为有
关这方面的内容尚未在课程中涉及。插值的深度值被储存在变量 z_interpolated
中。
请注意我们是如何初始化 depth buffer 和注意 z values 的符号。为了方便
同学们写代码,我们将 z 进行了反转,保证都是正数,并且越大表示离视点越远。
实现如下:
我也实现了MSAA
我也实现了MSAA
第一次做的时候好像求最大最小值那里出了一点问题,现在没有问题了
代码实现如下:
static bool insideTriangle(float x, float y, const Vector3f* _v) { Eigen::Vector3f P(x,y,0); Eigen::Vector3f AB(_v[1].x()-_v[0].x(),_v[1].y()-_v[0].y(),0); Eigen::Vector3f BC(_v[2].x()-_v[1].x(),_v[2].y()-_v[1].y(),0); Eigen::Vector3f CA(_v[0].x()-_v[2].x(),_v[0].y()-_v[2].y(),0); Eigen::Vector3f AP(x-_v[0].x(),y-_v[0].y(),0); Eigen::Vector3f BP(x-_v[1].x(),y-_v[1].y(),0); Eigen::Vector3f CP(x-_v[2].x(),y-_v[2].y(),0); Eigen::Vector3f ABxAP = AB.cross(AP); Eigen::Vector3f BCxBP = BC.cross(BP); Eigen::Vector3f CAxCP = CA.cross(CP); if(ABxAP.dot(BCxBP)>0&&CAxCP.dot(BCxBP)>0) { return true; } return false; // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2] }
然后是MSAA和Zbuffer的实现
注意,我们利用getindex函数得到具体的某个光栅里面的值,然后进行Zbuffer比较
其中我们实现了MSAA,注意,是四个点,而且一个pixel中的像素的最小值这里我们按这四个子像素中的最小值进行计算(我们这里,Z值取绝对值)
void rst::rasterizer::rasterize_triangle(const Triangle& t) { auto v = t.toVector4(); float min_x = std::min(v[0][0], std::min(v[1][0], v[2][0])); 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])); min_x = (int)std::floor(min_x); max_x = (int)std::ceil(max_x); min_y = (int)std::floor(min_y); max_y = (int)std::ceil(max_y); std::vector<Eigen::Vector2f> pos { {0.25,0.25}, {0.75,0.25}, {0.25,0.75}, {0.75,0.75}, }; //MSAA program for (float x = min_x; x <= max_x; x++) { for (float y = min_y; y <= max_y; y++) { // TODO : Find out the bounding box of current triangle. // 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. int count = 0; float minDepth = FLT_MAX; for(int i=0;i<=3;i++) { if(insideTriangle(x+pos[i][0],y+pos[i][1],t.v)) { count++; 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; minDepth = std::min(minDepth, z_interpolated); } } Eigen::Vector3f color = t.getColor()*count/4; int zindex = get_index(x,y); Eigen::Vector3f tmp(x,y,minDepth); if(depth_buf[zindex]>minDepth) { set_pixel(tmp,color); depth_buf[zindex] = minDepth; } }
最后效果如下: