Ana içeriğe geç

Bölüm 2: 3B Vektör Sınıfı (vec3)

Hemen hemen tüm bilgisayar grafikleri programlarında geometrik konumları, yönleri, yüzey normallerini ve hatta RGB renklerini temsil etmek için 3 boyutlu vektörler kullanılır.

Grafik motorumuzda: - Bir 3B uzay noktası \((x, y, z)\), - Bir ışının yönü \((dx, dy, dz)\), - Bir pikselin rengi \((r, g, b)\),

tamamen aynı 3 sayıdan oluşur! Bu yüzden tek bir güçlü vec3 sınıfı yazacak ve onu point3 ve color takma adlarıyla (alias) her yerde kullanacağız.


vec3.h Sınıfının Tasarımı

Sınıfımız 3 adet double değer tutar: e[0], e[1], e[2].

Aşağıdaki tam tanımlamayı projenizde vec3.h dosyası olarak kaydedin:

vec3.h
#ifndef VEC3_H
#define VEC3_H

#include <cmath>
#include <iostream>

class vec3 {
  public:
    double e[3];

    vec3() : e{0, 0, 0} {}
    vec3(double e0, double e1, double e2) : e{e0, e1, e2} {}

    double x() const { return e[0]; }
    double y() const { return e[1]; }
    double z() const { return e[2]; }

    vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
    double operator[](int i) const { return e[i]; }
    double& operator[](int i) { return e[i]; }

    vec3& operator+=(const vec3& v) {
        e[0] += v.e[0];
        e[1] += v.e[1];
        e[2] += v.e[2];
        return *this;
    }

    vec3& operator*=(double t) {
        e[0] *= t;
        e[1] *= t;
        e[2] *= t;
        return *this;
    }

    vec3& operator/=(double t) {
        return *this *= 1/t;
    }

    double length() const {
        return std::sqrt(length_squared());
    }

    double length_squared() const {
        return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
    }
};

// Tip takma adları (kodun anlamsal okunabilirliği için)
using point3 = vec3; // 3B Uzay Noktası

#endif

Vektör Geometrik İşlemleri

İki vektörün toplanması, skalerle çarpılması, nokta çarpımı (dot product) ve vektörel çarpımı (cross product) ışın izleyicinin her saniyesinde yüz binlerce kez çağrılacaktır:

vec3.h (Yardımcı Fonksiyonlar)
// Akış çıktısı: std::cout << v << std::endl;
inline std::ostream& operator<<(std::ostream& out, const vec3& v) {
    return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
}

inline vec3 operator+(const vec3& u, const vec3& v) {
    return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
}

inline vec3 operator-(const vec3& u, const vec3& v) {
    return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
}

inline vec3 operator*(const vec3& u, const vec3& v) {
    return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
}

inline vec3 operator*(double t, const vec3& v) {
    return vec3(t * v.e[0], t * v.e[1], t * v.e[2]);
}

inline vec3 operator*(const vec3& v, double t) {
    return t * v;
}

inline vec3 operator/(const vec3& v, double t) {
    return (1/t) * v;
}

// Nokta Çarpım (Dot Product): u . v = |u||v|cos(theta)
inline double dot(const vec3& u, const vec3& v) {
    return u.e[0] * v.e[0]
         + u.e[1] * v.e[1]
         + u.e[2] * v.e[2];
}

// Vektörel Çarpım (Cross Product): u x v (Her iki vektöre dik üçüncü vektör)
inline vec3 cross(const vec3& u, const vec3& v) {
    return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
                u.e[2] * v.e[0] - u.e[0] * v.e[2],
                u.e[0] * v.e[1] - u.e[1] * v.e[0]);
}

// Birim Vektör (Normalize Etme): u / |u|
inline vec3 unit_vector(const vec3& v) {
    return v / v.length();
}

Renk Yardımcıları: color.h

Renk vektörlerini PPM çıktısına dönüştürmek için temiz bir write_color fonksiyonu yazalım. color.h başlık dosyamızı oluşturalım:

color.h
#ifndef COLOR_H
#define COLOR_H

#include "vec3.h"
#include <iostream>

using color = vec3;

void write_color(std::ostream& out, const color& pixel_color) {
    auto r = pixel_color.x();
    auto g = pixel_color.y();
    auto b = pixel_color.z();

    // [0,1] aralığını [0,255] tamsayı aralığına çevir
    int rbyte = int(255.999 * r);
    int gbyte = int(255.999 * g);
    int bbyte = int(255.999 * b);

    // Renk bileşenlerini yazdır
    out << rbyte << ' ' << gbyte << ' ' << bbyte << '\n';
}

#endif

main.cc Kodunu vec3 ile Güncelleme

Şimdi ilk bölümdeki ana programımızı yeni vektör ve renk sınıflarımızla çok daha zarif bir hale getirelim:

main.cc
#include "vec3.h"
#include "color.h"
#include <iostream>

int main() {
    int image_width = 256;
    int image_height = 256;

    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_color = color(double(i)/(image_width-1), double(j)/(image_height-1), 0);
            write_color(std::cout, pixel_color);
        }
    }

    std::clog << "\rTamamlandı!                 \n";
}

Artık 3B uzayda geometri kurmak ve ışık ışınları göndermek için gereken temel matematik zırhına sahibiz!