Işıklandırma Eşlemeleri

Önceki bölümde her nesnenin ışığa farklı tepki veren kendine özgü bir malzemeye sahip olma olasılığını tartışmıştık. Bu, her nesneye diğer nesnelerle karşılaştırıldığında benzersiz bir görünüm kazandırmak açısından harikadır, ancak yine de bir nesnenin görsel çıktısı konusunda fazla esneklik sunmaz.

Önceki bölümde bir bütün olarak nesnenin tamamı için bir malzeme tanımladık. Ancak gerçek dünyadaki nesneler genellikle tek bir malzemeden değil, birden fazla malzemeden oluşur. Bir araba düşünün: Dış kısmı parlak bir maddeden oluşuyor, çevreyi kısmen yansıtan pencereleri var, lastikleri neredeyse parlak olduğundan aynasal vurgulara sahip değiller ve süper parlak jantları var (eğer gerçekten yıkadıysanız). Araç ayrıca nesnenin tamamı için aynı olmayan dağınık ve ortam renklerine de sahiptir; bir araba birçok farklı ortam/yaygın renk görüntüler. Sonuçta böyle bir nesne, farklı parçalarının her biri için farklı malzeme özelliklerine sahiptir.

Yani önceki bölümdeki malzeme sistemi en basit modeller dışında hepsi için yeterli değildir, dolayısıyla dağınık ve aynasal eşlemeler sunarak sistemi genişletmemiz gerekir. Bunlar, bir nesnenin dağınık (ve zaten aynı olması gerektiği için dolaylı olarak ortam bileşenini) ve aynasal bileşenini çok daha hassas bir şekilde etkilememize olanak tanır.

Dağınık Eşlemeler

İstediğimiz şey, her bir fragment için bir nesnenin dağınık renklerini ayarlamanın bir yoludur. Fragment'in nesne üzerindeki konumuna göre renk değerini alabileceğimiz bir tür sistem var mı?

Muhtemelen bunların hepsi size tanıdık gelecektir ve bir süredir böyle bir sistemi kullanıyoruz. Bu, önceki bölümlerden birinde kapsamlı bir şekilde tartıştığımız texture'lara benziyor ve temelde tam da budur: bir texture. Sadece aynı temel prensip için farklı bir isim kullanıyoruz: fragment başına benzersiz renk değerleri için indeksleyebileceğimiz bir nesnenin etrafına sarılmış bir görüntü kullanmak. Işıklandırılmış sahnelerde buna genellikle dağınık eşleme adı verilir (PBR'den önce 3B sanatçıları genellikle bu şekilde adlandırır), çünkü bir texture görünüsü nesnenin tüm dağınık renklerini temsil eder.

Dağınık eşlemeleri göstermek için çelik kenarlı ahşap bir konteynerin aşağıdaki görüntüsünü kullanacağız:

Shader'larda dağınık eşleme kullanmak tam olarak texture bölümünde gösterdiğimiz gibidir. Ancak bu sefer texture Material yapısı içinde sampler2D olarak saklıyoruz. Daha önce tanımlanan vec3 dağınık renk vektörünü dağınık eşleme ile değiştiriyoruz.

Sampler2D'nin opak tür olarak adlandırılan bir tür olduğunu unutmayın; bu türleri somutlaştıramayacağımız, yalnızca uniform olarak tanımlayacağımız anlamına gelmektedir. Yapı tek tip (işlev parametresi gibi) dışında bir şekilde başlatılırsa GLSL tuhaf hatalara neden olabilir; aynı durum bu tür opak türleri barındıran herhangi bir yapı için de geçerlidir.

Ayrıca ortam materyali renk vektörünü de kaldırıyoruz çünkü ortam rengi zaten dağınık renge eşit olduğundan artık ortamı ışıkla kontrol ediyoruz. Yani ayrı olarak saklamanıza gerek yok:

struct Material {
    sampler2D diffuse;
    vec3      specular;
    float     shininess;
}; 
...
in vec2 TexCoords;

Biraz inatçıysanız ve yine de ortam renklerini farklı bir değere (dağınık değer dışında) ayarlamak istiyorsanız ambient vec3'ü koruyabilirsiniz, ancak bu durumda ortam renkleri nesnenin tamamı için halen aynı kalacaktır. Her parça için farklı ortam değerleri elde etmek amacıyla yalnızca ortam değerleri için başka bir doku kullanmanız gerekir.

Fragment shader'da doku koordinatlarına tekrar ihtiyacımız olacağını unutmayın, bu nedenle fazladan bir giriş değişkeni tanımladık. Daha sonra fragment'in dağınık renk değerini elde etmek için dokudan örnek alıyoruz:

vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));  

Ayrıca ortam materyalinin rengini de dağınık materyalin rengine eşit olarak ayarlamayı unutmayın:

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

Dağınık bir eşleme kullanmak için gereken tek şey budur. Gördüğünüz gibi yeni bir şey değil ama görsel kalitede ciddi bir artış sağlıyor. Çalıştırmak için vertex verilerini doku koordinatlarıyla güncellememiz, bunları vertex attribute olarak fragment shader'a aktarmamız, dokuyu yüklememiz ve dokuyu uygun doku birimine bağlamamız gerekiyor.

Güncellenmiş vertex verilerini burada bulabilirsiniz. Vertex verileri artık küpün köşe noktalarının her biri için vertex konumlarını, normal vektörleri ve doku koordinatlarını içeriyor. Doku koordinatlarını vertex attribute olarak kabul edecek ve bunları fragment shader'a iletecek şekilde vertex shader'ı güncelleyelim:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
...
out vec2 TexCoords;

void main()
{
    ...
    TexCoords = aTexCoords;
} 

Her iki VAO'nun vertex attribute pointer'larını yeni vertex verileriyle eşleşecek şekilde güncellediğinizden ve konteyner görüntüsünü doku olarak yüklediğinizden emin olun. Küpü oluşturmadan önce, material.diffuse tek tip örnekleyiciye doğru doku birimini atamak ve konteyner dokusunu bu doku birimine bağlamak istiyoruz:

lightingShader.setInt("material.diffuse", 0);
...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);

Şimdi dağınık bir eşleme kullanarak yine ayrıntılarda muazzam bir artış elde ediyoruz ve bu sefer konteyner gerçekten parlamaya başlıyor (tam anlamıyla). Konteyneriniz artık muhtemelen şuna benziyordur:

Uygulamanın tam kaynak kodunu burada bulabilirsiniz.

Specular maps

Muhtemelen aynasal vurgunun biraz tuhaf göründüğünü fark etmişsinizdir, çünkü nesne çoğunlukla ahşaptan oluşan bir konteyner ve ahşapta bu tür aynasal vurgular olmaz. Nesnenin aynasal materyalini vec3(0.0)'e ayarlayarak bunu düzeltebiliriz, ancak konteynerin çelik kenarlarının da aynasal vurgular göstermeyi bırakacağı anlamına gelir ki çeliğin aynasal vurgular göstermesi gerekmektedir. Nesnenin hangi bölümlerinin değişen yoğunlukta aynasal bir vurgu göstermesi gerektiğini kontrol etmek istiyoruz. Bu tanıdık gelen bir sorundur. Tesadüf mü? Bence değil.

Ayrıca yalnızca aynasal vurgular için bir doku eşlemesi de kullanabiliriz. Bu, nesnenin her bir fragment'inin aynasal yoğunluğunu tanımlayan siyah beyaz (veya isterseniz renkli) bir doku oluşturmamız gerektiği anlamına gelir. Aynasal eşlemenin bir örneği aşağıdaki görüntüdür:

Aynasal vurgunun yoğunluğu görüntüdeki her pikselin parlaklığından gelir. Aynasal eşlemenin her pikseli, örneğin siyahın vec3(0.0) renk vektörünü ve grinin de vec3(0.5) renk vektörünü temsil ettiği bir renk vektörü olarak görüntülenebilir. Fragment shader'da daha sonra karşılık gelen renk değerini örnekliyoruz ve bu değeri ışığın aynasal yoğunluğuyla çarpıyoruz. Bir piksel ne kadar 'beyaz' olursa çarpmanın sonucu o kadar yüksek olur ve dolayısıyla bir nesnenin aynasal bileşeni o kadar parlak olur.

Konteynerin çoğunlukla ahşaptan oluşması ve bir malzeme olarak ahşabın yansıtıcı vurgulara sahip olmaması gerektiğinden, dağınık dokunun tüm ahşap bölümü siyaha dönüştürüldü: siyah bölümlerde herhangi bir yansıtıcı vurgu bulunmuyor. Konteynerin çelik kenarı değişen aynasal yoğunluklara sahiptir; çeliğin kendisi aynasal vurgulara nispeten duyarlı iken çatlaklar değildir.

Teknik olarak ahşabın ayrıca çok daha düşük bir parlaklık değerine (daha fazla ışık saçılımı) ve daha az etkiye sahip olmasına rağmen aynasal vurguları vardır, ancak öğrenme amacıyla ahşabın aynasal ışığa herhangi bir tepki vermediğini varsayabiliriz.

Photoshop veya Gimp gibi araçları kullanarak, bazı parçaları keserek, siyah beyaza dönüştürerek ve parlaklık/kontrast artırarak dağınık bir dokuyu bunun gibi aynasal bir görüntüye dönüştürmek nispeten kolaydır.

Sampling specular maps

Aynasal bir eşleme tıpkı diğer dokular gibidir, dolayısıyla kodu da dağınık eşleme koduna benzer. Görüntüyü düzgün şekilde yüklediğinizden ve bir doku nesnesi oluşturduğunuzdan emin olun. Aynı fragment shader'da başka bir doku örnekleyici kullandığımızdan, aynasal eşleme için farklı bir doku birimi kullanmamız gerekiyor (Dokular'a bakın), bu yüzden işlemeden önce onu uygun doku birimine bağlayalım:

lightingShader.setInt("material.specular", 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap); 

Daha sonra fragment shader'ın materyal özelliklerini, aynasal bileşen olarak vec3 yerine sampler2D'yi kabul edecek şekilde güncelleyin:

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};  

Ve son olarak fragment'in ilgili aynasal yoğunluğunu elde etmek için aynasal eşlemeyi örneklemek istiyoruz:

vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
FragColor = vec4(ambient + diffuse + specular, 1.0); 

Aynasal bir eşleme kullanarak, bir nesnenin hangi parçalarının parlak özelliklere sahip olduğunu muazzam ayrıntılarla belirleyebilir ve hatta buna karşılık gelen yoğunluğu bile kontrol edebiliriz. Aynasal eşlemeler, dağınık eşlemenin ışıklandırma üzerindeki ek bir kontrol katmanını sağlar.

Ana akıma uyumlu olmak istemiyorsanız, yalnızca her fragment'in aynasal yoğunluğunu değil, aynı zamanda aynasal vurgunun rengini de ayarlamak için aynasal haritadaki gerçek renkleri de kullanabilirsiniz. Ancak gerçekçi olmak gerekirse, aynasal vurgunun rengi çoğunlukla ışık kaynağının kendisi tarafından belirlenir, dolayısıyla gerçekçi görseller oluşturmaz (bu nedenle görüntüler genellikle siyah beyazdır; biz yalnızca yoğunluğu önemsiyoruz).

Şimdi uygulamayı çalıştırırsanız, konteynerin malzemesinin artık çelik çerçeveli gerçek ahşap konteynere çok benzediğini açıkça görebilirsiniz:

Uygulamanın tam kaynak kodunu burada bulabilirsiniz.

Dağınık ve aynasal eşlemeler kullanarak, nispeten basit nesnelere gerçekten muazzam miktarda ayrıntı ekleyebiliriz. Hatta normal/bump eşlemeleri ve/veya yansıma eşlemeleri gibi diğer doku eşlemelerini kullanarak nesnelere daha fazla ayrıntı da ekleyebiliriz, ancak bu daha sonraki bölümlerde işleyeceğimiz bir şeydir. Konteynerinizi tüm arkadaşlarınıza ve ailenize gösterin ve konteynerimizin bir gün olduğundan daha da güzel olabileceği gerçeğinden memnun olun!

Alıştırmalar

  • Işık kaynağının ortamsal, dağınık ve aynasal vektörleriyle eğlenin ve bunların konteynerin görsel çıktısını nasıl etkilediğini görün.

  • Fragment shader'daki aynasal eşlemenin renk değerlerini ters çevirmeyi deneyin, böylece ahşap aynasal vurgular gösterir ve çelik kenarlıklar göstermez (çelik kenardaki çatlaklar nedeniyle kenarlıkların daha az yoğunlukta olsa da hala bir miktar aynasal vurgu gösterdiğini unutmayın) : çözüm.

  • Siyah ve beyaz yerine gerçek renkleri kullanan dağınık dokudan aynasal bir eşleme oluşturmayı deneyin ve sonucun çok gerçekçi görünmediğini görün. Kendiniz oluşturamıyorsanız bu renkli aynasal eşlemeyi kullanabilirsiniz: sonuç.

  • Ayrıca, fragment başına emisyon değerlerini saklayan bir doku olan emisyon eşlemesi adını verdikleri bir şeyi de ekleyin. Emisyon değerleri, bir nesnenin sanki kendisi bir ışık kaynağı içeriyormuş gibi yayabileceği renklerdir; bu şekilde bir nesne ışık koşullarından bağımsız olarak parlayabilir. Emisyon eşlemeleri genellikle bir oyundaki nesneler parladığında (bir robotun gözleri veya bir kabın üzerindeki ışık şeritleri gibi) gördüğünüz şeylerdir. Aşağıdaki dokuyu (creativesam tarafından) bir emisyon eşlemesi olarak, sanki harfler ışık saçıyormuş gibi konteynerin üzerine ekleyin: çözüm; sonuç.

Orijinal Kaynak: Lighting Maps

Çeviri: Nezihe Sözen

Last updated