Bölüm 8: Mat Materyaller (Diffuse / Lambertian)
Şimdiye kadar piksellerimizi yalnızca yüzey normallerine göre renklendirdik. Ancak gerçek dünyada mat nesneler (örneğin tebeşir, kil, ham ahşap veya mat boyalı bir duvar) kendi renklerini yaymazlar; çevrelerindeki ışıktan beslenirler ve çarpan ışığı her yöne rastgele saçar.
İşte bilgisayar grafiklerinde fotogerçekçiliğin sırrı burada yatar: Dolaylı Aydınlatma (Indirect Lighting / Global Illumination)!
Basit Bir Mat Materyal (A Simple Diffuse Material)
Mat yüzeyler ışığı doğrudan bir ayna gibi tek bir açıyla yansıtmaz. Çarpan bir ışık ışını, yüzeyin mikro pürüzlülüğü nedeniyle rastgele bir açıyla geri seker:

Peki bir ışın yüzeye çarptığında sekeceği rastgele yönü nasıl belirleyeceğiz?
En sezgisel yöntem şudur: Çarpışma noktasında yüzeye teğet duran bir birim küre hayal edin. Bu kürenin merkezi \(\mathbf{P} + \mathbf{n}\) noktasıdır (\(\mathbf{P}\) çarpışma noktası, \(\mathbf{n}\) yüzey normali).

Eğer biz bu birim kürenin içinden rastgele bir nokta \(\mathbf{S}\) seçer ve çarpışma noktasından o noktaya doğru bir ışın gönderirsek \((\mathbf{S} - \mathbf{P})\), fiziksel olarak çok inandırıcı bir saçılma elde ederiz!
Rastgele Vektör Üretimi: Reddetme Yöntemi (Rejection Method)
Bir kürenin içinden rastgele bir nokta seçmenin en basit yolu Reddetme Yöntemidir (Rejection Method): 1. \(X, Y, Z\) bileşenleri \([-1.0, +1.0]\) arasında olan rastgele bir küp içinde nokta seçilir. 2. Bu noktanın orijine olan uzaklığının karesi \(1\)'den büyükse nokta reddedilir ve yeni bir nokta seçilir. 3. Nokta birim kürenin içindeyse kabul edilir!

Bu rastgele noktayı normalize ederek birim vektör haline getirelim:
| vec3.h (Birim Vektör) | |
|---|---|
Özyinelemeli Sıçrama Derinliğini Sınırlama (Limiting Child Rays)
Bir ışın nesneye çarptığında yeni bir saçılan ışın fırlatır. O ışın gider zemine çarpar, oradan başka bir küreye seker...
Eğer ışın iki küre arasında sonsuza kadar gidip gelirse ne olur? Stack Overflow (Yığın Taşması) gerçekleşir ve programınız çöker!
Bunu engellemek için ışınlarımıza bir Maksimum Sıçrama Derinliği (max_depth) limiti koyarız (örneğin 50 veya 10 sıçrama):
Buradaki 0.5 katsayısı, mat yüzeyin her sıçramada ışığın enerjisinin %50'sini emdiğini (absorbe ettiğini) simgeler.
Gölge Aknesi (Shadow Acne) Hatası ve Çözümü
Yukarıdaki kodda çok kritik ve sinsi bir detay bulunur:
Bilgisayarlar reel sayıları sınırlı bir kayan noktalı sayı (floating point) hassasiyetiyle saklar. Bir ışın \(t = 1.000000\) noktasında küreye çarptığında, bir sonraki saçılan ışın matematiksel olarak tam o yüzeyden başlamalıdır.
Fakat yuvarlama hataları yüzünden fırlatılan yeni ışının başlangıç noktası bazen küre yüzeyinin hemen altında (\(t = -0.000001\)) kalır! Bu durumda yeni ışın henüz doğar doğmaz kendi çıktığı küreye çarpar!
Sonuç: Yüzeyin üzerinde çirkin siyah benekler, dalgalanmalar ve kirli gölgeler (Shadow Acne / Gölge Aknesi)!
Aralığın alt sınırını 0.0 yerine 0.001 yaparak ışının yüzeyden çok azıcık ileriden başlamasını sağlar ve bu sinsi hatayı tamamen ortadan kaldırırız:

Gerçek Lambertian Yansıma (True Lambertian Reflection)
Yukarıda kullandığımız rastgele nokta seçimi pratik bir yaklaşımdı. Ancak fiziksel olarak kusursuz bir mat yüzey (Johann Heinrich Lambert'in 1760'ta tanımladığı Lambertian Dağılımı), ışığı yüzey normali yönünde \(\cos(\theta)\) olasılığıyla daha yoğun saçmalıdır.
Bunu sağlamak için yüzeye teğet duran birim kürenin yüzeyinden rastgele bir birim vektör seçeriz:

Bu formül, ışınların yüzey normaline yakın doğrultularda toplanmasını, yüzeye teğet (sıyırıp geçen) açılarda ise seyrekleşmesini kusursuz bir şekilde sağlar.
Gama Düzeltmesi (Gamma Correction)
Mat küremizi bu formülle render ettiğimizde görüntünün gereğinden belirgin şekilde karanlık olduğunu fark edeceksiniz:

Bunun sebebi ışın izleyicimizin hatası değildir! Suçlu, bilgisayar monitörlerimizin çalışma prensibidir.
İnsan gözü karanlık tonlardaki değişimleri aydınlık tonlardakine göre çok daha hassas algılar. Bu yüzden modern monitörler gelen sinyalleri doğrusal (linear) olarak göstermez; \(V^{2.2}\) şeklinde bir üstel eğriye (gamma curve) tabi tutar.
Eğer biz monitöre doğrudan doğrusal fiziksel ışık değerlerini (\([0.0, 1.0]\)) gönderirsek, monitör bu değerlerin karesini alarak her şeyi olması gerekenden çok daha karanlık gösterir!
Görüntünün insan gözüne doğru görünmesi için, pikselleri dosyaya yazmadan önce renk değerlerinin tersini alarak Gama 2.0 düzeltmesi (karekökünü alma) uygularız:
Sonuç: Kürenin altındaki temas gölgesi (ambient occlusion), gökyüzünden seken yumuşacık dolaylı ışıklar ve gerçekçi renk tonları!
