Ana içeriğe geç

Bölüm 1: İlk Görüntü Çıktısı (PPM)

Bir render motoru yazmaya başlarken ihtiyacımız olan ilk şey, ürettiğimiz pikselleri ekranda görebileceğimiz bir resim dosyasına dönüştürmektir.

Genellikle bir PNG veya JPEG dosyası yazmak için harici bir kütüphaneye (stb_image_write, libpng vb.) ihtiyaç duyarsınız. Ancak biz işe sıfır bağımlılıkla başlamak istiyoruz. İşte bu yüzden dünyanın en basit, düz metin tabanlı görsel formatı olan PPM (Portable Pixmap) ile başlayacağız!


PPM Dosya Formatı Nedir?

PPM, ASCII metin formatında saklanan bir resim dosyasıdır. Wikipedia veya herhangi bir metin düzenleyicide açtığınızda doğrudan okunabilir.

Bir PPM dosyasının anatomisi şöyledir:

PPM Formatı Yapısı

  1. P3: Dosyanın düz metin (ASCII) renkli PPM formatında olduğunu belirten sihirli bayt.
  2. Genişlik ve Yükseklik: Görselin piksel cinsinden boyutları (örneğin 256 256).
  3. Maksimum Renk Değeri: Her bir renk kanalının alabileceği tavan değer (genellikle 255).
  4. Piksel Verileri: Soldan sağa, yukarıdan aşağıya doğru her bir pikselin Kırmızı Yeşil Mavi (R G B) tamsayı değerleri.

İlk C++ Programımız: Renk Gradyanı

Aşağıdaki program \(256 \times 256\) boyutlarında bir renk gradyanı oluşturur. Kırmızı kanal soldan sağa doğru artarken, yeşil kanal yukarıdan aşağıya doğru azalır; mavi kanal ise sabit tutulur.

main.cc
#include <iostream>

int main() {

    // Resim Boyutları
    int image_width = 256;
    int image_height = 256;

    // PPM Başlığı
    std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

    // Pikselleri Satır Satır Yaz
    for (int j = 0; j < image_height; j++) {
        for (int i = 0; i < image_width; i++) {
            auto r = double(i) / (image_width - 1);
            auto g = double(j) / (image_height - 1);
            auto b = 0.0;

            int ir = int(255.999 * r);
            int ig = int(255.999 * g);
            int ib = int(255.999 * b);

            std::cout << ir << ' ' << ig << ' ' << ib << '\n';
        }
    }
}

Kodun İncelenmesi:

  • r, g, b değişkenleri \([0.0, 1.0]\) aralığında kayan noktalı (double) değerlerdir.
  • 255.999 ile çarpıp int'e dönüştürdüğümüzde değerler tam olarak \([0, 255]\) tamsayı aralığına oturur.
  • Standart çıktı (std::cout) doğrudan terminale PPM metnini basar.

Çıktıyı Dosyaya Yönlendirme

Programınızı derleyip çalıştırdığınızda konsolunuz milyonlarca sayı ile dolup taşacaktır. Bunun yerine işletim sisteminizin çıktı yönlendirme (>) operatörünü kullanarak çıktıyı bir dosyaya aktarırız:

# Programı derleyin
g++ -O3 main.cc -o inOneWeekend

# Çıktıyı image.ppm dosyasına yazdırın
./inOneWeekend > image.ppm

Oluşan image.ppm dosyasını Photoshop, GIMP, VS Code (PPM Viewer eklentisi ile) veya web tabanlı herhangi bir PPM görüntüleyici ile açtığınızda şu güzel gradyanı göreceksiniz:

İlk PPM Çıktısı


İlerleme Göstergesi Eklemek (std::clog)

İlerleyen bölümlerde sahnemize yüzlerce ışın ve yansıma eklediğimizde render işlemi birkaç saniye (hatta dakika) sürebilir. Programın donup donmadığını anlamak için bir ilerleme çubuğu eklemek harika bir fikirdir.

Peki bu ilerleme mesajlarını nereye yazacağız? std::cout'a yazamayız; çünkü o zaman PPM dosyamız bozulur!

Çözüm: std::clog (veya std::cerr). Bu akış hata ve günlük kayıtları içindir ve > yönlendirmesinden etkilenmeyip doğrudan konsol ekranına basılır:

main.cc (İlerleme Göstergeli)
#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 tarama satırı: " << (image_height - j) << ' ' << std::flush;
        for (int i = 0; i < image_width; i++) {
            auto r = double(i) / (image_width - 1);
            auto g = double(j) / (image_height - 1);
            auto b = 0.0;

            int ir = int(255.999 * r);
            int ig = int(255.999 * g);
            int ib = int(255.999 * b);

            std::cout << ir << ' ' << ig << ' ' << ib << '\n';
        }
    }

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

Artık terminalinizde satırların geri sayımını canlı olarak görebilirsiniz. İlk görüntümüzü başarıyla ürettik; şimdi ışın izleyicimizin kalbini oluşturacak 3B Vektör Sınıfına geçelim!