接上一章,采样后,出现了锯齿
------>【一些采样的理论】
光栅化的过程,就是在屏幕空间对所有像素中心进行“是否在三角形内部”这个函数的采样
采样时,会出现一些“瑕疵”Artifacts
出现这些Artifacts的原因 ——信号变化太快而采样慢
<滤波Filter>
简单理解:把特定的某些频率去掉
<傅里叶变换>可以把时域变为频域,还有逆傅里叶变换
高通滤波(边界变化明显,所以留下了边界)
低通滤波(低频信号留了下来)
<图形学中的卷积——信号在周围的区域做一个平均>
时域卷积==频率相乘
这张图也验证了上边的,低通滤波模糊效果
走样,在频率的角度上看,就是频率的频谱在搬移时发生了混合
像素密集,就意味着采样密集,搬移间隔大,就不容易走样
【反走样】
增加采样率(这是从根本上解决问题,但是现实中也不可能改变分辨率。。)
先进行模糊,再采样(先采取低通滤波,再采样)
如上图,用正方形的滤波去掉高频分量,再采样就不会出现混叠了
【SSAA-超采样反走样(Super Sampling AA)】
如果有限离散像素点逼近结果不好,那么我们用更多的采样点去逼近不就会得到更好的结果了吗?所以根据这个思想我们可以把原来的每个像素点进行细分,比如下例中,我们讲每个像素点细分成了4个采样点
把每个像素点内部所细分的采样点的颜色值全部加起来再求均值,作为该像素点的抗走样之后的颜色值
靠近三角形边缘的像素点有的变淡了,从宏观角度来看的话,这个锯齿就会变得不那么明显了
【多采样反走样(Multi-Sampling AA)】
MSAA的做法也很容易理解,我们依然同样会分采样点,但是只会去计算究竟有几个采样点会被三角形cover,计算颜色的时候只会利用像素中心坐标计算一次颜色(即所有的信息都会被插值到像素中心然后取计算颜色)
【作业2】
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]
//判断bounding Box内的像素是否在三角形内
Vector2f p;
p << x,y; //像素的中心点
//(head是取前2个数)
Vector2f AB = _v[1].head(2) - _v[0].head(2);
Vector2f BC = _v[2].head(2) - _v[1].head(2);
Vector2f CA = _v[0].head(2) - _v[2].head(2);
Vector2f AP = p - _v[0].head(2);
Vector2f BP = p - _v[1].head(2);
Vector2f CP = p - _v[2].head(2);
//叉乘,判断是否在三角形内
return AB[0] * AP[1] - AB[1] * AP[0] > 0
&& BC[0] * BP[1] - BC[1] * BP[0] > 0
&& CA[0] * CP[1] - CA[1] * CP[0] > 0;
}
void rst::rasterizer::rasterize_triangle(const Triangle& t) {
auto v = t.toVector4();
// bounding box
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);
bool MSAA = false;
//MSAA 4X
if (MSAA) {
// 格子里的细分四个小点坐标
std::vector<Eigen::Vector2f> pos
{
{0.25,0.25},
{0.75,0.25},
{0.25,0.75},
{0.75,0.75},
};
for (int x = min_x; x <= max_x; x++) {
for (int y = min_y; y <= max_y; y++) {
// 记录最小深度
float minDepth = FLT_MAX;
// 四个小点中落入三角形中的点的个数
int count = 0;
// 对四个小点坐标进行判断
for (int i = 0; i < 4; i++) {
// 小点是否在三角形内
if (insideTriangle((float)x + pos[i][0], (float)y + pos[i][1], t.v)) {
// 如果在,对深度z进行插值
auto tup = computeBarycentric2D((float)x + pos[i][0], (float)y + pos[i][1], t.v);
float alpha;
float beta;
float gamma;
std::tie(alpha, beta, gamma) = tup;
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);
count++;
}
}
if (count != 0) {
if (depth_buf[get_index(x, y)] > minDepth) {
Vector3f color = t.getColor() * count / 4.0;
Vector3f point(3);
point << (float)x, (float)y, minDepth;
// 替换深度
depth_buf[get_index(x, y)] = minDepth;
// 修改颜色
set_pixel(point, color);
}
}
}
}
}
else {
for (int x = min_x; x <= max_x; x++) {
for (int y = min_y; y <= max_y; y++) {
if (insideTriangle((float)x + 0.5, (float)y + 0.5, t.v)) {
auto tup = computeBarycentric2D((float)x + 0.5, (float)y + 0.5, t.v);
float alpha;
float beta;
float gamma;
std::tie(alpha, beta, gamma) = tup;
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;
//插值所得的点的深度小于原来这个点的深度,进行替换
if (depth_buf[get_index(x, y)] > z_interpolated) {
Vector3f color = t.getColor();
Vector3f point(3);
point << (float)x, (float)y, z_interpolated;
depth_buf[get_index(x, y)] = z_interpolated;
set_pixel(point, color);
}
}
}
}
}
}