Skip to content

Tarayıcıda açık dünya inşa etmek, bölüm 28: Ufka uzanan çimler ve kendini gizleyen zemin

Yazan: Oleg Sidorkin, Cinevva CTO'su ve Kurucu Ortağı

Burada yeni misiniz? Seri rehberine göz atın. Rehber, spike'ın ne olduğunu açıklıyor ve tüm bölümlere bağlantı veriyor.

27. Bölüm adayı oluşturdu ve ona döşeme gibi görünmeyen bir zemin kazandırdı. Bu bölüm, araziyi boş değil de yaşamla dolu hissettiren iki şeyi ele alıyor. Spike 56, dokulu bir yamacı içinde yürümek isteyeceğiniz bir yere dönüştüren yüzey ayrıntısı olan çimlere odaklanıyor; buradaki sorun, çim alanını dağınık çizgiler yerine gerçekten bir tarla gibi göstermek. Spike 57 ise daha fazla çizmenin tam tersini ele alıyor: Bir tepenin zaten gizlediği şeyleri çizmemek. İlginç sonuçsa bunu test etmenin hızlı yolunun aynı zamanda doğru yol olduğunun ortaya çıkması.

Konfeti değil, tarla gibi görünen çimler

Spike 56'yı yeni sekmede aç ↗ · Kaynak kodunu görüntüle

Temel karar geometrik. Tek bir ince ve uca doğru daralan çim bıçağı, çoğu kamera açısından piksel altı boyutta kalıyor; dolayısıyla yarım milyon düz bıçak, bir tarla yerine seyrek yeşil konfetiler gibi görünüyor. Çözüm, çapraz dörtgenlerden oluşan bir küme: Yerel yukarı eksenin etrafında birbirinden altmış derece farklı açılara döndürülmüş üç daralan dörtgen. Böylece her bakış yönünde en az bir dörtgen kameraya neredeyse dik duruyor ve her örnek, ekranda yaklaşık üç çim bıçağı genişliğinde gerçek alan kaplıyor. Yeşil çizgiler görmekle çim görmek arasındaki farkı yaratan da bu.

Bir WebGPU compute çekirdeği, başlangıçta her kümeyi bir kez yerleştiriyor. Örnek indeksini birbiriyle korelasyonsuz beş rastgele akışa hash'liyor, alan içinde bir XZ konumu seçiyor, CPU'daki zemin örgüsünün kullandığı aynı FBM'den zemin yüksekliğini örnekliyor (değerlerin birebir eşleşmesi ve bıçakların yüzeyin üzerinde havada durmak yerine yüzeye oturması için işareti koruyan mod kullanan bir TSL uyarlaması), merkezi fark yöntemiyle bir normal hesaplıyor ve her küme için genişlik, yükseklik ve renk tonu belirliyor. Render tarafında, örnek matrisini tamamen atlayıp doğrudan clip space'e yazan bir TSL vertex grafiği bulunuyor: Birim çapraz dörtgeni ölçekliyor, Rodrigues formülüyle yerel yukarı yönünü zemin normaline döndürüyor ve küme konumuna taşıyor. Mesafe LOD'u, bir ayıklama geçişine gerek kalmadan elde ediliyor; çünkü vertex grafiği kümenin yüksekliğini 1smoothstep(fadeNear,fadeFar,dist) ile çarpıyor. Böylece uzaktaki kümeler sıfır yüksekliğe kadar düzleşiyor ve fill maliyeti oluşturmamaya başlıyor. Rüzgâr, kümenin dünya XZ konumu ile zaman üzerinde hesaplanan iki oktav sinüs ve kosinüsten oluşuyor; yatay olarak uygulanıyor ve yükseklik oranıyla sınırlandırılıyor. Böylece taban sabit kalırken uçlar hareket ediyor. Kamerayı hesaba katan çok hafif bir eğim de her kümeyi izleyiciye doğru yatırarak düz kartlar gibi görünmek yerine perspektifte açılmalarını sağlıyor.

Renk tarifi, NedMakesGames'in Breath of the Wild URP shader'ından alındı: Çim bıçağı rengi, yükseklik oranı boyunca kök tonundan uç tonuna lerp edilen düz gölgelendirme kullanıyor; kök ve uç tonlarının kendileri de küme başına renk tonu değerine göre iki palet arasında lerp ediliyor. Bu, gerçek bir BotW çim alanındaki benekli iki tonlu görünümü sağlıyor. Stilize görünümde çapraz dörtgenlerin ayrı ayrı normalleri tek tek gölgelendirmek için fazla gürültülü olduğundan diffuse, ortam ışığı alt sınırıyla birlikte zemin normalinin güneşe göre yönünü kullanıyor. Tüm alan, bütünüyle GPU üzerinde yerleştirilip canlandırılan tek bir draw çağrısıyla çiziliyor.

Bir tepenin gizlediği öğeleri ayıklamanın dört yolu

Spike 57'yi yeni sekmede aç ↗ · Kaynak kodunu görüntüle

Spike 57, prodüksiyon istemcisinin stream ettiği aynı prosedürel dünya üzerinde dört ayıklama yöntemini karşılaştırıyor; tepelerin bitki örtüsünü gerçekten kapatabilmesi için dünya dikey eksende 1,8 kat ölçeklenmiş. T0 yalnızca mesafeyi kullanıyor ve yayınlanan sürümdeki chunk görünürlüğünün hâlihazırda yaptığı işle eşleşiyor. T1, düşük maliyetle en büyük kazancı sağlayan frustum ayıklamasını ekleyerek görüş konisinin dışındaki her şeyi eliyor. T2, araziyi hesaba katan bir ufuk testi ekliyor: Gözden her örneğe bir ışın ilerletiyor ve yolun herhangi bir noktasında heightmap ışının üzerine çıkarsa örneği eliyor. Böylece bir sırtın arkasındaki ağaç, frustum içinde olsa bile ayıklanıyor. T3 aynı ufuk testini koruyor ancak maksimum yükseklik piramidiyle hızlandırıyor. T4 ise mesafe, frustum ve ufuk testlerinin tamamını, bitki örtüsü materyalinin gizli vertex'leri daraltmak için okuduğu örnek başına görünürlük ölçeğini yazan bir TSL compute çekirdeğine taşıyor. İki karelik görünür kalma sayacı, kamera hafifçe oynadığında bir örnekleme noktasının texel'ın hemen içine veya dışına düşmesinden kaynaklanan tek karelik titremeyi yumuşatıyor. Bu süre özellikle kısa tutuluyor; çünkü daha uzun süreler algoritmik hataları düzeltmek yerine gizliyor.

Spike'ın asıl değerini ortaya koyduğu yer piramit ve buradaki ders, testi ekranda gerçekten görünen şeyle eşleştirmekle ilgili. Render edilen arazi, vertex'leri sabit iki metrelik bir ızgara üzerinde bulunan üçgen örgüden oluşuyor ve rasterizer vertex'ler arasında doğrusal enterpolasyon yapıyor. Dolayısıyla herhangi bir dikdörtgen içindeki gerçek render yüksekliği, aradaki sürekli gürültü tepesinin değil, o dikdörtgen içindeki vertex'lerin maksimumu oluyor. Örtülme piramidi gürültüyü daha sık bir ızgarada örneklerse örgünün hiç göstermediği hayali tepeler buluyor ve kameranın açıkça ötesini görebildiği ışınları engellemeye başlıyor. Bu nedenle piramidin taban texel'ları tam olarak vertex ızgarasına oturuyor; her biri dört köşe vertex'inin maksimumunu saklıyor ve üst seviyeler standart 2x2 maksimum indirgemelerini kullanıyor. Bu da piramidi render edilen örgü açısından kesin hâle getiriyor. Kod, her ışın adımındaki test için piramidi sorgulamak yerine örgüyü bilineer enterpolasyonla noktasal olarak örnekliyor. Çünkü texel altı bir AABB sorgusu tüm texel'ın maksimumunu döndürerek dik sırtlarda yüksekliği birkaç metre artırıyor; bu da görünür nesneleri gizleyecek aşırı örtülmenin ta kendisi.

Şaşırtıcı sonuç, kaba kuvvet referansı olan T2'nin hatalı yöntem olması. T2, sürekli gürültüyü doğrudan noktasal olarak örneklediği için örgü vertex'leri arasında kalan ve render edilmeyen tepeleri yakalıyor; dolayısıyla biraz fazla örtülme uygulayıp oyuncunun gerçekte görebildiği bitki örtüsünü gizliyor. Piramit yöntemi, chunk düzeyindeki testlerde kullanılan O(logN) AABB indirgemesi sayesinde hem daha hızlı hem de geometrik olarak daha doğru; çünkü yalnızca örgünün gerçekten gösterdiği yükseklikleri döndürebiliyor. Spike'ın vermek istediği mesaj tam olarak bu: Hızlandırılmış yapı, hız uğruna kabul edilen bir kalite ödünü değil; gerçeklikle eşleşen sürüm. Chunk ayıklaması, her chunk için beş noktalı bir test çalıştırıyor (chunk'ın maksimum yüksekliğinde dört üst köşe ve merkez) ve chunk'ın kendini asla örtmemesi için kendi kapladığı alanı örtücü kümesinden çıkarıyor. Önerilen prodüksiyon yöntemi T4: Renderer zaten WebGPU'ya geçtiği için örnek metadata'sını ve yükseklik alanını storage buffer'lara aktarıp aynı döngüyü indirect draw kullanan bir compute shader'da çalıştırmak.

Bu bölümde kullanılan teknolojiler

Çapraz dörtgenli GPU çimleri. Her kümede birbirinden altmış derece farklı açılara döndürülmüş üç daralan dörtgen, her açıdan bakıldığında bir dörtgenin neredeyse dik görünmesini garanti ediyor. Böylece yarım milyon örnek, piksel altı konfetiler yerine bir tarla gibi algılanıyor. Bir compute çekirdeği her kümeyi yerleştiriyor (CPU örgüsüyle aynı işareti koruyan mod kullanılarak örneklenen FBM zemin yüksekliği, merkezi fark normali, küme başına boyut ve renk tonu); bir TSL vertex grafiği ise ölçeklemek, Rodrigues yöntemiyle normale döndürmek, taşımak ve yüksekliğe bağlı rüzgâr uygulamak için örnek matrisini atlıyor. Mesafe LOD'u ücretsiz: Kümeler 1smoothstep(fadeNear,fadeFar,dist) ile sıfır yüksekliğe daralıyor. Renk, NedMakesGames'in BotW tarifini izliyor: renk tonları karıştırılmış iki palet üzerinde kökten uca uzanan bir gradyan. Bkz. GPU güdümlü LOD.

Arazi ufku örtülme ayıklaması. Prodüksiyon dünyasında dört yöntem karşılaştırıldı: mesafe; buna eklenen frustum; sırtın arkasındaki örnekleri eleyen bir heightmap ışın testi; bunu hızlandıran maksimum yükseklik piramidi ve bir TSL compute uyarlaması. Piramit, örgünün tessellation için kullandığı iki metrelik vertex ızgarasının tam üzerinde örnekleme yapıyor (taban texel'ı = dört köşe vertex'inin maksimumu, üst seviyelerde 2x2 maksimum indirgeme). Böylece yalnızca rasterizer'ın gerçekten gösterdiği yükseklikleri döndürüyor.

Ödün vermeden hem hızlandırılmış hem doğru. Sürekli gürültünün noktasal olarak örneklenmesi (kaba kuvvet T2 yöntemi), örgü vertex'leri arasında kalan ve asla render edilmeyen tepeleri bulup görünür bitki örtüsünü gereğinden fazla örtüyor. Vertex ızgaralı piramit hem daha hızlı (O(logN) AABB indirgemesi) hem de geometrik olarak kesin. Bu nedenle her ışın adımındaki testler, dik sırtlarda yüksekliği metrelerce artırabilecek texel başına piramit maksimumunu sorgulamak yerine örgüyü bilineer olarak örnekliyor. Chunk ayıklaması beş noktalı bir test kullanıyor ve her chunk'ın kendi kapladığı alanı hariç tutarak kendini örtmesini önlüyor. Prodüksiyon yöntemi, metadata ile yükseklik alanını storage buffer'lara aktararak indirect draw kullanan bir compute shader ayıklaması gerçekleştiriyor.


29 bölümün 28'incisi. Önceki: 27. Bölüm - Gürültüden bir ada, zemine benzeyen bir zemin Sonraki: 29. Bölüm - Tek kontrolcü, her beden Seri rehberi: /tr/blog/2026-02-25-open-world-browser-series-guide