holodepth

Shader & GLSL · Vertex / fragment

Veri akışı

Köşeden piksele veri yolculuğu

GLSL sözdizimi serisi (VariablesUniforms & varyings) shader dosyasında ne bildirildiğini sabitledi: uniform, attribute, varying ve türleri. Vertex / fragment serisi ise ne zaman ve hangi yönde aktığını sorar — JavaScript sahnesinden GPU invokasyonlarına kadar tek bir harita.

Pratikte iki dünya üst üste çalışır: Three.js tarafında geometry, material.uniforms ve renderer.render; shader tarafında vertex ve fragment programları. Veri akışı sayfası bu iki dünyayı «köprü» kelimesiyle değil, zaman çizelgesi ile anlatır — draw call anında hangi paketin vertex’e, hangisinin raster sonrası fragment’e ulaştığını gösterir.

Pipeline adımlarının tam listesi Shader pipeline, enterpolasyonun nasıl karıştırdığı Interpolation, model / görüntü / clip uzayları Coordinate spaces konusundadır. Burada yalnızca taşıyıcılar (attributevaryinguniform okuma noktaları) ve akış yönü öne çıkar; aşağıdaki §2 bu haritayı tek kareye indirger.

Neden veri akışı?

Shader yazarken iki soru üst üste biner. Birincisi: «Bu değişken hangi anahtar sözcükle bildirilir?» — uniform, attribute veya varying? İkincisi: «Bu değer vertex’te mi, fragment’te mi, arada mı kullanılır?» Birinci soru Uniforms & varyings konusundaydı; üç karar sorusu ( Uniforms · üç soru) orada özetlenir. İkinci soru bu sayfanın konusudur — kod derlenir ama yanlış aşamada okunursa sessiz veya link hatası üretir.

İki soru farklı hata türlerine yol açar. Yanlış anahtar sözcük çoğu zaman derlemede veya link aşamasında yakalanır (attribute fragment dosyasında geçemez). Yanlış aşama ise bazen derlemeyi geçer: örneğin fragment’te texture2D(uMap, uv) yazmak — uv vertex’teki attribute adıdır, fragment’te okunacak isim enterpolasyonlu vUv olmalıdır ( §6). Uniform ismi yanlışken program yine derlenebilir; akış doğru görünür ama değer bağlanmaz ( Uniforms · sessiz hatalar).

Bu bölüm, üstteki giriş paragraflarında söylenen «ne zaman / hangi yönde» ayrımını sayfa sınırları ve okuma sırasıyla somutlaştırır; §2’den itibaren zincir ve örnek kod devreye girer.

Bu sayfa ne anlatır, ne anlatmaz?

Holodepth’te vertex / fragment serisi, sözdizimi serisinin ardından gelir. Sözdizimi dosyada ne yazılır; bu seri değer hangi pipeline adımında okunur diye sorar. Aşağıdaki ayrım, kardeş sayfalara tekrar etmeden bu sayfanın payını çizer.

Anlatır:

  • CPU → draw call → vertex → raster → fragment zincirinde taşıyıcı seçimi (§2 özet, §3 giriş, §4§5 örnekler).
  • gl_Position ile konumun raster’a gidişi; varying ile köşe verisinin fragment girdisine bağlanması — formül değil, yön.
  • Yanlış aşamada veri arama ( §6).

Anlatmaz (bilinçli sınır; her konu kendi sayfasında derinleşir):

Seyrek vertex, yoğun fragment

Vertex shader seyrek çalışır: invokasyon sayısı köşe sayısına yakındır. Fragment shader yoğun çalışır: üçgen başına çok sayıda aday piksel üretilir. Bu yüzden attribute yalnızca vertex’e bağlanır; köşe verisini fragment’e taşımak için varying ve rasterizasyon gerekir. Genel rol ayrımı GLSL giriş · shader türleri bölümünde anlatılır; burada yalnızca «veri neden köprü ister?» sorusuna cevap verilir.

Köprü ihtiyacı, «seyrek → yoğun» farkından gelir. Vertex’te bir kez yazılan vUv = uv;, raster sonrası binlerce fragment invokasyonuna enterpolasyonla yayılır; JavaScript’te bu adımın karşılığı yoktur ( Interpolation). Buna karşılık uTime gibi çağrı boyunca sabit veri her invokasyonda aynıdır; varying ile taşımak yerine uniform yeterlidir ( Uniforms · üç soru, birinci madde).

Özet: attribute ve uniform vertex girişidir; yalnızca köşeye özel ve fragment’te de gereken bilgi varying ile «arada» taşınır. §3 attribute ve uniform girişini, §4§5 vertex çıkışı ve fragment girişini kodla gösterir.

Seri içindeki yer

Vertex / fragment alt serisini şu sırayla okumanız önerilir: Veri akışı (bu sayfa) → Shader pipelineInterpolationCoordinate spaces. Önce «veri nereye akıyor?», sonra «pipeline’da hangi donanım adımı var?», sonra «köprü nasıl karışıyor?», en son «konum hangi uzayda?».

Her kardeş sayfa tek bir soruya odaklanır; bu sayfada yalnızca bağlantıları hatırlatıyoruz: Shader pipeline raster öncesi/sonrası donanım adımlarını (derinlik, blending) listeler — §2’deki 6. madde oraya gider. Interpolation varying’lerin «arada» nasıl karıştığını açar; burada yalnızca köprünün varlığı yeter. Coordinate spaces gl_Position öncesi matris zincirini işler; veri akışı yalnızca «konum vertex çıkışıdır» der.

Uniforms & varyings sayfasını bitirdiyseniz, bu sayfayı doğrudan §2 özet listesiyle de okuyabilirsiniz; sözdizimi tekrarı yapılmaz. İlk kez shader yazıyorsanız §1§2§3 sırası zihinde haritayı kurar, §4§5 örnekleri doldurur.

Aşağıdaki §2, tek bir render karesinde bu akışın özet zincirini listeler; sonraki bölümler her taşıyıcıyı örnek kodla açar.

Tek karede özet yol

renderer.render(scene, camera) tek bir kare çizer. O karede sahne tarafında önce veri hazırlanır, sonra GPU her mesh için ayrı bir draw call gönderir. Aşağıdaki liste tek bir çizim için kronolojik özetdir — «şu anda veri nerede, hangi taşıyıcıyla okunuyor?» sorusuna cevap verir. Genel vertex / fragment rol ayrımı GLSL giriş · shader türleri bölümünde de özetlenir; burada yalnızca taşıyıcılar işaretlenir.

Liste iki bölüme ayrılır: 1–2 adımlar JavaScript / CPU tarafında (shader henüz çalışmaz); 3–5 adımlar GPU’da (vertex → raster → fragment). 6. adım shader sonrası donanım işleridir.

  1. CPU / Three.js — geometry attribute tamponları (konum, UV, normal) ve material.uniforms güncellenir. Veri GPU’ya yüklenmiş veya güncellenmiş olmalıdır; henüz vertex shader invokasyonu yoktur.
  2. Draw call — Seçilen programa bağlanır: vertex + fragment shader, attribute tampon bağlamaları, uniform seti. Bu paket çağrı boyunca sabitlenir ( §3).
  3. Vertex shader — Her köşe için bir invokasyon: attribute okunur, uniform uygulanır; gl_Position (konum çıkışı) ve varying (fragment’e taşınacak köşe verisi) yazılır ( §4).
  4. Rasterizasyon — Üçgenler ekranda aday piksellere bölünür; köşe varying değerleri iç noktalara enterpolasyonla yayılır. Bu adım shader dosyası yazılmaz; GPU pipeline’ının «arada» adımıdır ( Interpolation).
  5. Fragment shader — Her aday piksel için invokasyon: enterpolasyonlu varying + aynı draw call’ın uniform’ları okunur; renk (gl_FragColor veya out) üretilir ( §5). attribute bu aşamada yoktur.
  6. Sonrası — Derinlik testi, stencil, blending, framebuffer’a yazma vb. Shader pipeline konusundadır; veri taşıyıcısı değil, çıktının ekrana nasıl birleştiği sorulur.

Taşıyıcılar hangi adımda?

Aynı isimler farklı adımlarda farklı rol oynar. Listeyi ezberlemek yerine her taşıyıcı için «ilk nerede hazırlanır, nerede okunur, nerede biter?» üçlüsünü düşünün — anahtar sözcük seçimi Uniforms · kim, nerede okur? konusunda kalır.

  • attribute — 1. adımda geometry tamponunda güncellenir, 2. adımda draw call ile GPU’ya bağlanır; okuma yalnızca 3. adımda, köşe başına bir kez (position, uv). 5. adımda attribute yoktur — fragment’te aynı ismi kullanmak akış hatasıdır; köşe verisi önce varying ile taşınmalıdır.
  • uniform — 1. adımda material.uniforms ile beslenir, 2. adımda pakete sabitlenir; 3. ve 5. adımda aynı değer okunur (matris, uTime, doku tutamacı). Köşeden köşeye değişmez; bu yüzden 4. adımda enterpolasyon görmez — paylaşılan çağrı parametresidir.
  • varying — 3. adımda vertex’te yazılır (vUv = uv;), henüz fragment girdisi değildir. 4. adımda raster üçgen içinde karıştırır; formül Interpolation konusundadır. 5. adımda fragment aynı isimle okur — değer artık köşe değil, o pikseldeki enterpolasyonlu sonuçtur.

Kısa kontrol: köşeye özel → attribute + (gerekirse) varying; çağrıya ortak → uniform; vertex’te üretilip fragment’te lazım → varying zinciri (3 → 4 → 5).

gl_Position taşıyıcı değildir; vertex’in zorunlu konum çıkışıdır ve 4. adıma girdi sağlar. Konumun hangi matrislerle çarpıldığı Coordinate spaces konusundadır.

Haritayı pratikte kullanmak

Shader’da bir isim «tanımsız» veya yanlış görünüyorsa listede ilk geçtiği adımı işaretleyin. Fragment’te uv arıyorsanız 5. adımda attribute olmadığını, 3–4–5 zincirinde vUv varying’inin gerekli olduğunu hatırlayın. Uniform değeri bir kare gerideyse sorun çoğu zaman 1. adımda — render öncesi güncelleme ( §6).

Bu sayfa 3–5. adımlardaki taşıyıcıları örnek kodla açar ( §3 giriş, §4 vertex çıkışı, §5 fragment girişi). 1–2’nin Three.js ayrıntısı §3’te; 6. adımın donanım tablosu Shader pipeline sayfasında kalır.

Giriş: attribute ve uniform

§2’deki zincirin 1–3. adımları bu bölümün konusudur: draw call öncesi CPU tarafında veri hazırlanır; çağrı anında GPU’ya iki tür giriş bağlanır — köşe başına attribute, çağrı boyunca sabit uniform. varying henüz giriş değildir; vertex çıkışı olarak §4’te ele alınır.

Draw call başladığında bu iki kanal akar. Attribute kanalları geometry tamponundan okunur; her vertex invokasyonu farklı köşe satırını görür. Uniform değerleri aynı çağrıda tüm invokasyonlarca paylaşılır — matrisler, zaman, doku tutamacı. Bildirim sözdizimi ve Three.js köprüsü Uniforms · uniform ve attribute bölümlerindedir; burada yalnızca «hangi adımda, hangi yönde?» sorulur.

Draw call öncesi: tampon ve uniform paketi

renderer.render çağrılmadan önce sahne tarafında iki hazırlık yapılır. BufferGeometry attribute tamponları (konum, UV, normal) GPU’ya veya paylaşımlı belleğe yüklenmiş olmalıdır; isimler shader’daki attribute bildirimleriyle eşleşir. material.uniforms veya ShaderMaterial üzerindeki alanlar aynı draw için shader’daki uniform isimlerine bağlanır. Tampon yapısı ve güncelleme sıklığı Attributes & buffer’lar konusundadır; burada önemli olan, attribute’un «render anından önce hazır tampon» olduğudur.

Uniform güncellemesi çoğu projede kare başında yapılır (uTime, uResolution). Değer draw call sırasında sabitlenir; vertex ve fragment aynı paketi okur. Güncellemeyi render sonrasına bırakmak bir kare gecikme üretir ( §6).

Draw call anında: program + tampon + uniform

Tek bir mesh çizimi, GPU’ya «şu program, şu attribute bağlamaları, şu uniform seti» paketi gönderir. Paket draw call boyunca değişmez: aynı üçgenin üç köşesi farklı position / uv attribute değerleri görür; hepsi aynı modelViewMatrix ve uTime uniform’unu görür. Bu ayrım, §1’deki «seyrek vertex, yoğun fragment» notunun pratik karşılığıdır — köşe verisi taşınır, çağrı parametreleri paylaşılır.

Three.js içinde WebGLRenderer bu paketi materyal ve geometry’den derler; siz akışı shader dosyasında okursunuz: vertex’te attribute + uniform, fragment’te yalnızca uniform (ve §5’teki enterpolasyonlu varyings). Fragment shader geometry tamponuna doğrudan erişemez; bu kısıt akış hatası değil, pipeline kuralıdır.

Akış dilinde kısa özet

Rol tablosunun tam listesi Uniforms · kim, nerede okur? bölümündedir. Veri akışı perspektifinden yalnızca zaman çizelgesi hatırlanır:

  • attribute — Yalnızca vertex girişi; değer draw call öncesi yüklenen tampondan, invokasyon başına bir köşe satırı olarak okunur.
  • uniform — Vertex ve fragment girişi; değer draw call paketinde sabit; aynı çağrıdaki tüm köşe ve piksel invokasyonları paylaşır.
  • varying — Bu bölümde giriş değil; vertex’te yazılır, raster sonrası fragment’te okunur ( §4, Interpolation).

«Bu değer köşeye mi özel, çağrıya mı ortak?» sorusu Uniforms · üç soru ile seçim yapar; «vertex’te mi okunur?» sorusu bu sayfada cevaplanır.

Vertex’te birlikte okuma

Tipik vertex girişi şöyle okunur: position, uv, normal attribute’tan gelir; modelViewMatrix, projectionMatrix uniform’dan. Vertex bu iki kaynağı birleştirip gl_Position yazar ve fragment’e taşınacak varyings atar — örnek akış aşağıdaki editörde. Konumun hangi uzayda çarpıldığı Coordinate spaces konusunda; burada yalnızca «girişler vertex’te bir araya gelir» notu yeterlidir.

Attribute yalnızca vertex girişidir. Uniform aynı draw call paketinin parçası olarak fragment’te de okunur — çoğu zaman doku, zaman veya ışık; vertex çıkışları ve fragment girişi §4§5’te devam eder.

Vertex çıkışı: konum ve varying

Vertex shader’ın iki tür çıkışı vardır. gl_Position donanımın sabit çıktısıdır — clip uzayına yakın konum; rasterizer hangi piksellerin aday olacağını buna göre belirler. Model → görüntü → projeksiyon zinciri ve uzay isimleri Coordinate spaces konusundadır.

İkinci çıkış varying atamalarıdır: vUv = uv;, vNormal = normalMatrix * normal;. Vertex’te hesaplanır, fragment’te tüketilir; aradaki enterpolasyon GPU pipeline’ında olur — JavaScript’te karşılığı yoktur ( Uniforms · varying).

// Giriş: attribute + uniform
attribute vec3 position;
attribute vec2 uv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

varying vec2 vUv;

void main() {
  vUv = uv;  // fragment'e taşınacak
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

Fragment girişi: enterpolasyonlu paket

Fragment shader çalıştığında attribute artık yoktur; enterpolasyonlu varying değerleri ve aynı draw call’ın uniform’ları okunur. Tipik akış: varyings ile UV/normal al → uniform ile doku/zaman/ışık al → yerel hesap → renk çıkışı.

texture2D(uMap, vUv) bu akışın kısa özeti: uMap uniform (çağrı başına sabit tutamak), vUv varying (köşeden gelen koordinat). texture2D Built-in · doku konusundadır.

// Giriş: varying + uniform (attribute yok)
varying vec2 vUv;
uniform sampler2D uMap;
uniform float uTime;

void main() {
  vec4 albedo = texture2D(uMap, vUv);
  float pulse = 0.5 + 0.5 * sin(uTime);
  gl_FragColor = vec4(albedo.rgb * pulse, albedo.a);
}

Akışa özel karışıklıklar

Aşağıdaki liste yanlış aşamada veri aramayı vurgular — doğru isim yanlış adımda kullanıldığında da hata oluşur. Derleme veya link hatası yerine siyah ekran, donmuş animasyon veya «her pikselde aynı UV» gibi belirtiler çoğu zaman akış kopuğuna işaret eder. İsim, tür ve Three.js sözleşmesi
( { value: … }, yanlış uniform adı, attribute kanalı eksikliği) Uniforms · sık hatalar bölümünde kalır.

Her madde §2 zincirindeki adım numarasıyla okunabilir — «bu değer hangi adımda hazırlanır, nerede tüketilir?» sorusu akış hatalarını ayırmada yeterlidir.

  • Fragment’te (5. adım) texture2D(uMap, uv)uv yalnızca vertex’te (3. adım) okunan attribute’tur; fragment’te geçerli değildir. Doğrusu: vertex’te vUv = uv;, 4. adımda enterpolasyon, 5. adımda texture2D(uMap, vUv) ( §5).
  • Zaman (uTime) veya global ışık rengini varying ile taşımak — değer çağrı boyunca sabittir; 2. adımda pakete uniform olarak bağlanmalı, 3. ve 5. adımda doğrudan okunmalıdır. Varying köşe verisi içindir; sabit parametreyi «taşımak» gereksiz ve bazen yanlış enterpolasyon üretir ( Uniforms · üç soru, birinci madde).
  • Vertex’te hesaplanan değeri (dünya normali, özelleştirilmiş UV) varying ile fragment’e göndermeyi unutmak — 3. adımda üretilen veri 4. adımdan geçmeden 5. adımda yoktur. Belirti: fragment’te sabit veya sıfır değer; düzeltme vertex’te atama + fragment’te aynı varying bildirimi ( §4).
  • Uniform güncellemesini render sonrasına koymak — veri 1. adımda hazırlanmalı, 2. adımda pakete girmelidir; bir sonraki karede görünür (bir kare gecikme). Animasyon ve etkileşimli uniform’larda ilk kontrol noktası JavaScript zamanlamasıdır, shader sözdizimi değil ( §3 · draw call öncesi).
  • mesh.position (nesne dönüşümü, sahne grafiği) ile köşe position attribute’unu karıştırmak — ilki sahne düğümü, 2. adımda modelMatrix uniform’u ile shader’a gider; ikincisi geometry tamponunda, 3. adımda attribute olarak okunur. İkisini aynı kavram sanmak konum kaymasına yol açar; uzay ayrımı Coordinate spaces konusundadır.

Şüphede: §2 zincirini işaretleyin

Değişken için iki nokta çizin: ilk görünüm (hangi adımda veri kaynağa bağlanır?) ve tüketim (hangi adımda shader okur?). Örnek: doku rengi — uMap 1–2’de uniform, vUv 3’te yazılır, 5’te texture2D ile okunur. Arada 4. adım atlanmışsa veya 5’te hâlâ 3. adımın attribute adı kullanılıyorsa akış kırılmıştır.

Anahtar sözcük doğru ama sonuç yanlışsa önce adımı, adım doğruysa sözleşmeyi kontrol edin. Taşıyıcı–adım eşlemesi için §2 · taşıyıcılar hangi adımda? özetine dönün; bildirim ve isim listesi Uniforms · sık hatalar’da kalır.

Holodepth notu

Vertex / fragment serisini şu sırayla okuyun: Veri akışı (bu sayfa) → Shader pipeline → Interpolation → Coordinate spaces. Yeni materyalde kağıda attribute → varying → uniform oklarını çizin; sonra shader ve material.uniforms tarafını doldurun.