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;
}
最後所得圖片爲:
將局部放大一下做一下對比:
可以看到,下面的圓的邊緣明顯出現了“過渡色”的像素塊,比較好的解決了走樣問題。
同時我們也要注意到,開啓反走樣之後,渲染速率變慢了非常多。