Shader & GLSL · Sözdizimi & türler
Variables (GLSL değişkenler)
Türü yaz, kapsamı bil — arayüz değişkeni değil
GLSL’de her değişkenin türü kaynakta bellidir; JavaScript’teki
let / const esnekliği yoktur. Bu sayfa, shader içinde yerel
ve sabit değişkenleri nasıl tanımlayacağınızı anlatır: bildirim, başlangıç değeri,
const ve fonksiyon kapsamı.
Uniform, attribute ve varying / in · out arabirimi başka bir katmandır — CPU’dan veya köşe tamponundan gelen «dış veri» sözleşmesidir ( GLSL giriş · depolama, Uniforms & varyings konusu). Burada yalnızca programın kendi hesapladığı ara değerler vardır.
Genel dil çerçevesi ve vertex / fragment rolleri
GLSL
nedir?
sayfasındadır; vec2 / vec3 yapıcıları ve swizzle
Vec types
konusuna bırakılır. Geometri tarafındaki buffer attribute kavramı
Attributes & buffer’lar
ile karıştırılmamalıdır — isim benzerliği, farklı katmanlardır.
Shader içinde değişken ne demek?
Bir GLSL programı (vertex veya fragment gövdesi), C’ye yakın
sözdiziminde ifadelerden oluşur; ifadelerin çoğu bir türde saklanan
değişkenlere dayanır. Değişken, o invokasyonda (bir köşe veya bir fragment adayı) geçici
olarak tuttuğunuz skaler, vektör veya matristir — örneğin dönüştürülmüş normal,
karıştırılmış
renk veya gürültü örneklemesi için ara float.
JavaScript tarafında sahne nesneleri ve uniform köprüleri vardır; GPU tarafında ise derleyici her değişkenin boyutunu ve yaşam süresini statik olarak bilir. Bu yüzden «sonradan başka türe atama» çoğu zaman mümkün değildir; uyumsuzluk derleme aşamasında yakalanır — bu, çalışma anında sürpriz azaltır.
Pratikte shader’daki isimler üç katmana ayrılır: programın kendi hesapladığı yerel ve global değişkenler ile dış dünyadan veya önceki aşamadan gelen arayüz değişkenleri. Aşağıda her katmanın rolü kısaca özetlenir; arayüz ayrıntısı kardeş sayfalara bırakılır.
Yerel değişken
Fonksiyon gövdesi veya { … } bloğu içinde tanımlanır; yalnızca o kapsamda yaşar
—
main() de bir fonksiyondur, oradaki float t yalnızca o
invokasyonda
geçerlidir. Vertex shader’da her köşe, fragment’te her aday piksel için ayrı invokasyon
düşünüldüğünde yerel değişkenler «o çalıştırma örneğine» özeldir; bir köşede hesaplanan
ndotl başka köşenin register’ına taşınmaz.
Efekt yazarken çoğu satır burada kalır: UV ölçekleme, maske, ışık terimi, renk karışımı…
Okunabilirlik için uzun ifadeleri parçalayıp ara isim vermek (
Holodepth notu) bu
katmandadır.
Blok açarsanız (if, for gövdesi), blok içi isim dışarıdan görünmez
—
aynı ismi iç ve dış blokta farklı türle kullanmak hataya yol açabilir.
Üst düzey (global) değişken
Shader dosyasında, fonksiyonların dışında tanımlanır; dosyadaki tüm
fonksiyonlar okuyup yazabilir (paylaşılan sabit veya yardımcı durum). Küçük örneklerde
const float PI = 3.14159; gibi sabitleri üst düzeye koymak yaygındır; büyük
shader’larda ise «kimin hangi global’i güncellediği» takibi zorlaşır — isim çakışması ve yan
etkili yanlış atama riski artar.
Mümkün olduğunda hesabı main veya yardımcı fonksiyon içinde yerel tutun; global
yalnızca gerçekten paylaşılan, değişmeyen veya tüm fonksiyonların ihtiyaç duyduğu değerler
için kalsın. const global sabitler (
const bölümü) çoğu zaman yeterlidir;
her kare değişmesi gereken değer global değil uniform olmalıdır.
Arayüz değişkeni
uniform, attribute, varying veya GLSL
ES 3.00’daki in / out anahtar sözcükleriyle
bildirilir — bunlar «shader içi hesap» değil, sözleşme katmanıdır: CPU’dan
gelen
parametreler, köşe tamponları veya vertex → fragment köprüsü. Örneğin uniform float
uTime tüm çizim çağrısı boyunca sabittir; attribute vec3 position
yalnızca
vertex aşamasında köşe başına okunur.
Bu isimler yerel değişkenle karıştırılmamalıdır: Three.js’te BufferGeometry
attribute’u (
Attributes & buffer’lar) JavaScript / GPU tampon
katmanındadır; shader’daki attribute vec3 aPosition o tamponun vertex shader
girişidir. Rol tablosu ve köprü mantığı
GLSL giriş · depolama
ve
Uniforms
& varyings
konusundadır — burada yalnızca «yerel/global değil, arayüz» ayrımını sabitliyoruz.
Bildirim ve başlangıç değeri
Temel kalıp: tür + isim, isteğe bağlı = başlangıç, sonda
;. Tür yazılmadan değişken tanımlanamaz.
void main() {
float t = 0.5;
vec3 baseColor = vec3(0.2, 0.4, 0.9);
vec2 uvScaled = vUv * 2.0;
float edge = smoothstep(0.45, 0.55, uvScaled.x);
gl_FragColor = vec4(baseColor * edge, 1.0);
}
Vektör ve matrisler için yapıcı çağrıları sık kullanılır (vec3(1.0, 0.0, 0.0),
mat4(1.0) birim matris). Bileşen erişimi ve swizzle kuralları
Vec types
konusundadır. Skaler literallerde çoğu bağlamda 1.0 tercih edilir;
1 tek başına int sayılabilir — karışık aritmetikte
Operators
konusuna bakın.
JavaScript’e alışkın iseniz aşağıdaki üç kural özellikle önemlidir — GLSL derleyicisi türü önceden bilir; «sonra anlarım» diye bırakılan satırlar çoğu zaman kırmızı konsola düşer.
Tip zorunluluğu
var x = 1.0; gibi tür çıkarımı yoktur; her bildirimde tür açık yazılır:
float x = 1.0;, vec3 c = vec3(1.0);. Bu, C ve HLSL’e yakın
disiplindir;
GPU derleyicisi register boyutunu ve opcode seçimini buna göre planlar. JavaScript’teki
let / const esnekliği shader gövdesinde karşılık bulmaz — tip
güvenli
çerçevenin özeti
GLSL giriş · tipler
bölümündedir; vec yapıcıları
Vec types
konusuna bırakılır.
Yanlış türle birleştirme (ör. float ile int toplama) çoğu zaman
açık dönüşüm veya yapıcı ister; sessiz genişleme beklemeden hatayı okuyun. Literal yazarken
1.0 / 1 ayrımı
Operators
konusunda netleşir.
Başlatılmamış okuma
float mask; yazıp hemen mask = smoothstep(…); demek güvenlidir;
fakat float mask; sonrası doğrudan color *= mask; kullanmak
tanımsız davranış (undefined behavior) üretebilir —
register’da önceki invokasyondan kalan çöp değer okunabilir. Bazı derleyiciler uyarır,
bazıları
sessizce geçer; üretim kodunda «muhtemelen sıfırdır» varsaymayın.
Pratik kural: bildirirken mümkünse başlangıç verin (float mask = 0.0;) veya ilk
kullanımdan önce tek bir atama yolunun her dallada çalıştığından emin olun. Dalgalı
if / else dallarında bir kolun mask’i atamaması, sonraki satırda
başlatılmamış okuma riskidir.
Atama ve yeniden bildirim
Aynı kapsamda aynı isimle ikinci kez float foo; yazmak yeniden
bildirimdir ve derleme hatası verir — JavaScript’teki gölgeleme (
shadowing) burada genelde izin verilmez. Değeri güncellemek için türü
tekrar yazmayın; yalnızca foo = yeniDeger; yeterlidir (
const tanımlılar hariç).
// Hatalı: aynı kapsamda ikinci bildirim
float intensity = 0.5;
float intensity = 1.0; // redefinition
// Doğru: atama
float intensity = 0.5;
intensity = 1.0;
İç içe blokta dışarıdaki isimle aynı ada sahip yeni bir yerel tanımlamak bazı profillerde iç gölgeleme yapar; okunabilirlik için farklı isim seçmek daha güvenlidir. Üst düzey (global) ile yerel aynı ismi taşıyorsa hangi satırın hangisini kastettiği karışabilir — global bölüm uyarısına bakın.
const · derleme zamanı sabiti
const ile tanımlanan değişken, shader yürütülmeden önce bilinen bir değere
kilitlenir; sonradan atama yapılamaz. Büyüme hızı, renk paleti eşiği veya döngü üst sınırı
gibi
«kod içi sabitler» için okunabilirliği artırır.
const float PI = 3.14159265;
const vec3 SKY = vec3(0.05, 0.08, 0.15);
const ifadesi, optimizasyon sırasında sabit katlanmaya (constant folding) uygun veri üretir; mobilde gereksiz register
baskısını hafifletebilir. Dışarıdan her kare değişmesi gereken değerler için
const değil uniform kullanılır — ayrım
Uniforms
& varyings
konusunun kalbidir.
Kapsam: global, fonksiyon ve blok
Shader dosyasının en üstünde (precision bildirimleri ve arayüz değişkenlerinden sonra)
tanımlanan
global değişkenler tüm fonksiyonlardan erişilebilir. Küçük örneklerde cazip olsa da, büyük
projede «kim yazdı, kim okuyor?» takibini zorlaştırır; mümkünse hesabı main
veya
yardımcı fonksiyon içinde yerel tutun.
Fonksiyon parametreleri de yerel kapsamdadır; GLSL’de referans veya «pointer» yoktur —
vektör
ve matrisler değer semantiğiyle kopyalanır (optimizasyonlar derleyiciye bağlıdır). İç içe
blok
{ … } açarsanız, blok içi değişken dışarıdan görünmez.
Precision bildirimi (kısa not)
Özellikle fragment shader kökünde precision mediump float; gibi satırlar,
float değişkenlerin varsayılan hassasiyetini belirler — bu bir değişken
tanımı değil, tür için varsayılan kalitedir. Mobil performans / bant sorunlarında önemlidir;
tam tablo
GLSL giriş · altın kurallar
bölümünde özetlenir.
İsimlendirme ve karışıklık önleme
GLSL’de önek kullanmak zorunlu değildir; fakat uTime gördüğünüzde «bu
uniform, muhtemelen JS’ten geliyor» demek okumayı hızlandırır.
Aşağıdaki
dört kalıp, arayüz değişkeni
katmanı ile yerel değişken
katmanını bir bakışta ayırmanız içindir — tam sözleşme
Uniforms
& varyings
konusunda.
uöneki: uniform — çizim çağrısı boyunca sabit kalan parametreler. Örnekler:uTime(animasyon zamanı),uResolution(viewport boyutu),uMouse(fare). Değerler JavaScript tarafındamaterial.uniformsveya ShaderMaterial köprüsüyle her kare veya her çizim öncesi yazılır; köşe başına farklılaşmaz. «Her vertex’e farklı renk» isteği uniform ile çözülmez — attribute veya texture örnekleme gerekir.vöneki: Vertex’ten fragment’e taşınan varying (GLSL ES 3.00’da vertex’teout, fragment’te aynı isimlein). Örnekler:vUv(doku koordinatı),vNormal(dünya veya model uzayında normal). Üçgen içinde enterpolasyon uygulanır — köşe değerleri arasında yumuşak geçiş fragment aşamasında oluşur; gradient ve desenlerde sık görülür.aöneki: Vertex attribute — yalnızca vertex shader girişi;BufferGeometrytampon kanallarından beslenir. Örnek:aPositionham köşe konumu. Three.js’teMeshStandardMaterialgibi hazır materyaller attribute’ları motor üretir; özel shader’da kanal adlarını siz geometri ile eşleştirirsiniz ( Attributes & buffer’lar).- Yerel değişken: Önek zorunlu değil; shader gövdesinde hesaplanan ara
değerler. Anlamlı isimler okunurluğu artırır:
diffuse(yayılan renk),ndotl(normal · ışık yönü),mask(maskeleme). Bunlar uniform değildir; yalnızca o invokasyonda yaşar — §1’deki yerel katman.
ShaderMaterial veya RawShaderMaterial
kullanırken motor bazen modelViewMatrix gibi ek uniform’lar enjekte eder;
sizin u* isimlerinizle çakışmaması için tanımları kontrol edin. İsim benzerliği
tuzakları: geometry’deki position attribute’u ile shader içindeki yerel
vec3 position aynı şey değildir; biri CPU tamponu, diğeri GPU’daki yerel
register’dır.
Sık hatalar ve teşhis
Shader hataları çoğu zaman derleme aşamasında kalır; «siyah ekran» gördüğünüzde önce tarayıcı konsolundaki vertex / fragment log’una bakın. Aşağıdaki dört kalıp, bu sayfadaki değişken katmanlarıyla doğrudan ilişkilidir — kök neden çoğu zaman «yanlış tür» veya «yanlış veri katmanı»dır.
- Tür uyumsuzluğu:
float a = 1;bazı profillerde uyarı veya hata üretir; literali1.0yazın veyafloat(1)ile açık dönüşüm yapın. GLSL’deintilefloatsessizce birleşmez ( tip zorunluluğu, Operators). JavaScript alışkanlığıyla yazılan satır, shader derleyicisinde ilk kırmızı satır olabilir. - Yanlış katman: Köşe başına farklı renk, konum veya ID için
uniform yeterli değildir — uniform tüm çizim çağrısında
sabittir. Çözüm: attribute (vertex girişi), varying köprüsü veya texture / buffer örnekleme. «
uile her köşeye farklı değer» denemesi bu hataya düşer ( önekler, arayüz katmanı). varyingeşleşmesi: Vertex’teout vec2 vUv, fragment’tein vec2 vUv— isim ve tür birebir aynı olmalı; birivec2diğerivec3ise link hatası alırsınız. Yerelvec2 uvtanımlamak köprü oluşturmaz; varying /in·outsözleşmesi şarttır. Ayrıntı Uniforms & varyings konusunda.- GLSL ES 3.00: Eski örneklerde
attribute/varyingvegl_FragColorgörülür; WebGL2 / Three.js GLSL3 yolundain/outve kendi
out vec4 fragColortanımınız gerekir. Forumdan kopyalanan shader’ı yapıştırmadan önce hedef sürümü ( GLSL giriş · depolama) ile eşleştirin.
Konsolda vertex ve fragment shader ayrı ayrı raporlanır; ilk hatayı
düzeltmeden
alttaki «undeclared identifier» zincirine takılmayın — çoğu zaman üstteki noktalı virgül
veya
yeniden bildirim (
atama vs bildirim)
kaynaklıdır.
Tek bir uniform’u güncelledim ama görüntü değişmedi ise önce isim eşleşmesi ve
güncellemenin çizim çağrısından önce yapıldığını doğrulayın; bu, değişken
katmanı değil JavaScript köprüsü sorunudur.
Holodepth notu
Yeni bir efekt yazarken önce girdileri (uniform / varying) ve
çıktıyı (gl_FragColor veya out vec4) sabitleyin;
aradaki her adımı küçük yerel değişkenlere bölün. Tek satırlık dev mix(mix(…))
zincirleri yerine float mask = …; gibi ara isimler, hem hatayı bulmayı hem de
sonraki built-in birleştirmelerini kolaylaştırır.