Bölüm 7: Kenar Yumuşatma (Antialiasing)
Önceki bölümlerde ürettiğimiz küre görüntülerinin kenarlarına yakından baktığınızda rahatsız edici tırtıklar, basamaklar ve merdivenlenme etkileri (Aliasing / Jaggies) göreceksiniz.
Gerçek dünyada bir fotoğraf makinesi tek bir noktaya bakmaz; piksel sensörünün üzerine düşen tüm fotonların ortalamasını kaydeder. Biz ise her pikselin tam orta noktasına yalnızca tek bir ışın gönderdiğimiz için ya küreye çarptık ya da kaçırdık!

Piksel Altı Çoklu Örnekleme (Supersampling)
Bu sorunu çözmenin yolu, her bir piksel için tek bir ışın yerine \(N\) adet rastgele ışın göndermek ve dönen renklerin aritmetik ortalamasını almaktır:

Pikselin kapsadığı \([-0.5, +0.5]\) kare alanının içine rastgele serpilen bu ışınlar, kürenin kenarında kısmen küreye kısmen gökyüzüne çarparak pürüzsüz bir renk geçişi oluşturur.
Rastgele Sayı Üretimi: rtweekend.h
Rastgele sayılar üretmek için rtweekend.h dosyamıza modern C++ <random> kütüphanesini ekleyelim:
| rtweekend.h (Rastgele Sayı Yardımcıları) |
|---|
| #include <random>
inline double random_double() {
// [0.0, 1.0) aralığında rastgele reel sayı döner
static std::uniform_real_distribution<double> distribution(0.0, 1.0);
static std::mt19937 generator;
return distribution(generator);
}
inline double random_double(double min, double max) {
// [min, max) aralığında rastgele reel sayı döner
return min + (max - min) * random_double();
}
|
Kamera Sınıfına Çoklu Örnekleme Eklenmesi
Kameramıza samples_per_pixel parametresi ekleyelim ve her örnek için piksel karesi içinde rastgele bir ışın üretelim:
| camera.h (Güncelleme) |
|---|
| class camera {
public:
double aspect_ratio = 1.0;
int image_width = 100;
int samples_per_pixel = 10; // Piksel başına örnekleme sayısı
void render(const hittable& world) {
initialize();
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
for (int j = 0; j < image_height; j++) {
std::clog << "\rKalan satır: " << (image_height - j) << ' ' << std::flush;
for (int i = 0; i < image_width; i++) {
color pixel_color(0, 0, 0);
for (int sample = 0; sample < samples_per_pixel; sample++) {
ray r = get_ray(i, j);
pixel_color += ray_color(r, world);
}
write_color(std::cout, pixel_samples_scale * pixel_color);
}
}
std::clog << "\rTamamlandı! \n";
}
private:
double pixel_samples_scale; // 1.0 / samples_per_pixel
void initialize() {
// ... (önceki hesaplamalar) ...
pixel_samples_scale = 1.0 / samples_per_pixel;
}
ray get_ray(int i, int j) const {
// (i, j) pikseli etrafındaki kare alandan rastgele bir ışın üret
auto offset = sample_square();
auto pixel_sample = pixel00_loc
+ ((i + offset.x()) * pixel_delta_u)
+ ((j + offset.y()) * pixel_delta_v);
auto ray_origin = center;
auto ray_direction = pixel_sample - ray_origin;
return ray(ray_origin, ray_direction);
}
vec3 sample_square() const {
// [-0.5, +0.5] aralığında rastgele 2B ofset döner
return vec3(random_double() - 0.5, random_double() - 0.5, 0);
}
};
|
Piksel başına örneğin 100 ışın (samples_per_pixel = 100) gönderdiğimizde, küremizin kenarları jilet gibi pürüzsüz ve doğal bir yumuşaklığa kavuşacaktır!