Bölüm 3: Işınlar, Basit Kamera ve Arka Plan
Bütün ışın izleyicilerin temelinde tek bir soru yatar: Gözden çıkan bir ışık ışını uzayda hangi rengi görür?
Bu soruyu yanıtlamak için:
1. Bir ışını matematiksel olarak tanımlamamız,
2. Kameramızdan sahnedeki her bir piksele doğru ışınlar fırlatmamız,
3. Ve o ışının yöneldiği rengi hesaplamamız gerekir.
Işın (ray) Nedir?
Matematikte bir ışın, belirli bir başlangıç noktasından çıkan ve belirli bir doğrultuda sonsuza uzanan bir doğrudur. Parametrik denklemi şöyledir:
\[\mathbf{P}(t) = \mathbf{A} + t \cdot \mathbf{b}\]
Burada:
- \(\mathbf{A}\): Işının başlangıç noktası (Origin),
- \(\mathbf{b}\): Işının doğrultu vektörü (Direction),
- \(t\): Bir reel sayı parametresidir.
\(t > 0\) olduğu sürece \(\mathbf{P}(t)\), ışın çizgisi boyunca ileriye doğru hareket eden bir noktayı temsil eder:
| ray.h |
|---|
| #ifndef RAY_H
#define RAY_H
#include "vec3.h"
class ray {
public:
ray() {}
ray(const point3& origin, const vec3& direction)
: orig(origin), dir(direction) {}
const point3& origin() const { return orig; }
const vec3& direction() const { return dir; }
// P(t) = Origin + t * Direction
point3 at(double t) const {
return orig + t*dir;
}
private:
point3 orig;
vec3 dir;
};
#endif
|
Kamera Geometrisi ve Görüş Penceresi (Viewport)
Kameramızı dünya koordinatlarında \((0, 0, 0)\) merkezine yerleştirelim.
Grafik geleneğinde standart olarak:
- \(+X\): Sağa bakar,
- \(+Y\): Yukarı bakar,
- \(-Z\): Ekranın içine (karşıya) bakar.

Kameranın önünde, \(Z = -1\) mesafesinde sanal bir Görüş Penceresi (Viewport) tanımlarız. Ekrandaki her bir piksel, bu pencere üzerinde küçük bir alana karşılık gelir:

Penceremizin sol üst köşesinden başlayarak her bir pikselin dünya koordinatlarını hesaplar ve kameramızdan o noktaya doğru bir ray fırlatırız!
Gökyüzü Gradyanı: Doğrusal İnterpolasyon (lerp)
Eğer fırlattığımız ışın sahnede hiçbir nesneye çarpmazsa ne görecek? Arka planı, yani gökyüzünü!
Mavi-beyaz harika bir gökyüzü gradyanı oluşturmak için Doğrusal İnterpolasyon (Linear Interpolation / lerp) kullanırız:
\[\text{renk} = (1 - a) \cdot \text{Beyaz} + a \cdot \text{Mavi}\]

Burada \(a\), ışın yönünün normalize edilmiş \(y\) bileşenine göre \([0, 1]\) aralığına ölçeklenir:
| ray_color Fonksiyonu |
|---|
| color ray_color(const ray& r) {
vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5 * (unit_direction.y() + 1.0);
return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
}
|
Tam Çalışan Kamera Programı
Artık pikselleri doğrudan boyamak yerine, kameramızdan ışınlar göndererek sahneyi render ediyoruz:
| main.cc |
|---|
| #include "vec3.h"
#include "color.h"
#include "ray.h"
#include <iostream>
color ray_color(const ray& r) {
vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5 * (unit_direction.y() + 1.0);
return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
}
int main() {
// Resim En/Boy Oranı ve Boyutları
auto aspect_ratio = 16.0 / 9.0;
int image_width = 400;
// Yüksekliği hesapla (en az 1 piksel olmalı)
int image_height = int(image_width / aspect_ratio);
image_height = (image_height < 1) ? 1 : image_height;
// Kamera Parametreleri
auto focal_length = 1.0;
auto viewport_height = 2.0;
auto viewport_width = viewport_height * (double(image_width)/image_height);
auto camera_center = point3(0, 0, 0);
// Viewport kenar vektörleri
auto viewport_u = vec3(viewport_width, 0, 0);
auto viewport_v = vec3(0, -viewport_height, 0);
// Pikseller arası mesafe vektörleri (delta)
auto pixel_delta_u = viewport_u / image_width;
auto pixel_delta_v = viewport_v / image_height;
// Sol üst pikselin konumunu belirle
auto viewport_upper_left = camera_center
- vec3(0, 0, focal_length) - viewport_u/2 - viewport_v/2;
auto pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
// Render Döngüsü
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++) {
auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);
auto ray_direction = pixel_center - camera_center;
ray r(camera_center, ray_direction);
color pixel_color = ray_color(r);
write_color(std::cout, pixel_color);
}
}
std::clog << "\rTamamlandı! \n";
}
|
Bu kodu derleyip çalıştırdığınızda:
g++ -O3 main.cc -o inOneWeekend
./inOneWeekend > image.ppm
Elde edeceğimiz görsel, gerçek bir kameranın sanal gökyüzüne bakışıdır:

Tebrikler! Işın izleyicimizin sanal kamerası artık çalışıyor. Şimdi bir sonraki bölümde uzayımıza ilk 3 boyutlu nesnemizi (bir küreyi) yerleştireceğiz!