holodepth

Shader & GLSL · Sözdizimi & türler

Vec types (GLSL vektör tipleri)

İlgili sayıları tek pakette taşı — xyz, rgb, uv

Grafik shader’larında aynı anlama gelen sayılar çoğu zaman ayrı ayrı float değil, vektör olarak paketlenir: vec2, vec3, vec4. Konum (xyz), renk (rgba), doku koordinatı (uv) veya clip uzayı (vec4 ile w) bu tiplerle taşınır — GPU hem bellek düzenini hem matematik işlemlerini buna göre optimize eder.

Bu sayfa vektör türlerinin sözdizimine odaklanır: yapıcılar, bileşen erişimi, swizzle, yaygın kullanım eşlemeleri ve kısa vektör matematiği. Değişken bildirimi ve kapsam Variables konusundadır; float / int genel çerçevesi ve mat4 özeti GLSL giriş · tipler sayfasında — burada matrisleri yalnızca vektörle nasıl çarpıldığına değiniriz.

Geometri tamponlarında position kanalı üç bileşenli dizidir ( itemSize = 3); shader tarafında bu veri vec3 veya vec4 olarak okunur. Operatör ve yerleşik fonksiyon ayrıntısı Operators ve Built-in functions konularına bırakılır.

Neden vektör tipi?

GLSL’de «vektör tipi» seçmek, yalnızca sözdizimi tercihi değildir — GPU’nun veriyi register içinde nasıl paketleyeceğini ve hangi yerleşik işlemlerin geçerli olacağını belirler. Bu bölüm, vec2 / vec3 / vec4 ayrıntılarına geçmeden önce «neden paket halinde taşıyoruz?» sorusuna kısa cevap verir.

Aynı paket, farklı anlam

Üç eksenli konum için üç ayrı float (px, py, pz) tutmak yerine tek bir vec3 yazmak hem okunabilirliği artırır hem de dot, cross, normalize gibi işlemlerin doğrudan uygulanmasını sağlar — ayrıntılı liste vektör matematiği bölümünde. Renk için vec3 (RGB) veya şeffaflıkla vec4 (RGBA); 2B doku için vec2 (UV) — matematiksel yapı «N adet float yan yana», anlamsal yorum bağlama bağlıdır.

Buffer tarafında da aynı fikir görülür: position attribute’u üçlü dizidir, shader’da vec3 olarak okunur ( itemSize). CPU’daki Vector3 ( Vektör mantığı) ile isim benzerliği vardır; GLSL tarafında vec3 shader register’ıdır.

Boyut uyumu ve broadcast

Vektör tipleri skalerlerle serbestçe «karışmaz» — her ifadenin boyutu tanımlıdır. vec3 + float çoğu profilde geçerlidir: skaler her bileşene eklenir ( broadcast). Örneğin position + uOffset düşünülebilir; uOffset tek sayıysa üç eksene de uygulanır.

Buna karşılık vec3 + vec2 çoğu zaman derleme hatası verir — boyutlar uyuşmaz. Benzer şekilde vec4 bekleyen bir fonksiyona vec3 vermek de hataya gider; gerekirse yapıcı veya swizzle ile açıkça genişletirsiniz ( yapıcılar, swizzle). Bu disiplin, tip zorunluluğu ile uyumludur; operatör kuralları Operators konusunda ayrıntılandırılır.

Vec2, Vec3, Vec4

GLSL’de vec son eki, kaç adet float bileşeninin tek register’da paketlendiğini söyler: vec2 iki, vec3 üç, vec4 dört. Aşağıda her boyutun sahne ve shader’daki tipik rolü özetlenir; yapıcı ve swizzle ayrıntıları §3–5’te kalır.

Tür adı = bileşen sayısı

Bileşen sayısı tür adında yazar — ayrı bir «uzunluk» alanı taşımazsınız; derleyici vec3 ifadesini gördüğünde üç kayan nokta bekler. Hangi veri kanalında kaç bileşen gittiği, shader girişinde de aynı sayıyla bildirilir: attribute vec3 position, uniform vec2 uResolution, varying vec2 vUv gibi. Bu eşleşme, Uniforms & varyings konusunun özüdür; burada yalnızca «boyut seçimi» tarafını netleştiriyoruz.

vec2 — iki bileşen

İki bileşenli paket, düzlemde çalışan her şey için doğal türdür. En yaygın örnek doku koordinatıdır: UV eksenleri (u, v) genelde vec2 olarak taşınır; vertex shader’da uv attribute’undan okunur, fragment tarafına varying vec2 vUv ile aktarılır ( GLSL · varying, ilk örnek).

Aynı tür 2B ofsetler, ekran çözünürlüğünün yalnızca genişlik–yükseklik çifti ( uResolution.xy), gradyan yönü veya «iki skaleri birlikte taşı» durumlarında da görülür. Fragment shader’da doku örneklemek için çoğu zaman elinizdeki koordinat vec2’dir — üçüncü bir eksen gerekmez.

vec3 — üç bileşen

Üç bileşen, 3B uzayın «doğal» boyutudur. Model (veya dünya) uzayında konum position.xyz, yüzey yönü normal, düz RGB renk veya ışık yönü vektörü tipik olarak vec3 ile ifade edilir. Buffer’da itemSize 3 olan attribute kanalları vertex shader’da vec3 olarak okunur — tampon düzenine kısa köprü §1 ve itemSize konusundadır.

Renk şeffaflık gerektirmiyorsa vec3 yeterlidir; alpha kanalı eklenecekse vec4’e geçilir. Konum ile renk aynı türde paketlense de anlamları farklıdır — karıştırmamak için isimlendirme (baseColor, lightDir) ve uniform/varying bildirimleri net kalmalıdır.

vec4 — dört bileşen

Dört bileşen, hem 3B+ek bilgi hem de grafik boru hattının zorunlu çıktı formatını kapsar. Homojen koordinat (x, y, z, w) perspektif projeksiyonda w ile bölme yapılana kadar dördüncü bileşeni taşır; vertex shader’ın standart çıktısı gl_Position her zaman vec4’tür ( GLSL · vertex aşaması).

Fragment tarafında vec4 çoğunlukla RGBA rengi ( gl_FragColor veya out vec4) temsil eder; bazen de «üç vektör + tek skaler» paketi olarak kullanılır (örneğin yön + yoğunluk). vec3’ten vec4’e geçerken dördüncü bileşeni bilinçli atamak gerekir — yapıcılar bölümündeki
vec4(vec3, float) kalıbı bu boşluğu kapatır.

Yapıcılar ve kısaltmalar

vec2 uv = vec2(0.5, 0.25);
vec3 red = vec3(1.0, 0.0, 0.0);
vec4 white = vec4(1.0);
vec3 fromVec4 = vec3(vColor.rgb);
vec4 promoted = vec4(vUv, 0.0, 1.0);

Tek skaler vererek tüm bileşenleri doldurma (vec3(1.0)) yaygındır. Daha küçük vektörden büyüğe geçiş: vec4(vec3, float) veya swizzle + yapıcı birleşimi. Tür dönüşümü için float(), int() skalerde olduğu gibi vektör yapıcılarına da uygulanabilir (vec3(intVec)).

Bileşen erişimi: .xyzw ve .rgba

Vektör değişkeni bellekte sıralı float bileşenlerinden oluşur; GLSL bunlara nokta notasyonuyla erişmenizi sağlar. Bu bölüm tek bileşen ve isimlendirme aileleri (.xyzw / .rgba) üzerinde durur; birden fazla harfle alt vektör türetmek ( vUv.yx, color.bgra) bir sonraki §5 · Swizzle konusundadır.

Tek bileşen: nokta notasyonu

En basit erişim, tek harfle okumaktır: position.x, normal.y, color.a. Sonuç her zaman float’tır — vektörden bir skaler «çıkarır», türü değiştirmez. Örneğin clip uzayına yakın bir noktada perspektif için gl_Position.w kullanılır; fragment tarafında alpha testi için baseColor.a okunabilir.

Bileşen sırası yapıcıyla verdiğiniz sırayla aynıdır ( §3 · yapıcılar): ilk argüman .x / .r, ikincisi .y / .g … Bu sayfa boyunca vec3 / vec4 rolleri §2’de özetlendi; burada «hangi harf hangi kanal?» sorusuna odaklanıyoruz.

.xyzw ve .rgba / .stpq

GLSL, aynı fiziksel bileşenlere iki (üç) paralel isimlendirme ailesi tanır. Bunları aynı vektörde karıştırmak derleme hatası değildir — anlam bağlamına bağlı bir okuma kolaylığıdır:

  • xyzw: Konum, yön, clip uzayı, genel 3B/4B geometri — «eksen» veya «bileşen indeksi» okuması. position.xyz, lightDir.xy bu aileye yakışır.
  • rgba: Renk kanalları — kırmızı, yeşil, mavi, alpha. vColor.rgb, texel.a fragment shader’da sık görülür.
  • stpq: Doku / örnekleme bağlamında alternatif kanal adları (.s, .t …); pratikte çoğu ekip uv veya rgba ile kalır, stpq daha çok eski veya API dokümantasyonunda karşılaşılır.

Özet tablo (aynı vec4 için):

// 1. bileşen   2.       3.       4.
//  .x  .y  .z  .w  ≡  .r  .g  .b  .a  ≡  .s  .t  .p  .q

Aynı bileşen, farklı harf — tutarlılık

vColor.r ile vColor.x aynı birinci bileşendir; derleyici için fark yoktur, yalnızca okuyanın zihnindeki model değişir — biri «kırmızı kanal», diğeri «ilk eksen». Karışık aile örneği: vec4 p = vec4(1.0, 2.0, 3.0, 1.0); float u = p.x; float v = p.g; geçerlidir; p.x ile p.r yine aynı değeri verir.

Ekip çalışmasında dosya veya proje genelinde tek aileyi tercih etmek (konum shader’ında çoğunlukla xyz, renk shader’ında rgba) diff’leri okunur kılar. Çok harfli permütasyonlar ve atama kuralları swizzle bölümünde; kısa hatırlatma ayrıca GLSL giriş · altın kurallar listesindedir — burada yalnızca «harf = kanal indeksi, aile = yorum» ayrımını netleştiriyoruz.

Swizzle: yeniden sıralama ve alt vektör

Swizzle, aynı kaynaktan yeni bir vektör (veya skaler) türetmektir — tek harfli erişim §4’ün çok bileşenli uzantısıdır. vUv.yx UV’yi ters çevirir, normal.xyz dörtlü paketten vec3 kesiti alır, color.bgra kanalları permüte eder. GLSL’in en sık kullanılan sözdizimi özelliklerinden biridir; prosedürel desen ve maske kodunda satır sayısını ciddi azaltır.

vec4 c = texture2D(uMap, vUv);
vec3 rgb = c.rgb;
float a  = c.a;
vec2 rg  = c.rg;
vec3 bump = c.agb; // okuma: kanallar yeniden seçilir

Kurallar ve tuzaklar

Swizzle’ın gücü, okuma tarafındaki esnekliktir; yazma (atama) tarafında ise kanal çakışmasına karşı sıkı kurallar vardır. Aşağıdaki dört başlık günlük shader hatalarının çoğunu önler.

Çıktı boyutu

Seçtiğiniz harf sayısı, sonuç türünü doğrudan belirler: .xyvec2, .xyzvec3, tek harf → float. Kaynak vec4 olsa bile position.xy yalnızca iki bileşenli bir alt vektördür — boyut «kaynak türüyle aynı kalır» diye bir kural yoktur.

Bu kural, §2’deki boyut disipliniyle uyumludur: swizzle sonrası elinizdeki türü bir sonraki işleme (ör. dot, mix) verirken bileşen sayısını bilinçli seçmiş olursunuz.

Tekrar ve genişletme

Aynı harfi birden fazla seçmek okuma tarafında geçerlidir: normal.xxx vec3 üretir — bir skaleri üç eksene «yaymak» (genişletme) için kullanılır. vec3(n.xxx) gibi kalıplar, tek bileşenli bir girdiyi üç bileşenli bir türe taşımak istediğinizde yapıcıyla birlikte görülür.

Tekrar, okuma tarafında serbest olsa da atama tarafında farklı kurallara tabidir (aşağıdaki bölüm); vColor.xyx = … gibi yazımlar bu yüzden reddedilir.

Atama kısıtı (l-value)

Swizzle sol tarafta — yani yazılacak hedefte — kullanılacaksa kurallar sıkıdır. Örneğin vColor.rg = vec2(1.0, 0.0); çoğu profilde mümkündür: yalnızca kırmızı ve yeşil kanallara yazar, mavi ve alpha dokunulmaz kalır.

Örtük çakışan kanallara yazmak geçersizdir: vColor.xyx = vec3(1.0); birinci ve üçüncü hedefin ikisi de x kanalına işaret ettiği için derleyici reddeder — hangi değerin kalacağı belirsizdir. Okuma tarafında böyle bir belirsizlik yoktur; vec3 v = vColor.xyx; her üç bileşene de aynı x değerini kopyalar.

vColor.rg = vec2(1.0, 0.0);   // çoğu zaman geçerli
// vColor.xyx = vec3(1.0);    // geçersiz: çakışan yazma hedefi

Karışık isimlendirme ailesi

.xy ile .rg aynı fiziksel bileşenlere denk gelir; fark yalnızca okuma kolaylığıdır ( §4 · isim aileleri). Okuma swizzle’ında .xgr gibi harf ailelerini karıştırmak da çoğu GLSL sürümünde geçerlidir — örneğin c.agb dört kanallı bir dokudan permütasyon üretir.

Yine de ekip içinde tek aileyi sürdürmek diff okunurluğunu artırır; karışık yazım «mümkün» olsa bile her satırda zihinsel çeviri gerektirir. Atama tarafında aile seçimi değil, kanal çakışması asıl hatayı üretir.

Kısa swizzle hatırlatması ayrıca GLSL giriş · altın kurallar listesindedir; bu sayfa shader pratiğindeki dört kurala odaklanır.

Vektör matematiği (özet)

Vektörler skaler gibi +, - ile toplanır; * ve / çoğu bağlamda bileşen bazında uygulanır (broadcast kuralları Operators konusunda). §1’de «paket» olarak seçtiğiniz vec3 artık bu yerleşiklerle geometri ve renk hesabına girer — tam API listesi Built-in functions sayfasına bırakılır; burada shader pratiğinde en sık beş fonksiyon özetlenir.

Length — uzunluk

Length(v) vektörün Öklid uzunluğunu tek bir float olarak döndürür. Mesafe karşılaştırması, hız büyüklüğü veya «bu normal çok kısa mı?» kontrolü için kullanılır. Girdi vec2, vec3 veya vec4 olabilir; sonuç her zaman skalerdir — boyut uyumu §1 ile uyumludur.

Normalize — birim yön

Normalize(v), vektörü aynı yönde tutup uzunluğu 1 yapar — ışık yönü, bakış vektörü veya yüzey normali girdiğinde sık görülür. Matematiksel olarak v / length(v) düşüncesine denktir.

Sıfır vektörde bölme tanımsızdır; üretim shader’larında pratikte v / max(length(v), 1e-6) veya benzeri küçük epsilon koruması görülür. Aksi halde bazı GPU’larda NaN üreten pikseller oluşabilir.

Dot — iç çarpım

Dot(a, b) iki vektörün iç çarpımını verir (skaler). Lambert tarzı aydınlatmada klasik kalıp float ndotl = max(dot(normal, lightDir), 0.0); şeklindedir — yüzey normali ile ışık yönü ne kadar hizalı, parlaklık o kadar yüksek. Aynı işlem, bir vektörün başka bir yön üzerindeki projeksiyon fikrine de bağlanır.

a ve b aynı boyutta olmalıdır (vec3 ile vec3 gibi); farklı boyutlar derleme hatasıdır.

Cross — çapraz çarpım

cross(a, b) yalnızca vec3 ile tanımlıdır; sonuç yine vec3’tür ve her iki girdiye de dik bir vektör üretir. İki kenar vektöründen yüzey normali türetmek veya teğet uzayında üçüncü eksen bulmak için kullanılır — normal haritalama ve geometri shader’larında sık görülür.

Sıra önemlidir: cross(a, b) ile cross(b, a) zıt yönlüdür; elinizle sağ kuralına göre hangi yönü istediğinizi kontrol edin.

Mix — karışım / geçiş

mix(a, b, t), a ile b arasında doğrusal geçiş yapar: t == 0.0 tamamen a, t == 1.0 tamamen b. Renk karışımı
(mix(baseColor, highlight, mask)) veya vektör interpolasyonu için kullanılır; a ve b aynı vektör türünde olmalıdır.

t skaler olabileceği gibi vektör de olabilir — örneğin mix(colorA, colorB, vUv.x) yatay gradyan, mix(vec3(0.0), vec3(1.0), vec3(t)) ise kanal başına farklı ağırlık verir. smoothstep ile birlikte yumuşak geçişler prosedürel desenlerin temelidir; ayrıntı Built-in functions konusuna bırakılır.

Three.js Vector3 API’si ( Vektör mantığı) CPU tarafındaki karşılıktır — shader’da karşılık vec3 ve bu yerleşiklerdir; matris çarpımı §7’de özetlenir.

Vektör ve matris: mat4 * vec4

Bu sayfa boyunca vektörleri paketleme, swizzle ve yerleşiklerle işledik; vertex shader’da onlar çoğu zaman matris çarpımının girdisi veya çıktısı olur. §2’de gl_Position’ın vec4 olduğu söylenmişti — burada bu dörtlünün mat4 zinciriyle nasıl üretildiğine odaklanıyoruz. Matris yapıcıları ve mat2 / mat3 tanımları GLSL giriş · matris konusundadır; burada vektör–matris köprüsü özetlenir.

Vertex dönüşüm zinciri

Three.js ve benzeri motorlarda konum, önce model–view, sonra projeksiyon ile clip uzayına taşınır. Tipik satır:

gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

position attribute’u çoğu zaman vec3’tür; homojen koordinat için dördüncü bileşen 1.0 ile yapıcı veya vec4(position, 1.0) kalıbıyla eklenir. Çarpım sırası ve uniform isimleri sahneye göre değişir; önemli olan «mat4 * vec4vec4» boyut uyumudur. Örnek akış GLSL · vertex aşaması ve ilk örnek kodunda görülür; projectionMatrix gibi matrisler uniform olarak gelir.

Column-major düzen

GPU tarafında matrisler sütun öncelikli (column-major) saklanır; GLSL’de mat4 * vec4 yazımı bu düzenle uyumludur. JavaScript’te Matrix4 elemanlarını elle shader’a yazarken satır/sütun karışıklığı sık hata kaynağıdır — vektör paketini doğru seçmek yetmez, matrisin bellek düzeni de eşleşmelidir.

Bu bölüm matris çarpımının cebirsel kanıtına girmez; «shader’da neden vec4 gerekir?» sorusuna pratik cevap verir. Teorik matris özeti yukarıdaki GLSL giriş bağlantısındadır.

Normal taşıma: mat3 ve normalMatrix

Konum mat4 ile taşınırken yüzey normali çoğu zaman ayrı bir lineer dönüşüm ister — ölçek veya perspektif içeren tam mat4 normali bozabilir. Three.js normalMatrix (mat3) uniform’ı bu iş için sağlanır; shader’da yerel normal üzerinde çarpılır:

vec3 worldNormal = normalize(normalMatrix * normal);

Burada girdi ve çıktı vec3’tür — §6’daki normalize yine devreye girer. Aydınlatmada dot(normal, lightDir) hesaplanmadan önce normalin doğru uzayda ve birim uzunlukta olduğundan emin olun; normal haritası ve TBN uzayı ileri konularda ele alınır.

Boyut uyumu özeti

mat4 * vec4 geçerlidir; mat4 * vec3 çoğu zaman derleme hatasıdır — önce vec4’e yükseltin. mat3 * vec3 normal ve renk yönü gibi saf 3B vektörler içindir. Vektör–vektör kuralları §1 ile aynı disiplin: tür adındaki boyut, çarpımda da korunur veya bilinçli genişletilir.

Holodepth notu

Yeni bir efekt yazarken her «paket» için türü bilinçli seçin: UV → vec2, renk → vec3 veya vec4, dünya normali → vec3. Uzun ifadeyi tek satırda bırakmak yerine vec2 uv2 = vUv * 2.0; ve float band = smoothstep(0.2, 0.8, uv2.y); gibi ara vektörler hem hatayı bulmayı kolaylaştırır hem swizzle hatalarını azaltır.