Babylon.js ile Shader Oluşturma
Yayınlanan: 2022-03-10GPU'nuzun ham gücünü serbest bırakmak istiyorsanız, gölgelendiriciler önemli bir kavramdır. Babylon.js sayesinde nasıl çalıştıklarını anlamanıza ve hatta içsel güçlerini kolay bir şekilde denemenize yardımcı olacağım.
O nasıl çalışır?
Denemeden önce, işlerin dahili olarak nasıl çalıştığını görmeliyiz.
Donanım hızlandırmalı 3D ile uğraşırken iki CPU ile uğraşmanız gerekecek: ana CPU ve GPU. GPU, bir tür son derece uzmanlaşmış CPU'dur.
SmashingMag'de Daha Fazla Okuma :
- Babylon.js ile Platformlar Arası Bir WebGL Oyunu Oluşturma
- Web Oyunlarında Gamepad API'sini Kullanma
- Poligonal Modellemeye Giriş ve Three.js
- Duyarlı 8-Bit Davul Makinesi Nasıl Oluşturulur
GPU, CPU'yu kullanarak kurduğunuz bir durum makinesidir. Örneğin, CPU, GPU'yu üçgenler yerine çizgiler oluşturacak şekilde yapılandıracaktır; şeffaflığın açık olup olmadığını belirleyecektir; ve bunun gibi.
Tüm durumlar ayarlandıktan sonra, CPU neyin oluşturulacağını tanımlayabilir: geometri.
Geometri şunlardan oluşur:
- köşe adı verilen ve köşe arabelleği adı verilen bir dizide saklanan noktaların bir listesi,
- dizin arabelleği adlı bir dizide depolanan yüzleri (veya üçgenleri) tanımlayan dizinlerin listesi.
CPU için son adım, geometrinin nasıl oluşturulacağını tanımlamaktır; bu görev için CPU, GPU'daki gölgelendiricileri tanımlayacaktır. Gölgelendiriciler, GPU'nun oluşturması gereken her bir köşe ve piksel için yürüteceği kod parçalarıdır. (Bir köşe - veya birkaç tane olduğunda köşeler - 3B'de bir "nokta"dır).
İki tür gölgelendirici vardır: köşe gölgelendiricileri ve piksel (veya parça) gölgelendiricileri.
grafik ardışık düzeni
Gölgelendiricilere girmeden önce, geri adım atalım. GPU, pikselleri oluşturmak için CPU tarafından tanımlanan geometriyi alacak ve aşağıdakileri yapacaktır:
- İndeks arabelleği kullanılarak, bir üçgeni tanımlamak için üç köşe toplanır.
- Dizin arabelleği, köşe dizinlerinin bir listesini içerir. Bu, dizin arabelleğindeki her girişin, tepe arabelleğindeki bir tepe noktasının numarası olduğu anlamına gelir.
- Bu, köşeleri çoğaltmaktan kaçınmak için gerçekten yararlıdır.
Örneğin, aşağıdaki dizin arabelleği iki yüzün bir listesidir: [1 2 3 1 3 4]. İlk yüz köşe 1, köşe 2 ve köşe 3'ü içerir. İkinci yüz köşe 1, köşe 3 ve köşe 4'ü içerir. Yani, bu geometride dört köşe vardır:

Köşe gölgelendiricisi, üçgenin her bir köşesine uygulanır. Köşe gölgelendiricisinin birincil amacı, her köşe için bir piksel üretmektir (3B tepe noktasının 2B ekranındaki projeksiyon):

Bu üç pikseli (ekranda bir 2B üçgen tanımlayan) kullanarak GPU, piksele eklenen tüm değerleri (en azından konumlarını) enterpolasyon yapacak ve piksel gölgelendirici, 2B üçgene dahil edilen her piksele uygulanacaktır. her piksel için bir renk oluşturun:

Bu işlem, dizin arabelleği tarafından tanımlanan her yüz için yapılır.
Açıkçası, paralel doğası nedeniyle, GPU bu adımı aynı anda birçok yüz için işleyebilir ve gerçekten iyi bir performans elde edebilir.
GLSL
Az önce üçgenleri oluşturmak için GPU'nun iki gölgelendiriciye ihtiyacı olduğunu gördük: köşe gölgelendirici ve piksel gölgelendirici. Bu gölgelendiriciler, Graphics Library Shader Language (GLSL) adlı bir dilde yazılmıştır. C'ye benziyor.
İşte ortak bir köşe gölgelendiricisinin bir örneği:
precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }
Vertex Shader Yapısı
Bir köşe gölgelendiricisi şunları içerir:
- Özellikler Nitelik, bir tepe noktasının bir bölümünü tanımlar. Varsayılan olarak, bir köşe en azından bir konum içermelidir (a
vector3:x, y, z
). Ancak geliştirici olarak daha fazla bilgi eklemeye karar verebilirsiniz. Örneğin, önceki gölgelendiricide,uv
adında birvector2
vardır (yani, bir 3B nesneye 2B doku uygulamanıza izin veren doku koordinatları). - Üniformalar . Üniforma, gölgelendirici tarafından kullanılan ve CPU tarafından tanımlanan bir değişkendir. Burada sahip olduğumuz tek biçim, tepe noktasının (x, y, z) konumunu ekrana (x, y) yansıtmak için kullanılan bir matristir.
- Değişen . Değişken değişkenler, tepe gölgelendirici tarafından oluşturulan ve piksel gölgelendiriciye iletilen değerlerdir. Burada, köşe gölgelendirici, piksel gölgelendiriciye bir
vUV
(basit biruv
kopyası) değeri iletir. Bu, bir pikselin burada bir konum ve doku koordinatlarıyla tanımlandığı anlamına gelir. Bu değerler GPU tarafından enterpole edilecek ve piksel gölgelendirici tarafından kullanılacaktır. - ana .
main
adlı işlev, GPU tarafından her tepe noktası için yürütülen koddur ve en azındangl_position
(geçerli tepe noktasının ekrandaki konumu) için bir değer üretmelidir.
Örneğimizde tepe gölgelendiricisinin oldukça basit olduğunu görebiliriz. İlişkili pikselin konumunu tanımlamak için gl_position
adında bir sistem değişkeni ( gl_
ile başlayan) üretir ve vUV
adında değişen bir değişken ayarlar.
Matrislerin Arkasındaki Vudu
Gölgelendiricimizle ilgili olan şey, worldViewProjection
adında bir matrisimiz olması ve bu matrisi tepe konumunu gl_position
değişkenine yansıtmak için kullanmamızdır. Bu harika, ama bu matrisin değerini nasıl elde ederiz? Bu bir üniforma, bu yüzden onu CPU tarafında tanımlamamız gerekiyor (JavaScript kullanarak).
Bu, 3D yapmanın karmaşık kısımlarından biridir. Karmaşık matematiği anlamalısınız (veya daha sonra göreceğimiz Babylon.js gibi bir 3D motor kullanmanız gerekecek).
worldViewProjection
matrisi, üç farklı matrisin birleşimidir:

Ortaya çıkan matrisi kullanmak, bakış açısını ve mevcut nesnenin konumu, ölçeği ve dönüşü ile ilgili her şeyi hesaba katarken, 3B köşeleri 2B piksellere dönüştürmemizi sağlar.
Bir 3D geliştirici olarak bu sizin sorumluluğunuzdadır: bu matrisi oluşturmak ve güncel tutmak.
Shader'a geri dön
Köşe gölgelendiricisi her köşede çalıştırıldığında (o zaman üç kez), doğru gl_position
ve vUV
değerine sahip üç pikselimiz olacaktır. GPU, bu piksellerle üretilen üçgende bulunan her pikselde bu değerleri enterpolasyon yapacaktır.
Ardından, her piksel için piksel gölgelendiriciyi çalıştırır:
precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); }
Piksel (veya Parça) Gölgelendirici Yapısı
Bir piksel gölgelendiricinin yapısı, bir köşe gölgelendiricininkine benzer:
- Değişen . Değişken değişkenler, tepe gölgelendiricisi tarafından yaratılan ve piksel gölgelendiriciye iletilen değerlerdir. Burada piksel gölgelendirici, köşe gölgelendiriciden bir
vUV
değeri alacaktır. - Üniformalar . Üniforma, gölgelendirici tarafından kullanılan ve CPU tarafından tanımlanan bir değişkendir. Burada sahip olduğumuz tek üniforma, doku renklerini okumak için kullanılan bir araç olan bir örnekleyicidir.
- ana .
main
adlı işlev, GPU tarafından her piksel için yürütülen koddur ve en azındangl_FragColor
için bir değer (yani mevcut pikselin rengi) üretmesi gerekir.
Bu piksel gölgelendirici oldukça basittir: Köşe gölgelendiriciden doku koordinatlarını kullanarak dokudan rengi okur (sırasıyla tepe noktasından alır).
Sorun şu ki, gölgelendiriciler geliştirildiğinde, yolun sadece yarısındasınız çünkü o zaman çok sayıda WebGL koduyla uğraşmak zorunda kalıyorsunuz. Gerçekten de, WebGL gerçekten güçlü ama aynı zamanda gerçekten düşük seviyelidir ve arabellekleri oluşturmaktan köşe yapılarını tanımlamaya kadar her şeyi kendiniz yapmanız gerekir. Ayrıca tüm matematiği yapmanız, tüm durumları ayarlamanız, doku yüklemeyi vb. yapmanız gerekir.
Çok zor? BABYLON.ShaderKurtarma Malzemesi
Ne düşündüğünüzü biliyorum: "Shader'lar gerçekten harika, ancak WebGL'nin dahili tesisatıyla ve hatta matematikle uğraşmak istemiyorum."
Ve haklısın! Bu tamamen meşru bir soru ve Babylon.js'yi tam da bu yüzden yarattım!
Babylon.js'yi kullanmak için önce basit bir web sayfasına ihtiyacınız var:
<!DOCTYPE html> <html> <head> <title>Babylon.js</title> <script src="Babylon.js"></script> <script type="application/vertexShader"> precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Normal varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; } </script> <script type="application/fragmentShader"> precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); } </script> <script src="index.js"></script> <style> html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; margin: 0px; overflow: hidden; } #renderCanvas { width: 100%; height: 100%; touch-action: none; -ms-touch-action: none; } </style> </head> <body> <canvas></canvas> </body> </html>
Gölgelendiricilerin <script>
etiketleri tarafından tanımlandığını fark edeceksiniz. Babylon.js ile bunları ayrı dosyalarda ( .fx
dosyaları) da tanımlayabilirsiniz.
- Babylon.js kaynağı
- GitHub deposu
Son olarak, ana JavaScript kodu şudur:
"use strict"; document.addEventListener("DOMContentLoaded", startGame, false); function startGame() { if (BABYLON.Engine.isSupported()) { var canvas = document.getElementById("renderCanvas"); var engine = new BABYLON.Engine(canvas, false); var scene = new BABYLON.Scene(engine); var camera = new BABYLON.ArcRotateCamera("Camera", 0, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas); // Creating sphere var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 5, scene); var amigaMaterial = new BABYLON.ShaderMaterial("amiga", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode", }, { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] }); amigaMaterial.setTexture("textureSampler", new BABYLON.Texture("amiga.jpg", scene)); sphere.material = amigaMaterial; engine.runRenderLoop(function () { sphere.rotation.y += 0.05; scene.render(); }); } };
BABYLON.ShaderMaterial
kullandığımı, shaderları derleme, bağlama ve kullanma yükünden kurtulmak için kullandığımı görebilirsiniz.
BABYLON.ShaderMaterial
oluşturduğunuzda, gölgelendiricileri depolamak için kullanılan DOM öğesini veya gölgelendiricilerin bulunduğu dosyaların temel adını belirtmeniz gerekir. Dosyaları kullanmayı seçerseniz, her gölgelendirici için bir dosya oluşturmalı ve şu kalıbı kullanmalısınız: basename.vertex.fx
ve basename.fragment.fx
. Ardından, malzemeyi şu şekilde oluşturmanız gerekecek:
var cloudMaterial = new BABYLON.ShaderMaterial("cloud", scene, "./myShader", { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] });
Ayrıca, kullandığınız niteliklerin ve üniformaların adlarını da belirtmelisiniz.
Ardından, setTexture
, setFloat
, setFloats
, setColor3
, setColor4
, setVector2
, setVector3
, setVector4
, setMatrix
işlevlerini kullanarak üniformalarınızın ve örnekleyicilerinizin değerlerini doğrudan ayarlayabilirsiniz.
Oldukça basit, değil mi?
Babylon.js ve BABYLON.ShaderMaterial
kullanan önceki worldViewProjection
matrisini hatırlıyor musunuz? Sadece bunun için endişelenmene gerek yok! BABYLON.ShaderMaterial
bunu sizin için otomatik olarak hesaplayacaktır çünkü onu üniforma listesinde ilan edeceksiniz.
BABYLON.ShaderMaterial
ayrıca sizin için aşağıdaki matrisleri de işleyebilir:
-
world
-
view
, -
projection
, -
worldView
, -
worldViewProjection
.
Artık matematiğe gerek yok. Örneğin, sphere.rotation.y += 0.05
her çalıştırdığınızda, kürenin world
matrisi sizin için oluşturulacak ve GPU'ya iletilecektir.
Canlı sonucu kendiniz görün.
Kendi Gölgelendiricinizi Yaratın (CYOS)
Şimdi biraz daha büyütelim ve dinamik olarak kendi shaderlarınızı oluşturabileceğiniz ve sonucu hemen görebileceğiniz bir sayfa oluşturalım. Bu sayfa, daha önce tartıştığımız aynı kodu kullanacak ve oluşturacağınız gölgelendiricileri derlemek ve yürütmek için BABYLON.ShaderMaterial
nesnesini kullanacak.
Kendi Gölgelendiricinizi Oluşturun (CYOS) için ACE kod düzenleyicisini kullandım. Sözdizimi vurgulama ile inanılmaz bir kod düzenleyicisidir. Bir göz atmaktan çekinmeyin.

İlk açılan kutuyu kullanarak önceden tanımlanmış gölgelendiricileri seçebileceksiniz. Her birini hemen sonra göreceğiz.
İkinci birleşik kutuyu kullanarak gölgelendiricilerinizi önizlemek için kullanılan ağı (yani 3B nesneyi) de değiştirebilirsiniz.
Derleme düğmesi, gölgelendiricilerinizden yeni bir BABYLON.ShaderMaterial
oluşturmak için kullanılır. Bu buton tarafından kullanılan kod aşağıdaki gibidir:
// Compile shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode", }, { attributes: ["position", "normal", "uv"], uniforms: ["world", "worldView", "worldViewProjection"] }); var refTexture = new BABYLON.Texture("ref.jpg", scene); refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE; refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE; var amigaTexture = new BABYLON.Texture("amiga.jpg", scene); shaderMaterial.setTexture("textureSampler", amigaTexture); shaderMaterial.setTexture("refSampler", refTexture); shaderMaterial.setFloat("time", 0); shaderMaterial.setVector3("cameraPosition", BABYLON.Vector3.Zero()); shaderMaterial.backFaceCulling = false; mesh.material = shaderMaterial;
İnanılmaz derecede basit, değil mi? Materyal size önceden hesaplanmış üç matris ( world
, worldView
ve worldViewProjection
) göndermeye hazırdır. Köşeler konum, normal ve doku koordinatlarıyla birlikte gelir. İki doku da sizin için zaten yüklendi:

amiga.jpg
(Büyük versiyonu görüntüleyin) 
ref.jpg
(Büyük versiyonu görüntüleyin) Son olarak, renderLoop
, iki uygun üniformayı güncellediğim yerdir:
- Birine
time
denir ve bazı komik animasyonlar alır. - Diğeri, kameranın konumunu gölgelendiricilerinize alan
cameraPosition
olarak adlandırılır (aydınlatma denklemleri için kullanışlıdır).
engine.runRenderLoop(function () { mesh.rotation.y += 0.001; if (shaderMaterial) { shaderMaterial.setFloat("time", time); time += 0.02; shaderMaterial.setVector3("cameraPosition", camera.position); } scene.render(); });
Temel Gölgelendirici
CYOS'ta tanımlanan ilk gölgelendirici ile başlayalım: temel gölgelendirici.
Bu gölgelendiriciyi zaten biliyoruz. gl_position
hesaplar ve her piksel için bir renk getirmek için doku koordinatlarını kullanır.
Piksel konumunu hesaplamak için sadece worldViewProjection
matrisine ve tepe noktasının konumuna ihtiyacımız var:
precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }
Doku koordinatları ( uv
) değiştirilmeden piksel gölgelendiriciye iletilir.
Chrome gerektirdiğinden, hem tepe noktası hem de piksel gölgelendiricileri için ilk satıra precision mediump float
eklememiz gerektiğini lütfen unutmayın. Daha iyi performans için tam hassas kayan değerler kullanmadığımızı belirtir.
Piksel gölgelendirici daha da basittir, çünkü doku koordinatlarını kullanmamız ve bir doku rengi getirmemiz yeterlidir:
precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); }
Daha önce textureSampler
üniformasının amiga
texture ile doldurulduğunu görmüştük. Yani, sonuç aşağıdaki gibidir:

Siyah Beyaz Gölgelendirici
Yeni bir gölgelendirici ile devam edelim: siyah beyaz gölgelendirici. Bu gölgelendiricinin amacı, öncekini ancak yalnızca siyah beyaz işleme moduyla kullanmaktır.
Bunu yapmak için aynı köşe gölgelendiricisini tutabiliriz. Piksel gölgelendirici biraz değiştirilecektir.
Elimizdeki ilk seçenek, yeşil olan gibi yalnızca bir bileşen almaktır:
precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = vec4(texture2D(textureSampler, vUV).ggg, 1.0); }
Gördüğünüz gibi .rgb
(bu işleme swizzle denir) kullanmak yerine .ggg
kullandık.
Ancak gerçekten doğru bir siyah beyaz efekti istiyorsak, (tüm bileşenleri hesaba katan) parlaklığı hesaplamak daha iyi olur:
precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { float luminance = dot(texture2D(textureSampler, vUV).rgb, vec3(0.3, 0.59, 0.11)); gl_FragColor = vec4(luminance, luminance, luminance, 1.0); }
dot
işlemi (veya dot
çarpımı) şu şekilde hesaplanır: result = v0.x * v1.x + v0.y * v1.y + v0.z * v1.z
.
Yani bizim durumumuzda luminance = r * 0.3 + g * 0.59 + b * 0.11
. (Bu değerler, insan gözünün yeşile daha duyarlı olduğu gerçeğine dayanmaktadır.)
Kulağa hoş geliyor, değil mi?

Hücre Gölgelendirme Gölgelendiricisi
Daha karmaşık bir gölgelendiriciye geçelim: hücre gölgelendirme gölgelendiricisi.
Bu, tepe noktasının normalini ve tepe noktasının konumunu piksel gölgelendiriciye almamızı gerektirecek. Böylece, köşe gölgelendirici şöyle görünecektir:
precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 world; uniform mat4 worldViewProjection; // Varying varying vec3 vPositionW; varying vec3 vNormalW; varying vec2 vUV; void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vPositionW = vec3(world * vec4(position, 1.0)); vNormalW = normalize(vec3(world * vec4(normal, 0.0))); vUV = uv; }
Lütfen dünya matrisini de kullandığımızı unutmayın, çünkü konum ve normal herhangi bir dönüşüm olmadan depolanır ve nesnenin dönüşünü hesaba katmak için dünya matrisini uygulamamız gerekir.
Piksel gölgelendirici aşağıdaki gibidir:
precision highp float; // Lights varying vec3 vPositionW; varying vec3 vNormalW; varying vec2 vUV; // Refs uniform sampler2D textureSampler; void main(void) { float ToonThresholds[4]; ToonThresholds[0] = 0.95; ToonThresholds[1] = 0.5; ToonThresholds[2] = 0.2; ToonThresholds[3] = 0.03; float ToonBrightnessLevels[5]; ToonBrightnessLevels[0] = 1.0; ToonBrightnessLevels[1] = 0.8; ToonBrightnessLevels[2] = 0.6; ToonBrightnessLevels[3] = 0.35; ToonBrightnessLevels[4] = 0.2; vec3 vLightPosition = vec3(0, 20, 10); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); vec3 color = texture2D(textureSampler, vUV).rgb; if (ndl > ToonThresholds[0]) { color *= ToonBrightnessLevels[0]; } else if (ndl > ToonThresholds[1]) { color *= ToonBrightnessLevels[1]; } else if (ndl > ToonThresholds[2]) { color *= ToonBrightnessLevels[2]; } else if (ndl > ToonThresholds[3]) { color *= ToonBrightnessLevels[3]; } else { color *= ToonBrightnessLevels[4]; } gl_FragColor = vec4(color, 1.); }
Bu gölgelendiricinin amacı ışığı simüle etmektir ve düzgün gölgeleme hesaplamak yerine ışığı belirli parlaklık eşiklerine göre uygulayacağız. Örneğin, ışık şiddeti 1 (maksimum) ile 0.95 arasında ise, nesnenin rengi (dokudan alınan) doğrudan uygulanacaktır. Yoğunluk 0,95 ile 0,5 arasındaysa, renk 0,8 faktörü ile zayıflatılacaktır. Ve bunun gibi.
Bu gölgelendiricide temel olarak dört adım vardır.
İlk olarak, eşikler ve seviyeler sabitleri beyan ederiz.
Ardından, Phong denklemini kullanarak aydınlatmayı hesaplarız (ışığın hareket etmediğini dikkate alacağız):
vec3 vLightPosition = vec3(0, 20, 10); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); // diffuse float ndl = max(0., dot(vNormalW, lightVectorW));
Piksel başına ışığın yoğunluğu, normal ve ışık yönü arasındaki açıya bağlıdır.
Ardından piksel için doku rengini alırız.
Son olarak eşiği kontrol ediyor ve seviyeyi renge uyguluyoruz.
Sonuç bir çizgi film nesnesine benziyor:

Phong Shader
Önceki gölgelendiricide Phong denkleminin bir kısmını kullandık. Şimdi tamamen kullanalım.
Köşe gölgelendirici burada açıkça basittir çünkü her şey piksel gölgelendiricide yapılacaktır:
precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vUV = uv; vPosition = position; vNormal = normal; }
Denkleme göre, ışık yönü ve tepe noktasının normalini kullanarak “yaygın” ve “speküler” kısımları hesaplamalıyız:
precision highp float; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; // Uniforms uniform mat4 world; // Refs uniform vec3 cameraPosition; uniform sampler2D textureSampler; void main(void) { vec3 vLightPosition = vec3(0, 20, 10); // World values vec3 vPositionW = vec3(world * vec4(vPosition, 1.0)); vec3 vNormalW = normalize(vec3(world * vec4(vNormal, 0.0))); vec3 viewDirectionW = normalize(cameraPosition - vPositionW); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); vec3 color = texture2D(textureSampler, vUV).rgb; // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); // Specular vec3 angleW = normalize(viewDirectionW + lightVectorW); float specComp = max(0., dot(vNormalW, angleW)); specComp = pow(specComp, max(1., 64.)) * 2.; gl_FragColor = vec4(color * ndl + vec3(specComp), 1.); }
Dağınık kısmı önceki gölgelendiricide zaten kullandık, bu yüzden burada sadece aynasal kısmı eklememiz gerekiyor. Phong gölgelendirme hakkında daha fazla bilgiyi Wikipedia'da bulabilirsiniz.
Küremizin sonucu:

Gölgelendiriciyi At
Discard shader için yeni bir konsept sunmak istiyorum: discard
anahtar sözcüğü.
Bu gölgelendirici, kırmızı olmayan her pikseli atar ve kazılmış bir nesnenin yanılsamasını yaratır.
Köşe gölgelendirici, temel gölgelendirici tarafından kullanılanla aynıdır:
precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varying varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; }
Yandaki piksel gölgelendirici, örneğin yeşil bileşen çok yüksek olduğunda rengi test etmeli ve atma özelliğini kullanmalıdır:
precision highp float; varying vec2 vUV; // Refs uniform sampler2D textureSampler; void main(void) { vec3 color = texture2D(textureSampler, vUV).rgb; if (color.g > 0.5) { discard; } gl_FragColor = vec4(color, 1.); }
Sonuç biraz komik:

Dalga Gölgelendirici
Piksel gölgelendiriciyle çok oynadık, ancak köşe gölgelendiricilerle çok şey yapabileceğimizi de bilmenizi isterim.
Dalga gölgelendirici için Phong piksel gölgelendiriciyi yeniden kullanacağız.
Köşe gölgelendirici, bazı canlandırılmış değerleri almak için tek biçimli time
adını kullanır. Bu üniformayı kullanarak, gölgelendirici köşelerin konumlarıyla bir dalga oluşturacaktır:
precision highp float; // Attributes attribute vec3 position; attribute vec3 normal; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; uniform float time; // Varying varying vec3 vPosition; varying vec3 vNormal; varying vec2 vUV; void main(void) { vec3 v = position; vx += sin(2.0 * position.y + (time)) * 0.5; gl_Position = worldViewProjection * vec4(v, 1.0); vPosition = position; vNormal = normal; vUV = uv; }
position.y
öğesine bir sinüs uygulanır ve sonuç aşağıdaki gibidir:

Küresel Çevre Haritalama
Bu, büyük ölçüde "Küresel Bir Yansıma/Çevre Haritalama Gölgelendiricisi Oluşturma" makalesinden esinlenmiştir. Bu mükemmel makaleyi okumanıza ve ilgili gölgelendiriciyle oynamanıza izin vereceğim.

Fresnel Gölgelendirici
Bu makaleyi en sevdiğim şeyle bitirmek istiyorum: Fresnel gölgelendirici.
Bu gölgelendirici, görüş yönü ile tepe noktasının normali arasındaki açıya göre farklı bir yoğunluk uygulamak için kullanılır.
Köşe gölgelendiricisi, hücre gölgelendirme gölgelendiricisi tarafından kullanılanla aynıdır ve piksel gölgelendiricimizde Fresnel terimini kolayca hesaplayabiliriz (çünkü normal ve kameranın görüş yönünü değerlendirmek için kullanılabilen konumuna sahibiz):
precision highp float; // Lights varying vec3 vPositionW; varying vec3 vNormalW; // Refs uniform vec3 cameraPosition; uniform sampler2D textureSampler; void main(void) { vec3 color = vec3(1., 1., 1.); vec3 viewDirectionW = normalize(cameraPosition - vPositionW); // Fresnel float fresnelTerm = dot(viewDirectionW, vNormalW); fresnelTerm = clamp(1.0 - fresnelTerm, 0., 1.); gl_FragColor = vec4(color * fresnelTerm, 1.); }

Shader'ın mı?
Artık kendi gölgelendiricinizi oluşturmaya daha hazırsınız. Deneylerinizi paylaşmak için Babylon.js forumunda paylaşım yapmaktan çekinmeyin!
Daha ileri gitmek istiyorsanız, işte bazı yararlı bağlantılar:
- Babylon.js, resmi web sitesi
- Babylon.js, GitHub deposu
- Babylon.js forumu, HTML5 Oyun Geliştiricileri
- Kendi Shader'ınızı (CYOS) Yaratın, Babylon.js
- OpenGL Gölgelendirme Dili, Wikipedia
- OpenGL Gölgelendirme Dili, belgeler