简介
本文章主要内容为作业二的最后一个题目:反走样的处理。本文使用了两种反走样的办法,ssaa和msaa。在实现的过程中遇到了几个问题与大坑,大家有同样类似问题的不妨看看。
tips:反走样结果图片均按照4x4频率来采样。
MSAA问题
1、黑边情况1
最开始实现的反走样在两个三角形的交界处会产生黑色的边,如下面所示。
其原因主要为:这时采用的为原图片大小的深度缓存空间,两个三角形位置为关系为绿色在上(前),蓝色在下(后)面,绘制时的顺序也按照这样。在计算完绿色三角形之后,帧缓存和深度缓存均已更新,三角形边缘的地方像素颜色采用覆盖比率插值计算,深度值为绿色三角形在该坐标所计算出的深度插值。根据上面的话,这条黑色的边其实并不是真的的黑色,而是插值之后的绿色,只不过是由于覆盖比率很小在插值之后rgb值变得很低,颜色的饱和度与亮度会随之下降。例如原rgb值为(200,200,100),若覆盖比率为4/16的话,如下面效果,若覆盖率更低那么会更接近黑色。
在计算完第一个绿色的三角形之后,开始计算第二个蓝色的三角形,这时又遍历到了上面异常的黑边处,也就是有一点点绿色的黑边,需要比较深度缓存来决定蓝色三角形是否要在该像素绘制。在每个像素采样计算时,三角形的覆盖是按照4x4采样计算的,但是深度值是按照像素级别1x1计算和存储来的, 之前该像素位置只存储了绿色三角形的深度值,蓝色三角形在绿色之后,所以深度比较失败,从而导致了该处不会再更新像素值。
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
int sampling_frequency, sampling_times;
sampling_frequency = 4;
sampling_times = sampling_frequency * sampling_frequency;
float sampling_period = 1.0f / sampling_frequency;
int minX = width, minY = height, maxX = 0, maxY = 0;
for (auto vert : v) {
if (vert.x() < minX)
minX = vert.x();
if (vert.y() < minY)
minY = vert.y();
if (vert.x() > maxX)
maxX = vert.x();
if (vert.y() > maxY)
maxY = vert.y();
}
Vector3f color = t.getColor();
for (int i = minY; i < maxY; i++) {
for (int j = minX; j < maxX; j++) {
float z_depth = 0,blend_rate;
int coloring_block_counter = 0;
for (int m = 0; m < sampling_frequency; m++) {
for (int n = 0; n < sampling_frequency; n++) {
float x = j + (n + 0.5f) * sampling_period;
float y = i + (m + 0.5f) * sampling_period;
if (insideTriangle(x, y, t.v)) {
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;
z_depth += z_interpolated;
coloring_block_counter++;
}
}
}
if (coloring_block_counter > 0) {
z_depth = z_depth / coloring_block_counter;
blend_rate = 1.0f * coloring_block_counter / sampling_times;
Vector3f point = {
j * 1.0f,i * 1.0f,0.0f };
if (z_depth < depth_buf[get_index(j, i)]) {
depth_buf[get_index(j, i)] = z_depth;
Vector3f color = t.getColor() * blend_rate ;
set_pixel(point, color);
}
}
}
}
}
2、黑边情况2
如果只使用与图像大小同样的帧缓冲空间和深度缓冲空间,上述情况可能是无解的,也就是边缘处靠后绘制的图像若位置也在后面(深度差值比较失败)将无法更新像素值从而产生“黑边”,如果有请告诉我!
上述的情况是因为深度缓冲采样率不足所导致的,所以按照采样率扩大深度缓存空间。这个时候在计算深度缓存的时候一个像素是按照4x4频率来计算的,可以存储多个深度值并更新和计算各个三角形的边缘像素的覆盖比率。
如上图所示红框为1个像