参考:
【GAMES101】作业2–三角形光栅化
GAMES101作业2-代码框架逐行理解(c++基础巩固)
games101 作业2
GAMES101作业(更新中)
GAMES101作业2及课程总结(重点解决黑边问题)
GAMES101作业2及课程总结(重点解决黑边问题)
文章目录
题目:
实现以下两个函数:
rasterize_triangle(): 执行三角形栅格化算法
static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。
具体函数分析
bool insideTriangle()
原理
判断点是否在三角形内部:
Games101-lecture02 Linear Algebra


代码
分别求AB,AC,BC,AP,BP,CP的向量,然后求叉乘
判断,代码中利用点乘是为了确定方向,点乘>0说明两个向量方向一致
static bool insideTriangle(int x, int y, const 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]
Vector3f p(x,y,1.0);
Vector3f a=_v[0],b=_v[1],c=_v[2];
Vector3f ab=b-p,ap=p-a,bc=c-b,bp=p-b,ca=a-c,cp=p-c;
Vector3f ans1=ab.cross(ap),ans2=bc.cross(bp),ans3=ca.cross(cp);
if(ans1.dot(ans2)>=0&&ans1.dot(ans3)>=0)
return 1;
else
return 0;
}
rasterize_triangle()
原理(题目要求pdf中已给出)
具体原理可以看这里
rasterize_triangle(const Triangle& t) 该函数的内部工作流程如下:
- 创建三角形的 2 维 bounding box。(需自己实现)
- 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。(判断函数insideTriangle()需要自己实现)
- 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。(注释里已经写好)
- 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。(需自己实现)
bounding box:

深度缓冲的算法(Z-Buffer Algorithm)

代码:
//Screen space rasterization
float minn(float a,float b,float c){
return min(a,min(b,c));
}
float maxx(float a,float b,float c){
return max(a,max(b,c));
}
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// 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.
//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.
//1.find bounding box
int xmin = (int)(minn(v[0].x(),v[1].x(),v[2].x())-0.5);
int ymin = (int)(minn(v[0].y(),v[1].y(),v[2].y())-0.5);
int xmax = (int)(maxx(v[0].x(),v[1].x(),v[2].x())+0.5);
int ymax = (int)(maxx(v[0].y(),v[1].y(),v[2].y())+0.5);
for(int x=xmin;x<=xmax;x++){
for(int y=ymin;y<=ymax;y++){
//2.判断点是否在三角形内部
if(insideTriangle(x+0.5,y+0.5,t.v)){
//3.获取深度值,z_interpolated,利用插值找到每个像素的深度值
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;
//4.设置像素颜色并更新深度缓冲区
if(z_interpolated < depth_buf[get_index(x,y)]){
set_pixel(Vector3f(x,y,z_interpolated),t.getColor());
depth_buf[get_index(x,y)] = z_interpolated;
}
}
}
}
}
效果图:

仔细看图像边缘会有锯齿感



附加题
用 super-sampling 处理 Anti-aliasing : 你可能会注意到,当我们放大图像时,图像边缘会有锯齿感。我们可以用 super-sampling来解决这个问题,即对每个像素进行 2 * 2 采样,并比较前后的结果 (这里并不需要考虑像素与像素间的样本复用)。需要注意的点有,对于像素内的每一个样本都需要维护它自己的深度值,即每一个像素都需要维护一个 sample list。最后,如果你实现正确的话,你得到的三角形不应该有不正常的黑边。
Anti-aliasing
先模糊,再采样 = 抗走样

MSAA(Antialiasing By Supersampling)
MSAA是一个对反走样的近似因为按照上面的那种求每个像素中的区域百分比十分困难,所以就有了MSAA这种反走样的方法,其思想也是如上面的那种方法,但是取的是一个近似的求区域像素平均值的方法。
在MSAA中,我们将以划分的每个像素在进行划分,将原有的一个像素分为更多个小像素,这跟题目要求是一致的(对每个像素进行 2 * 2 采样)

具体步骤:
1.将原有的一个大像素分为四个小像素,每个点都可以判断是否在三角形内,如果四个点都在三角形内就是100%覆盖,也就是100%的红色,如果只有三个点在三角形内,则三角形对这个像素就是75%覆盖,其颜色也就是75%的红色+25%的白色。于是就可以得出每个像素的覆盖结果。就相当于模糊做完了
2.然后再采样,这时采样就会十分简单,因为我们规定像素是最小的单位格子,每个格子的颜色都是平均的(像素内的颜色都是一样的),采样完也就会得到上图的结果。
代码实现
参考:
每个像素拆分成四个小像素,分别记录其深度和颜色,该大像素是这四个小像素颜色的平均值,然后再进行着色(比如在交叉线上,可能绿色占四分之一,蓝色占四分之三)
效果图

注意:
1.hw1中z轴是负的,而hw2中题目将z轴进行了处理,报保证为正数

因此在引用hw1中写的get_projection_matrix时,这里的abs要改成-1*zNear的写法,不然图像会反转过来(因为z已经是正的了,abs(z)不会有任何影响,而在hw1中z是负数,所以-1和abs效果是一样的)



2146

被折叠的 条评论
为什么被折叠?



