Işıklandırma

Önceki bölümdearrow-up-right, gerçekçi bir fizik tabanlı renderer kurmak için gereken temelleri attık. Bu bölümde, tartıştığımız teoriyi; nokta ışıklar, yönsel ışıklar ve/veya spot ışıkları gibi doğrudan (analitik) ışık kaynaklarını kullanan gerçek bir renderer'a dönüştürmeye odaklanacağız.

Önceki bölümdeki yansıma denklemini (reflectance equation) yeniden ele alalım:

Lo(p,ωo)=Ω(kdcπ+DFG4(ωon)(ωin))Li(p,ωi)nωidωiL_o(p,\omega_o) = \int\limits_{\Omega} \left(k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}\right) L_i(p,\omega_i)\, n \cdot \omega_i\, d\omega_i

Artık bu denklemin büyük bölümünü anlıyoruz; ancak hâlâ belirsizliğini koruyan soru şudur: Sahnenin toplam radyansını (L) nasıl temsil edeceğiz?

Radyans (L), bilgisayar grafiğinde yorumlandığı haliyle, bir ışık kaynağının belirli bir katı açı (ω) üzerindeki radyan akısını (φ) veya ışık enerjisini ölçer. Bizim durumumuzda katı açının sonsuz küçük olduğunu varsayıyoruz; bu durumda radyans, bir ışık kaynağının tek bir ışın yönündeki akısını ölçer.

Bu bilgiyi önceki bölümlerden öğrendiğimiz ışıklandırma bilgisiyle nasıl ilişkilendiririz? RGB değeri (23.47, 21.31, 20.79) olan, her yöne eşit parlaklıkta ışık yayan tek bir nokta ışığımız olduğunu düşünelim. Bu ışığın radyan yoğunluğu, tüm giden yön ışınlarındaki radyan akısına eşittir. Ancak belirli bir p noktasını gölgelerken, yüzeyin yarım küresi (Ω) üzerindeki olası gelen ışık yönlerinden yalnızca biri olan wᵢ, doğrudan bu nokta ışıktan gelir. Sahnede yalnızca tek bir ışık kaynağı olduğunu veya tek bir nokta olduğunu varsaydığımızdan, p noktasında sıfır olmayan radyans yalnızca bu bir ışın üzerindedir:

Nokta ışık kaynağında radyans

Başlangıçta ışık zayıflamasının (ışığın mesafeye göre kararması) nokta ışığı etkilemediğini varsayarsak, gelen ışının radyansı ışığı nereye yerleştirirsek yerleştirelim aynı kalır; yalnızca geliş açısı cos θ ile ölçeklenir. Çünkü nokta ışık hangi açıdan bakılırsa bakılsın aynı radyan yoğunluğuna sahiptir ve radyan yoğunluğunu sabit bir vektör ((23.47, 21.31, 20.79)) olarak modellemektedir.

Ancak radyans, girdi olarak p konumunu da alır ve herhangi bir gerçekçi nokta ışık zayıflamayı hesaba kattığından, radyan yoğunluğu p noktasıyla ışık kaynağı arasındaki mesafeye göre ölçeklenir. Ardından, orijinal radyans denkleminden çıkarıldığı üzere sonuç, yüzey normali n ile gelen ışın yönü wᵢ arasındaki nokta çarpımıyla da ölçeklenir.

Daha pratik bir ifadeyle: doğrudan bir nokta ışığı için radyans fonksiyonu L, ışık rengini ölçer, p'ye olan mesafeye göre zayıflatır ve n · wᵢ ile ölçekler — ancak yalnızca p'ye çarpan tek bir wᵢ ışın vektörü üzerinden. Kodda bu şöyle ifade edilir:

Farklı terminoloji bir yana, bu kod parçası size son derece tanıdık gelmeli: Şimdiye kadar diffuse ışıklandırma yaparken tam olarak bunu yapıyorduk. Doğrudan ışıklandırmada radyans, yüzeyin radyansına yalnızca tek bir ışık yönü katkıda bulunacağından, önceki hesaplamalarımızla aynı şekilde hesaplanır.

Not: Bu varsayım, nokta ışıkların uzayda sonsuz küçük ve tek bir nokta olduğu durumda geçerlidir. Alan veya hacme sahip bir ışık (area light) modellenecek olsaydı, radyans birden fazla gelen ışın yönünde sıfır olmazdı.

Tek bir noktadan kaynaklanan diğer ışık türleri için de radyansı benzer şekilde hesaplarız. Örneğin, yönsel bir ışık kaynağının (directional light) zayıflama faktörü olmaksızın sabit bir wᵢ vektörü vardır. Spot ışığının ise sabit bir radyan yoğunluğu olmayıp, ileri yön vektörüne göre ölçeklenen bir yoğunluğu vardır.

Bu da bizi yüzeyin yarım küresi (Ω) üzerindeki integral (∫) işaretine geri götürür. Tek bir yüzey noktasını gölgelerken katkıda bulunan tüm ışık kaynaklarının konumlarını önceden bildiğimizden, integrali sayısal olarak çözmemize gerek yoktur. Katkıda bulunan ışık sayısını (bilinen sayı) doğrudan hesaplayabilir ve toplam iradyansı bulabiliriz. Bu, PBR'ı doğrudan ışık kaynaklarında oldukça basitleştirir: pratikte yalnızca katkıda bulunan ışıklar üzerinde döngü kurmamız yeterlidir. IBLarrow-up-right bölümlerinde çevre ışıklandırmasını hesaba kattığımızda ise ışık her yönden gelebileceğinden integrali gerçekten çözmemiz gerekecektir.


PBR Yüzey Modeli

Önce PBR modellerini uygulayan bir fragment shader yazalım. İlk olarak gerekli PBR girdilerini alıyoruz:

Standart girdileri jenerik bir vertex shader'dan ve nesnenin yüzeyi üzerindeki sabit malzeme özelliklerinden alıyoruz.

Ardından fragment shader'ın başlangıcında her ışıklandırma algoritmasında olduğu gibi gerekli hesaplamaları yapıyoruz:

Doğrudan Işıklandırma

Bu bölümün örnek demosunda toplam 4 nokta ışık var ve bunlar birlikte sahnenin iradyansını temsil ediyor. Yansıma denklemini karşılamak için her ışık kaynağı üzerinde döngü kurarak her birinin radyan katkısını hesaplıyor, BRDF ve geliş açısıyla ölçeklendirip topluyoruz. Bu döngüyü, doğrudan ışık kaynakları için Ω üzerindeki integrali sayısal olarak çözmek gibi düşünebiliriz. İlk olarak her ışık için ilgili değişkenleri hesaplıyoruz:

Hesaplamalar linear uzayda yapıldığından (shader'ın sonunda gamma düzeltmesiarrow-up-right uygulayacağız), ışık kaynaklarını fiziksel olarak daha doğru olan ters kare yasasıyla (inverse-square law) zayıflatıyoruz.

Fiziksel olarak doğru olmasa da, sabit-doğrusal-kuadratik zayıflama denklemini kullanmak isteyebilirsiniz; bu denklem ışığın enerji düşüşü üzerinde çok daha fazla kontrol sağlar.

Ardından her ışık için tam Cook-Torrance specular BRDF terimini hesaplamak istiyoruz:

DFG4(ωon)(ωin)\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}

Yapmak istediğimiz ilk şey specular ile diffuse yansıma arasındaki oranı hesaplamaktır; yani yüzeyin ışığı ne kadar yansıttığına karşın ne kadar kırdığını bulmaktır. Önceki bölümdenarrow-up-right bildiğimiz gibi Fresnel denklemi tam olarak bunu hesaplar (clamp kullandığımıza dikkat edin — siyah noktaları önlemek için):

Fresnel-Schlick yaklaşımı, sıfır açısında yüzey yansıması olarak bilinen bir F0 parametresi bekler. Bu değer, yüzeye doğrudan bakıldığında malzemenin ne kadar yansıttığını temsil eder. F0 malzemeye göre değişir ve büyük malzeme veritabanlarında bulunabilir. PBR metallic workflow'da çoğu dielektrik yüzeyin F0 = 0.04 ile görsel olarak doğru sonuç verdiği basitleştirmesini yapıyoruz; metalik yüzeyler için ise F0 albedo değeriyle belirlenir. Kod olarak:

Gördüğünüz gibi metalik olmayan yüzeyler için F0 her zaman 0.04'tür. Metalik yüzeyler için ise metallic özelliğine bağlı olarak orijinal F0 ile albedo arasında doğrusal interpolasyon yapılır.

F elde edildikten sonra kalan terimler Normal Dağılım Fonksiyonu (D) ve Geometri Fonksiyonu (G)'dir.

Doğrudan PBR ışıklandırma shader'ındaki kod karşılıkları:

Burada önemli bir nokta: Teori bölümününarrow-up-right aksine, roughness parametresini doğrudan bu fonksiyonlara geçiriyoruz; böylece orijinal roughness değeri üzerinde terime özgü değişiklikler yapabiliyoruz. Disney'in gözlemlerine ve Epic Games'in benimsemesine göre, geometry ve NDF fonksiyonlarında roughness'ı kare alarak kullanmak ışıklamayı görsel olarak daha doğru hale getiriyor.

Her iki fonksiyon da tanımlandığına göre yansıma döngüsü içinde NDF ve G terimini hesaplamak artık kolaydır:

Bu Cook-Torrance BRDF'yi hesaplamak için yeterlidir:

Herhangi bir nokta çarpımının 0.0 olması durumunda sıfıra bölünmeyi önlemek için paydaya 0.0001 ekliyoruz.

Artık her ışığın yansıma denklemine olan katkısını hesaplayabiliriz. Fresnel değeri doğrudan kS'e karşılık geldiğinden, F değişkenini yüzeye çarpan herhangi bir ışığın specular katkısını göstermek için kullanabiliriz. kS'ten kırılma oranını kD olarak hesaplayabiliriz:

kS yansıyan enerjiyi temsil ettiğinden, kalan ışık enerjisi oranı kD olur. Ayrıca metalik yüzeyler ışığı kırmadığından ve dolayısıyla diffuse yansıması olmadığından, yüzey metalikse kD'yi sıfıra indiriyoruz. Bu sayede her ışığın giden radyans değerini hesaplamak için gereken son verilere sahibiz:

Elde edilen Lo değeri ya da giden radyans, pratikte yansıma denkleminin Ω üzerindeki integrali (∫) için bir yaklaşımdır. Tam olarak katkıda bulunan 4 gelen ışın yönünü bildiğimizden integrali çözmeye gerek yoktur; bu 4 ışın yönü üzerinden, yani sahnedeki ışık sayısı kadar doğrudan döngü kurabiliriz.

Geriye kalan, doğrudan ışıklandırma sonucu Lo'ya (hazır olmayan) bir ambient terimi ekleyerek fragment'ın son renklendirilmiş rengini bulmaktır:

Linear ve HDR Rendering

Tüm hesaplamalarımızın linear renk uzayında yapıldığını varsaydık; bunu hesaba katmak için shader'ın sonunda gamma düzeltmesiarrow-up-right yapmamız gerekiyor. Linear uzayda ışıklandırma hesaplaması PBR için son derece önemlidir — tüm girdilerin linear olması zorunludur, aksi hâlde ışıklandırma yanlış çıkar. Buna ek olarak, ışık girdilerinin fiziksel karşılıklarına yakın olmasını istiyoruz; bu nedenle radyans veya renk değerleri geniş bir aralıkta büyük değerlere ulaşabilir. Sonuç olarak Lo, LDR (düşük dinamik aralık) çıktısı nedeniyle 0.0 ile 1.0 arasına sıkışmadan önce hızla büyük değerlere ulaşabilir. Bunu düzeltmek için gamma düzeltmesinden önce Lo'yu ton eşleme (tonemapping) ya da pozlama eşlemesiyle HDRarrow-up-right değerini LDR'ye doğru şekilde dönüştürüyoruz:

Burada HDR rengi, olası yüksek dinamik aralıklı iradyansı koruyarak Reinhard operatörüyle ton eşliyoruz; ardından gamma düzeltmesini uyguluyoruz. Ayrı bir framebuffer veya post-processing aşamamız olmadığından, her iki adımı doğrudan ileri fragment shader'ın sonunda uygulayabiliyoruz.

Linear ve HDR rendering karşılaştırması

Linear renk uzayını ve HDR'yi hesaba katmak PBR pipeline'ında son derece önemlidir. Bu adımlar olmadan, farklı ışık yoğunluklarının yüksek ve düşük detaylarını doğru yakalamak mümkün değildir; hesaplamalar yanlış ve dolayısıyla görsel olarak tatmin edici olmayan sonuçlar üretir.

Tam Doğrudan Işıklandırma PBR Shader'ı

Geriye kalan tek şey, son ton eşlenmiş ve gamma düzeltilmiş rengi fragment shader'ın çıkış kanalına geçirmektir. İşte doğrudan PBR ışıklandırma shader'ının tamamı:

Umarım Teoriarrow-up-right bölümündeki bilgiler ve yansıma denklemi üzerine edinilenler sayesinde bu shader artık o kadar da korkutucu gelmiyordur. Bu shader'ı, 4 nokta ışık ve metallic ile roughness değerlerini sırasıyla dikey ve yatay eksende değiştirdiğimiz bir küre seti ile kullanırsak şöyle bir sonuç elde ederiz:

PBR küreleri — roughness ve metallic değerleri değiştirilerek

Aşağıdan yukarıya metallic değeri 0.0'dan 1.0'a, soldan sağa roughness değeri 0.0'dan 1.0'a artmaktadır. Bu iki basit parametreyi değiştirerek bile birbirinden farklı geniş bir malzeme yelpazesi gösterebildiğimize dikkat edin.

Demo'nun tam kaynak kodunu buradanarrow-up-right bulabilirsiniz.


Dokulu PBR (Textured PBR)

Sistemi uniform değerler yerine dokuları (texture) kabul edecek şekilde genişletmek, yüzey malzemesinin özellikleri üzerinde fragment başına kontrol sahibi olmamızı sağlar:

Sanatçılardan gelen albedo dokularının genellikle sRGB uzayında oluşturulduğunu; bu nedenle ışıklandırma hesaplamalarında kullanmadan önce bunları pow(..., 2.2) ifadesiyle linear alana dönüştürmemiz gerektiğini unutmayın. Ambient occlusion haritaları için kullanılan sisteme bağlı olarak bunları da sRGB'den linear alana dönüştürmeniz gerekebilir. Metallic ve roughness haritaları ise neredeyse her zaman linear uzayda oluşturulur.

Önceki küre setinin malzeme özelliklerini dokularla değiştirmek, daha önce kullandığımız ışıklandırma algoritmalarına kıyasla büyük bir görsel iyileşme sağlar:

Dokulu PBR küreleri

Dokulu demonun tam kaynak kodunu buradanarrow-up-right ve kullanılan doku setini buradanarrow-up-right bulabilirsiniz (beyaz bir ao haritasıyla birlikte). Metalik yüzeylerin doğrudan ışıklandırma ortamlarında çok karanlık görünme eğiliminde olduğunu unutmayın; diffuse yansıması olmadığından bu beklenen bir durumdur. Çevre specular ambient ışıklandırması hesaba katıldığında çok daha doğru görüneceklerdir; buna bir sonraki bölümlerde odaklanacağız.

Dışarıda gördüğünüz bazı PBR render demolarıyla kıyaslandığında bu görsel sonuç o kadar etkileyici olmayabilir — henüz görüntü tabanlı ışıklandırma (IBL)arrow-up-right entegre etmedik. Bununla birlikte, sahip olduğumuz sistem gerçek anlamda fizik tabanlı bir renderer'dır ve IBL olmaksızın bile ışıklandırmanın çok daha gerçekçi göründüğünü fark edeceksiniz.


Orijinal kaynak: LearnOpenGL – PBR/Lightingarrow-up-right Türkçe çeviri: Nezihe Sözen

Last updated