Chapter 6: Antialiasing
先回顾一下在上一章我们得到的图
把局部放大一下
可以看到球体边缘呈现比较明显的锯齿状。解决这个问题的过程就叫做反走样。其实就是让前景和背景交叉的边缘变得更平滑而已。具体应该怎么做呢?
从代码中我们知道,我们共发射出了200*100条射线,对应了200*100个像素格子(从上图中可以隐隐看出像素格子),这样在前景与背景交叉的像素格子上必定只有一种颜色,这样就会出现明显的锯齿状(锯齿其实就是一个个像素格子)。在不改变图像分辨率的情况下,如何改善这种状况呢?
其实,每个像素格子的颜色并不代表着那一块区域真正的颜色,只是我们采用了发射一条射线碰撞到的一点返回的颜色代替了那一小块区域的颜色,那么,解决这个问题就变得简单了,我们多发射一些射线到这个格子里,然后取这些射线返回颜色的均值就好啦!
首先为了方便,我们先把搁置了很久的 Camera 类封装起来:
#pragma once
#include "Ray.h"
class Camera
{
public:
Camera()
{
lower_left_corner = Vec3(-2.0f, -1.0f, -1.0f);
horizontal = Vec3(4.0f, 0.0f, 0.0f);
vertical = Vec3(0.0f, 2.0f, 0.0f);
origin = Vec3(0.0f, 0.0f, 0.0f);
}
Ray getRay(float u, float v)
{
return Ray(origin, lower_left_corner + u*horizontal + v*vertical - origin);
}
Vec3 lower_left_corner;
Vec3 origin;
Vec3 horizontal;
Vec3 vertical;
};
接着修改Main方法为:
int main()
{
ofstream outfile;
outfile.open("ch6Image.ppm");
int nx = 200;
int ny = 100;
//采样次数
int ns = 100;
outfile << "P3\n" << nx << " " << ny << "\n255\n";
Hitable *list[2];
list[0] = new Sphere(Vec3(0.0f, 0.0f, -1.0f), 0.5f);
list[1] = new Sphere(Vec3(0.0f, -100.5f, -1.0f), 100.0f);
Hitable *world = new HitableList(list, 2);
Camera cam;
//随机数引擎
default_random_engine reng;
uniform_real_distribution<float> uni_dist(0.0f, 1.0f);
for (int j = ny - 1; j >= 0; j--)
{
for (int i = 0; i < nx; i++)
{
Vec3 col(0.0f, 0.0f, 0.0f);
//每个区域采样ns次
for (int s = 0; s < ns; s++)
{
float u = float(i + uni_dist(reng)) / float(nx);
float v = float(j + uni_dist(reng)) / float(ny);
Ray r = cam.getRay(u,v);
//Vec3 p = r.point_at_parameter(2.0);
//将本区域((u,v)到(u+1,v+1))的颜色值累加
col += Color(r, world);
}
//获得区域的颜色均值
col /= float(ns);
int ir = int(255.99*col[0]);
int ig = int(255.99*col[1]);
int ib = int(255.99*col[2]);
outfile << ir << " " << ig << " " << ib << "\n";
}
}
outfile.close();
return 0;
}
最后所得图片为:
将局部放大一下做一下对比:
可以看到,下面的圆的边缘明显出现了“过渡色”的像素块,比较好的解决了走样问题。
同时我们也要注意到,开启反走样之后,渲染速率变慢了非常多。