WebGL 2 · OpenGL ES 3.0 · Tarayıcı GPU
WebGL 2: Modern grafik standartları ve genişletilmiş kapasite
WebGL 2, WebGL 1’in “üstüne yama” gibi düşünülmemesi gereken bir sürüm yükseltmesidir: tarayıcıda OpenGL ES 3.0 yeteneklerini standartlaştırır. WebGL 1’de çoğu zaman extension veya dolambaçlı yollarla kurulan pek çok özellik, WebGL 2’de çekirdek spesifikasyonun parçası hâline gelir.
Bu sayfa, API rehberi değil; WebGL 2’nin neden “daha az CPU, daha çok GPU işi” ve “daha az hile, daha çok sözleşme” anlamına geldiğini kurar. Holodepth’teki diğer WebGL konularıyla çakışmadan, yalnızca hangi kapıların açıldığını ve bunların hangi mevcut sayfalara bağlandığını netleştirir.
WebGL 1 vs. WebGL 2: neden geçiş?
WebGL 1, temelde OpenGL ES 2.0 çağının kısıtlı ama taşınabilir bir profilini tarayıcıya taşır. WebGL 2 ise OpenGL ES 3.0 ile aynı aileye yaklaşır: daha geniş buffer/texture yetenekleri, daha yetenekli shader dili ve bazı GPU işlerini CPU üzerinden “elle tekrar kurma” ihtiyacını azaltan standart mekanizmalar.
Pratikte “WebGL 2’ye geçmek”, yalnızca daha yeni bir tarayıcı sürümü kullanmak değildir; uygulama WebGL 2 bağlamı ile açılmalı ve shader tarafında GLSL ES 3.00 sözleşmesine uygun programlar kullanılmalıdır. Yani donanım + tarayıcı desteği tek başına yetmez; kod yolu da bu bağlamı seçecek şekilde kurulur. Bu yüzden geçiş maliyeti çoğu projede önce shader ve veri düzeni tarafında hissedilir.
- Kısıtların esnemesi: Doku boyutları, formatlar, örnekleme kuralları ve shader kapasitesi gibi sınırlar çoğu senaryoda daha gerçekçi hâle gelir; yine de her cihazda aynı üst sınır yoktur — limitler hâlâ sorgulanır, fakat “temel set” genişler. Özellikle mobil GPU’larda “çalışıyor” ile “istediğiniz boyutta/format hızlı çalışıyor” ayrımı devrededir; WebGL 2 bu ayrımı daraltır, ama sıfırlamaz.
- Uzantıdan çekirdeğe: Eskiden “var mı yok mu?” diye kontrol ettiğiniz
pek çok
özellik, WebGL 2 bağlamında çekirdek sözleşme olarak ele alınır. Bu,
kodun
daha öngörülebilir olmasını ve feature-detection yükünün azalmasını sağlar. Yine de
gerçek dünyada iki kontrol kalır: (1) bağlam gerçekten WebGL 2 mi, (2) hedef cihazın
MAX_*limitleri senaryonuza yetiyor mu?
Özet sezgi: WebGL 1 “her yere sığan minimum grafik” iken, WebGL 2 aynı web ortamında daha modern bir minimum grafik sunar. Bu, Holodepth’te ayrı sayfalarda anlattığımız buffer, framebuffer, shader ve texture konularının üstüne oturan bir yetenek tavanı yükseltmesidir; tek başına yeni bir sanat kuralı icat etmez, ama aynı kuralları daha az çarpık araçlarla uygulamanızı sağlar.
Altın cümle
WebGL 2, GPU’ya daha yakın ve daha modern bir kontrol katmanı sunar; böylece birçok grafik tekniği “mümkün ama acı verici” olmaktan çıkıp pratikte sürdürülebilir hâle gelir.
Gelişmiş buffer tarafı: VAO ve instancing
WebGL 1’de sık görülen bir maliyet, her çizim öncesi attribute düzenini tekrar tekrar kurmaktır. Vertex Array Object (VAO), bu düzeni bir kez paketleyip tek bağlama adımında geri çağırmanızı sağlar; CPU tarafındaki “ayar tekrarı” azalır.
VAO’yu “ekstra bir vertex buffer” sanmayın: pratikte bağlama durumunun kısa yolu gibi düşünün — hangi attribute’ların hangi buffer’dan, hangi stride/offset ile okunacağı ve (varsa) indeks buffer’ının ne olduğu gibi bilgiler bir arada tutulur. WebGL 1 dünyasında bu fikir çoğu zaman uzantıyla gelirdi; WebGL 2 ile birlikte bu tür düzenlemeler çekirdek akışta daha doğal hâle gelir.
Instancing ise aynı geometriyi sahneye çok sayıda kopyalarken draw call sayısını kontrol altında tutmanın klasik yoludur: tek bir çizim çağrısıyla çok sayıda örnek üretmek için GPU’ya “bu sefer kaç tane ve örnek başına hangi fark?” bilgisini verirsiniz. Bu, ağaçlar, parçacıklar, mermiler gibi tekrar eden içeriklerde CPU’nun boğulmasını engeller.
Instancing’in kalbi şudur: mesh aynı kalır, örnek başına değişen veri ayrı bir akışta taşınır (örneğin dünya matrisi, renk varyasyonu, animasyon indeksi). Vertex shader her örnek için bir “örnek kimliği” ile bu veriyi birleştirir; böylece binlerce kopya için binlerce draw yerine, çoğu zaman birkaç draw ile iş biter.
Mini teşhis: sahne “hafif” görünüp CPU’yu yakan durumlarda suçlu çoğu zaman geometri değil, çizim çağrısı fırtınası veya sürekli attribute yeniden bağlama (VAO’suz yoğun döngüler) olur. VAO ve instancing tam olarak bu iki ayrı darboğazı hedefler: biri durumu paketler, diğeri aynı mesh’i toplu üretir.
Bu iki başlık, Holodepth’teki buffer & attribute mantığı anlatımının doğal uzantısıdır: veri hâlâ buffer’dan gelir; fakat nasıl bağlandığı ve kaç kez çağrıldığı modernlaşır.
Multiple Render Targets (MRT): çoklu çıktı
MRT, tek bir fragment pass sırasında birden fazla color attachment’a yazmayı mümkün kılar. Pratik anlamı şudur: aynı geometri üzerinden hem “görünen renk” hem de örneğin normal, malzeme maskesi veya başka ara tamponlar üretmek için sahneyi defalarca yeniden koşturmak zorunda kalmadan, tek draw ile çok çıktı üretebilirsiniz.
Zihinsel model şudur: çoğu zaman “aynı fragment için aynı anda üretilmiş” birkaç sayıyı farklı hedeflere yazarsınız. Bu, deferred tarafında sık duyulan G-buffer fikrinin omurgasıdır — geometriyi bir kez işleyip ışığı/son rengi sonraki geçişlerde kurmak, geometriyi her ışık için yeniden koşturmaktan daha ölçeklenebilir olabilir.
MRT “bedava performans” değildir: her ek color attachment, aynı fragment üzerinde ek yazım ve bellek bant genişliği demektir. Kazanç çoğu zaman CPU ve draw tekrarını azaltmaktan gelir; yani maliyeti GPU’ya kaydırırsınız ama toplam işin daha öngörülebilir ve daha az parçalı olmasını sağlarsınız.
Bu özellik, deferred lighting ve gelişmiş post-processing gibi tekniklerin tarayıcıda sürdürülebilir olmasında kritik bir yapı taşıdır — çünkü ara veriyi üretmenin maliyetini düşürür. Hangi çıktıların aktif olduğu, hedef formatları ve donanım limitleri gibi ayrıntılar framebuffer sözleşmesinin parçasıdır; Holodepth’te mekanizma için Framebuffer — WebGL2 MRT bölümüyle birlikte okuyun.
Gelişmiş texture türleri
WebGL 1’de “texture” çoğu zaman düz 2D görüntü gibi düşünülür. WebGL 2 ile veri kaynağı çeşitliliği artar:
- 3D textures: Veriyi \(x\) ve \(y\) ile birlikte \(z\) boyutunda da adreslersiniz; hacimsel efektler, yoğunluk alanları veya katmanlı görünümler için uygundur.
- Texture arrays: Birden fazla 2D katmanı tek bir dizi gibi yönetirsiniz; shader içinde “hangi katman?” seçimiyle tek bir sözleşmede çok dokuyu taşımak kolaylaşır.
Kısa ayrım: 3D texture gerçek anlamda bir hacimdir; örneklerken \(z\) üzerinde de komşu hücreler arası filtreleme düşünülebilir (yoğunluk alanları, bulut/sis gibi sürekli veriler için doğal). Texture array ise aynı boyutlu birçok 2D dilimi üst üste koyar; katmanlar arası “karışım” beklentisi yoktur — seçtiğiniz dilim içinde klasik 2D örnekleme yaparsınız. Bu yüzden “çok doku taşımak” problemi çoğu zaman array’e, “tek parça hacim” problemi 3D’ye gider.
Pratik teşhis: tek bir büyük 2D atlas kullanırken sık görülen kenar taşması (bleeding) riski, texture array’de katmanlar birbirinden izole olduğu için azalır; maliyet ise bellek ve yönetim tarafında artan düzenliliktir. Her iki tür de GPU belleğinde ciddi yer kaplayabilir; özellikle 3D texture’da hacim büyüdükçe maliyet hızla büyür.
Örnekleme ve adresleme fikri değişmez; değişen, elinizdeki veri hacminin şeklidir. \((u,v)\) düşüncesi hâlâ geçerlidir; ek olarak 3D’de \(w\) (veya benzeri bir derinlik koordinatı), array’de ise bir katman indeksi seçimi devreye girer. Temel UV ve sampling sezgisi için Texture mapping sayfasıyla birlikte düşünmek verimi artırır — çünkü orada anlatılan “lookup” mantığı, bu genişlemiş şekillerde de aynı ailedir.
GLSL ES 3.00: daha yetenekli shader dili
WebGL 2 shader’ları GLSL ES 3.00 ile gelir. Bu, yalnızca “daha fazla fonksiyon” demek değildir; vertex/fragment arası veri düzenini daha kontrollü kurmanızı sağlayan sözleşmeler getirir.
Pratikte bu, WebGL 1’de alışılan GLSL ES 1.00 sözdiziminden belirgin bir kopuş demektir: aynı matematiği kopyalasanız bile, giriş/çıkış anahtar kelimeleri ve örnekleme çağrılarının çoğu yeni sözleşmeye uyar. Yani WebGL 2’ye geçişte “bir satır değiştirip çalıştırmak” nadiren yeterlidir; shader’lar çoğu zaman bilinçli bir port ister.
layout(location = …): Attribute girişlerinin adresini shader içinde açıkça bağlama imkânı; büyük programlarda veri sözleşmesini netleştirir. Aynı fikir, bu sayfanın 3. bölümünde değindiğimiz MRT bağlamında fragment çıktıları için de kurgulanabilir: birden fazla hedefe yazarken “hangi çıktı hangi slota?” sorusunu shader tarafında görünür kılar.- Daha zengin türler ve hassasiyet: Matematikte daha doğru ara sonuçlar için daha uygun tür seçimleri ve daha az “kırılgan” yaklaşım mümkün olur; görsel artifact’lerin bir kısmı doğrudan bu katmanda azalır. Özellikle tam sayı ve bit düzeyi işlemler, texture array katman seçimi gibi “indeks” doğası güçlü problemlerde CPU’ya devretmeden daha temiz ifade edilebilir.
Vertex ve fragment aşamaları arasındaki veri köprüsü, ES 3.00’da çoğu zaman isim ve
türü
eşleşen in/out arayüzleri üzerinden kurulur. Bu,
küçük
projelerde fazladan yazım gibi görünse de büyüdükçe “hangi varying gerçekten gidiyordu?”
hatalarını azaltır: arayüz sözleşmesi shader dosyasında daha açık hâle gelir.
Shader’ın pipeline içindeki yeri ve veri akışı iskeleti için Shader overview — veri akışı bölümüyle birlikte okumak iyi bir köprüdür; WebGL 2 tarafında ise bu iskeletin aynı rolü oynadığını, yalnızca dil katmanının modernleştiğini hatırlamak yeterlidir.
Modern rendering: deferred ve post-processing
WebGL 2’nin sunduğu MRT, gelişmiş buffer yönetimi ve daha yetenekli shader dili birleşince, masaüstü motorlarından tanıdık “iki aşamalı” akışlar tarayıcıda da daha makul hâle gelir:
Kısa karşılaştırma: klasik forward akışta ışık sayısı ve geometri sıkça birlikte büyür; deferred düşüncede ise önce yüzey bilgisini tamponlara “yazarsınız”, ışığı çoğu zaman ekran uzayında bu tamponları okuyarak ölçeklersiniz. Hangisinin “doğru” olduğu sahneye bağlıdır; WebGL 2’nin katkısı, deferred tarafında özellikle ara tampon üretimini ve okumasını daha az parçalı kılan altyapıyı standartlaştırmasıdır.
- Deferred rendering (kavramsal): Önce geometriden ara tamponlar (ör. normal, malzeme parametreleri) üretilir; ışıklandırma bu tamponlar üzerinden daha verimli ölçeklenir. MRT, bu ara tamponları aynı geçişte doldurmayı kolaylaştırır.
- Post-processing: Sahne tamponuna yazıldıktan sonra bloom, depth of field, tonemapping gibi efektler genelde tamponları zincirleyerek işler. WebGL 2, bu zincirin hem kalite hem bant genişliği açısından daha temiz kurulmasına yardım eder.
Deferred’ın faturası da bellidir: birden fazla yüksek çözünürlüklü tampon (G-buffer) demek, bellek ve bellek bant genişliği demektir. Kazanç çoğu zaman CPU/draw düzeninde olduğu için, mobil veya düşük bant genişlikli GPU’larda “her zaman kazanır” varsayımı doğru değildir. Bu yüzden deferred bir “sihirli düğme” değil; hangi maliyeti nereye kaydırdığınız sorusunun cevabıdır.
Post-processing tarafında tipik desen şudur: sahne bir hedefe çizilir, ardından tam ekran geçişlerde o hedef texture olarak okunur (çoğu zaman renk + derinlik gibi yardımcı verilerle birlikte). Bir efekt çıktısı bir sonraki efektin girdisi olur; bu zincirde “ping-pong” (iki hedef arasında yaz/oku) düzeni sık görülür. WebGL 2 ile bu okuma/yazma sözleşmesi, WebGL 1’e göre genelde daha az çevreleyici iş ve daha tutarlı format seçenekleriyle kurulabilir — yine de her adımın çözünürlük ve format uyumu, framebuffer / render target mantığında çözülür.
Forward tarafta ışığın geometriyle nasıl birleştiğini kavramsal olarak kurmak için Holodepth’teki Lighting models sayfası iyi bir referanstır; bu bölüm ise o matematiğin nerede ve kaç geçişte işlendiği üzerinden WebGL 2’nin sunduğu esnekliği tamamlar.
Burada amaç tek bir “doğru teknik” seçtirmek değil; WebGL 2’nin neden bu teknikleri CPU maliyetini şişirmeden denemeyi mümkün kıldığını göstermektir.
Bağlantı haritası
WebGL 2 sayfası tek başına “her şey”i anlatmaz; amaç, modern özellikleri Holodepth’teki derin konulara yönlendiren bir harita sunmaktır. Aşağıdaki satırlar, bu sayfada kurduğumuz başlıkların (2, 3, 4, 5, 6) hangi mevcut içerikle birleştiğini gösterir.
- Instancing / vertex düzeni: Buffer & attribute mantığı — veri hâlâ buffer’dan gelir; soru “GPU’ya hangi sırayla ve hangi anlamda bağlandı?” ve “aynı mesh’i nasıl toplu çizersin?” köprüsüdür (bu sayfada 2. bölüm).
- MRT: Framebuffer — WebGL2 MRT — tek geçişte çoklu color çıktısı ve hedef seçimi; deferred/post gibi akışların “ara tampon üretimi” tarafı (bu sayfada 3. bölüm).
- Shader layout / veri akışı: Shader overview — veri akışı — attribute/uniform/varying
düzeninin zihinsel iskeleti; WebGL 2’de GLSL ES 3.00 ile birlikte daha açık
in/outvelayoutsözleşmeleri devreye girer (bu sayfada 5. bölüm). - Texture örnekleme temeli: Texture mapping — UV, interpolation ve sampling lookup sezgisi; 3D texture / texture array gibi genişleyen şekiller yine bu ailenin parçasıdır (bu sayfada 4. bölüm).
Hızlı teşhis için: CPU’da draw/ayar tekrarı şüphesi varsa önce buffer/VAO/instancing hattına, “hedefe yazmıyor / çok hedef” şüphesi varsa framebuffer+MRT hattına, derleme/link veya stage arası uyumsuzluk şüphesi varsa shader veri akışına, titreme/blokluk/yanlış renk şüphesi varsa texture örnekleme hattına inin. Genel pipeline zamanlaması için ayrıca GPU & render pipeline sayfası üst anlatım sağlar.
Three.js bağlantısı
Üst seviye projelerde bu geçişi her satırda elle yönetmezsiniz. Tarayıcı WebGL 2 bağlamını açabildiğinde, Three.js gibi motorlar çoğu modern özelliği (ör. VAO, instancing, MRT tabanlı bazı iç yollar) uygun olduğunda devreye alır; siz sahne ve materyal seviyesinde çalışmaya devam edersiniz.
WebGL tarafındaki kazanım şudur: motor bir şeyi “otomatik yaptığında” bile, altta hangi GPU sözleşmesinin devreye girdiğini bilmek; performans ve görsel kalite sorunlarını doğru katmanda teşhis etmenizi sağlar.