Shader & GLSL · Sözdizimi & türler
Operators (GLSL operatörleri)
Türler neyi toplar, neyi çarpar — derleyici önce bunu bilir
GLSL’de operatörler yalnızca «matematik sembolü» değildir; her ifadenin
sonuç türünü ve geçerliliğini belirler. vec3 + float ile
vec3 + vec2 aynı + işaretini kullanır; biri çoğu profilde geçer,
diğeri
derleme hatasıdır. Bu sayfa, sözdizimi ve türler serisinde «işlemlerin kuralları» katmanıdır.
Değişken bildirimi ve kapsam
Variables;
vec2 / vec3 paketleri, swizzle ve broadcast özeti
Vec types
konusundadır — burada vektör–matris çarpımına kısa köprü verilir, ayrıntı
Vec types · mat4 × vec4
bölümünde kalır. Yerleşik fonksiyonlar (dot, mix …)
Built-in
functions
sayfasına bırakılır.
Genel dil çerçevesi
GLSL giriş · tipler
sayfasındadır; JavaScript’teki gevşek birleştirme alışkanlığı (
1 + 0.5 sorunsuz ) burada çoğu zaman geçmez —
tip zorunluluğu
ile birlikte düşünün.
Neden operatör kuralları?
JavaScript tarafında bir ifade «çalışma anında» şekillenir; GLSL’de ise vertex veya fragment programı çoğu zaman önce derlenir, sonra GPU’da binlerce kez çalıştırılır. Bu bölüm, aşağıdaki aritmetik ve matris bölümlerine geçmeden önce «operatör neden ayrı bir konu?» sorusuna kısa cevap verir.
Derleme zamanı ve tür çözümü
Shader derleyicisi her satırı çalıştırmadan önce türleri çözer. Operatör, «bu iki ifade
birleştirilebilir mi ve sonuç ne tür olur?» sorusunun cevabıdır — örneğin
vec3 + float çoğu profilde geçerken vec3 + vec2 reddedilir (
Vec types · boyut
uyumu).
Bu yüzden hata çoğu zaman tarayıcı konsolunda değil, shader derleme
aşamasında görünür. Özellikle iki tuzak öne çıkar: boyut uyumsuzluğu (
mat4 * vec3 gibi) ve int / float
karışımı — JavaScript’te sorunsuz görünen 1 + 0.5 alışkanlığı
GLSL’de literallerde 1.0 veya açık dönüşüm ister (
tip zorunluluğu,
§6 · sık hatalar).
Üç operatör ailesi
Pratikte shader kodunun büyük kısmı üç infix ailesiyle yazılır; geri kalanı
yerleşik fonksiyon çağrılarıdır (sin, texture2D, dot
…).
Aşağıdaki özet, sayfanın geri kalanına yol haritasıdır — her madde ilgili bölümde açılır,
burada
yalnızca «hangi işaret ne işe yarar?» sorusuna cevap verilir.
- Aritmetik (
+,-,*,/): Skalerde C’ye benzer toplama ve çarpma; vektörlerde çoğu zaman bileşen bazlı birleşim.vec3 + floatgibi broadcast örnekleri bu ailenin parçasıdır — renk tonlama, UV ölçekleme, maske ile çarpma.vec3 * vec3ise matris çarpımı değil, kanal kanal çarpımdır; iç çarpım içindotayrı sözdizimidir. Ayrıntı §2 · Aritmetik. - Karşılaştırma / mantık (
==,!=,&&,||…): «Koşul sağlanıyor mu?» sorusu; çıktı çoğu zaman skalerboolveya 0/1 maske. Fragment shader’da koşullu renk içinifyerinemix/steptercih edilmesinin nedeni bu katmandadır — operatör koşulu üretir, Built-in fonksiyonları koşulu renge çevirir. Ayrıntı §3 · Karşılaştırma ve mantık. - Matris–vektör çarpımı
(mat4 * vec4,mat3 * vec3): Vertex’ta konum ve normal taşıma; lineer dönüşüm. Aynı*sembolü olsa davec4 * vec4bileşen çarpımından farklı anlama gelir — boyut eşleşmesi zorunludur (mat4yalnızcavec4ile). Dönüşüm zinciri venormalMatrixözeti §5 ile Vec types · matris konusundadır.
Bu üç aile dışındaki işlemler parantez ve öncelikle birleştirilir ( §4). Tam yerleşik API Built-in functions sayfasına bırakılır — bu sayfa bilinçli olarak infix operatör sözdizimine odaklanır.
Aritmetik: +, -,
*, /
vec2 uv2 = vUv * 2.0;
vec3 tinted = baseColor + vec3(0.1);
vec3 scaled = normal * uBumpScale;
float ratio = color.r / max(color.g, 1e-4);
Skalerlerde aritmetik C’ye benzer. Vektörlerde ise çoğu işlem bileşen
bazında
uygulanır: vec3(1,2,3) + vec3(4,5,6) → vec3(5,7,9). Çarpma ve
bölme
de aynı mantıkla kanal kanal yapılır — matris çarpımı değildir (
§5).
Broadcast: vektör + skaler
vec3 + float ve vec3 * float çoğu profilde geçerlidir: skaler her
bileşene uygulanır (broadcast). Örnek:
position + uOffset — uOffset tek sayıysa üç eksene eklenir. Aynı
fikir
Vec types · boyut
uyumu
bölümünde paket mantığıyla anlatılmıştı; burada operatör tarafı netleşir.
vec3 + vec2 gibi farklı boyutlu vektör toplamı çoğu zaman derleme
hatasıdır. Gerekirse swizzle veya yapıcı ile boyutu eşitleyin (
yapıcılar).
Bileşen çarpımı vs matris çarpımı
vec3 * vec3 çoğu shader’da Hadamard (bileşen çarpımı) üretir —
renk maskeleme veya kanal bazlı modülasyon için kullanılır. İç çarpım için
dot(a,b)
yerleşiğini kullanın (
Vec types · dot), matris
dönüşümü için mat * vec (
§5).
Karşılaştırma ve mantık
Aritmetik operatörler «değeri hesaplar»; karşılaştırma ve mantık operatörleri «koşul doğru mu?» sorusuna cevap verir. Fragment shader’da bu cevap çoğu zaman doğrudan renge bağlanır — maske, kenar çizgisi veya iki malzeme arasında geçiş. Bu bölüm, §2’deki toplama/çarpmanın ötesinde koşullu ifade katmanını tanıtır; ayrıntılı fonksiyon listesi Built-in sayfasına bırakılır.
Karşılaştırma: skaler ve maske
Karşılaştırma operatörleri (==, !=, <,
<=, >, >=) skalerde bool üretir
— bazı GLSL profillerinde sonuç doğrudan 0.0 / 1.0 «maske» gibi de kullanılır. Örneğin
float inside = step(0.0, uTime - 1.0); düşüncesine yakın: eşik geçildi mi?
Karşılaştırma, aritmetikten daha düşük öncelikli sayılır; karmaşık
ifadelerde
parantez kullanın (
§4 · öncelik). Örnek:
vUv.x > 0.5 tek bir koşul üretir; sonucu renkle birleştirmek ayrı adımdır.
Vektörlerde karşılaştırma
vec3 == vec3 her profilde «üç bileşen de eşit mi?» anlamında tek bir
bool vermeyebilir — bazen bileşen bazında vektör maskesi, bazen derleme uyarısı
veya hata görülür. «Hepsi birden» veya «herhangi biri» testleri için genelde yerleşikler (
equal, lessThan …) veya bileşen bazlı mantık (
color.r == color.g gibi skaler parçalar) tercih edilir.
Pratik kural: karşılaştırmayı mümkün olduğunca skaler kanala indirgemek —
UV bandı için vUv.y, alpha testi için texel.a. Vektör paketinin
kendisiyle «eşitlik» aramak yerine swizzle ile tek bileşen seçin (
Vec types · bileşen
erişimi).
Mantık: &&, || ve
if yerine mix
Mantıksal birleştirme: &&, ||, !. İki koşulu
birleştirip tek maske üretmek mümkündür; fragment shader’da ise if ile
dallanmak her zaman ucuz değildir — GPU dal başına farklı yürütme yolları oluşturabilir.
Bu yüzden koşullu renk çoğu zaman aritmetik ile yazılır:
mix(colorA, colorB, mask), step(edge, vUv.x) veya
smoothstep(0.2, 0.8, vUv.y). Operatör bilgisi, Built-in sayfasına geçerken
«neden
if yerine mix?» sorusuna zemin hazırlar — mix aslında
§2’deki aritmetik ile aynı düşüncenin devamıdır (
Vec types · mix).
float band = step(0.5, vUv.x);
vec3 col = mix(vec3(0.1), vec3(0.9), band);
// if (vUv.x > 0.5) col = ...; // çoğu efektte mix tercih edilir
Tam step / smoothstep imzaları ve diğer yardımcılar
Built-in
functions
konusundadır; burada yalnızca karşılaştırma + mantığın shader stiline nasıl dönüştüğünü
sabitliyoruz.
Öncelik ve parantez
Operatör önceliği, aynı satırda birden fazla işaret varken «hangisi önce uygulanır?» sorusunu cevaplar. GLSL, C ve matematikteki alışkanlığa büyük ölçüde uyar; yine de shader satırları swizzle ve fonksiyon çağrılarıyla hızla karmaşıklaşır — bu bölüm §2–3’teki kuralları okunabilir ifade yazımına bağlar.
Aritmetik sırası
Çarpma ve bölme, toplama ve çıkarmadan önce gelir. a + b * c ifadesinde önce
b * c, sonra a ile toplanır; farklı bir sıra istiyorsanız parantez
şarttır: (a + b) * c. Vektörlerde de aynı kural geçerlidir —
baseColor + mask * highlight önce maskeyle çarpılmış vurguyu, sonra taban renge
ekler (
§2 · aritmetik).
Karşılaştırma ve mantık
Karşılaştırma (<, == …) ve mantıksal birleştirme (
&&, ||) genelde aritmetikten daha düşük
önceliklidir. Örneğin a + b > c okunurken önce
a + b
hesaplanır, sonra c ile karşılaştırılır — bu, §3’teki maske üretiminin
temelidir.
Karmaşık koşullarda parantez ile niyeti yazın:
(ndotl > 0.0) && (shadow < 0.5). Matris çarpımı (
§5) kendi gruplamasına
sahiptir;
mat4 * vec4 tek bir «yüksek öncelikli» birleşim gibi düşünülür — yine de uzun
zincirlerde ara isim kullanmak hatayı azaltır.
Swizzle ve parantez
Swizzle, nokta ile bağlandığı için çoğu zaman fonksiyon çağrısından sonra «yapışık» okunur; şüphe duyduğunuz her yerde parantez kullanın — özellikle swizzle ile aritmetik karışınca:
vec3 c = baseColor * (0.5 + 0.5 * sin(uTime));
float edge = smoothstep(0.4, 0.6, vUv.y) * mask.r;
vec2 uvFlip = (vUv * 2.0 - 1.0).yx; // önce ölçek, sonra ters UV
vUv.yx * 2.0 ile (vUv * 2.0).yx farklı sonuç verebilir; swizzle
kuralları
Vec types · swizzle
konusundadır — burada yalnızca «parantez, hangi adımın önce olduğunu sabitler» notunu
düşünün.
Ara değişken: en güvenli parantez
Uzun tek satırlı ifadeler hem okunurluğu düşürür hem yanlış öncelik hatasına açıktır. İfadeyi parçalayıp yerel isim vermek, görünür parantez gibidir:
float wave = 0.5 + 0.5 * sin(uTime);
vec3 c = baseColor * wave;
float edge = smoothstep(0.4, 0.6, vUv.y);
vec3 finalCol = mix(c, highlight, edge * mask.r);
Bu kalıp yerel değişken ve Holodepth notundaki «ara vektör» önerisiyle uyumludur — derleyici için sonuç aynı kalabilir, insan okuyucusu için sıra netleşir.
Matris × vektör
mat4 * vec4 lineer dönüşümün standart yazımıdır — vertex shader’da
gl_Position zinciri bununla kurulur. Bu, bileşen bazlı
vec4 * vec4
değildir; matris sütunları vektörle çarpılır (
Vec types · dönüşüm
zinciri).
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vec3 n = normalize(normalMatrix * normal);
Boyut uyumu: mat4 yalnızca vec4 ile; mat3 yalnızca
vec3 ile. Matris tanımları ve column-major notu
GLSL giriş · matris
konusundadır — bu sayfa operatör sözdizimini sabitler.
Sık derleme hataları
Bu bölüm, sayfa boyunca anlatılan kuralların derleyicide nasıl «kırmızı satır»a döndüğünü toplar. Çoğu hata mantık hatası değil, operand türü uyumsuzluğudur — konsol mesajında operatörü görürseniz bile önce sol ve sağ tarafın türüne bakın ( §1 · derleme zamanı).
int+float: JavaScript’te1 + 0.5sorunsuz; GLSL’de çoğu zaman açık dönüşüm gerekir —float(i)veya literallerde1.0yazın. Döngü sayacıintiken zamanfloatise çarpım öncesi dönüştürün; bu, Variables konusundaki tip zorunluluğu ile aynı disiplindir — operatör satırı değil, bildirim tarafı da kontrol edilmeli.- Boyut uyumsuzluğu:
vec3 + vec2veyamat4 * vec3çoğu profilde reddedilir. Çözüm: yapıcı veya swizzle ile boyutu eşitleyin — örneğin konum içinvec4(position, 1.0)( §2 · broadcast, §5 · matris, Vec types · boyut uyumu). «Aynı*işareti» her zaman aynı anlama gelmez. - Swizzle atama:
vColor.xyx = …gibi okuma serbest, yazmada çakışan kanallar yasaktır — hangi bileşene yazılacağı belirsizleşir. Okuma tarafındavec3 v = c.xyx;geçerli olabilir; atama kuralları Vec types · swizzle atama konusunda — burada yalnızca «sol taraf swizzle’ı da denetlenir» notu. - Matris sırası:
vec4 * mat4ilemat4 * vec4farklı sonuç verir; Three.js’teprojectionMatrix * modelViewMatrix * vec4(position, 1.0)sırası column-major düzenle uyumludur. Matrisi JavaScript’ten elle kopyalarken satır/sütun karışıklığı bu hatayı üretir — operatör doğru görünür, bellek düzeni yanlıştır ( Vec types · column-major).
Hata ayıklarken ifadeyi ara değişkenlere bölün ( §4 · ara değişken) — hangi satırın derlemeyi kırdığı hemen görünür. Holodepth notundaki «önce sonuç türü» alışkanlığı bu listeyle birlikte düşünülmelidir.
Holodepth notu
Yeni bir satır yazarken önce «sonuç türü ne?» diye sorun: vec2 uv2 = vUv * 2.0;
ardından float band = smoothstep(0.2, 0.8, uv2.y); — her adımda tür net kalır.
Kırmızı derleyici satırında operatörü değil, çoğu zaman sol ve sağ operand
türlerini kontrol edin.