Tarayıcıda açık dünya oluşturmak, bölüm 12: Halkalar, gökyüzü sisi ve yeniden yapacaklarımız
Yazan: Oleg Sidorkin, Cinevva CTO'su ve Kurucu Ortağı
Burada yeni misiniz? Seri rehberini kullanın. Rehber, spike'ın ne olduğunu açıklıyor ve tüm bölümlere bağlantı veriyor.
Spike 24'ün amacı "araziye clipmap halkaları eklemek"ti. Ancak aynı anda render sürecine, shader'lara, modül altyapısına ve görsel entegrasyona dokunan tam kapsamlı bir finale dönüştü.
Temel arazi görevi, vertex shader'da eş merkezli clipmap halkaları oluşturmaktı. Her halka, kameranın merkezinde yer alan ve vertex'leri heightmap örnekleriyle ötelenen düz bir grid mesh'tir. En içteki halka tam çözünürlük kullanır. Sonraki her halka vertex aralığını iki katına çıkarır ve daha geniş bir alanı kapsar. Zor kısım halkalar arasındaki sınırdır: yüksek çözünürlüklü bir halkanın düşük çözünürlüklü bir halkayla buluştuğu yerde, daha ince mesh'in kenar vertex'lerinin daha kaba mesh kenarının orta noktasına hizalanması gerekir. Halka kenarı boyunca grid koordinatı tek sayı olan sınır vertex'lerini tespit edip yüksekliklerini iki çift sayı koordinatlı komşularının orta noktasına,
Spike 24'ü yeni sekmede aç ↗ · Kaynak kodu görüntüle
Ardından sis ve gökyüzü entegrasyonu geldi. Uzak arazinin sabit, düz bir renge değil gerçek gökyüzü rengine karışmasını istiyorduk. Bunun için sis shader'ının, her fragment yönünde gökyüzünün hangi renkte olacağını bilmesi gerekiyordu. Eş dikdörtgensel bir HDR skybox dokusu yükledik ve fragment shader'da kameradan fragment'a uzanan görüş yönünü kullanarak bu dokudan örnek aldık; yönü TSL'nin equirectUV node'u aracılığıyla eş dikdörtgensel UV koordinatlarına dönüştürdük. Sis faktörü, kamera uzayındaki derinlik için positionView.z.negate() kullanılarak mesafeye göre hesaplandı ve yakın ile uzak mesafe arasında smoothstep ile harmanlandı.
Modülleri birbirine bağlamak, geometrinin herhangi bir bölümünden daha can sıkıcı çıktı. Derleme çıktılarını yeniden yapılandıran Three.js 0.183.1 sürümüne yükselttik. three/tsl import'unun three.tsl.js dosyasına çözümlenmesi gerekiyordu ve TSL kendi içinde three/webgpu öğesini yalın bir belirteç olarak import ediyordu. Her iki eşlemenin de HTML import map'inde açıkça tanımlanması gerekti. Bunlardan birinin bile eksik olması, hangi eşlemenin yanlış olduğuna dair hiçbir ipucu vermeyen anlaşılmaz "does not provide an export" veya "failed to resolve module specifier" hatalarına yol açıyordu. Her ikisi de import map'e eklendiğinde shader grafiği doğru şekilde yüklendi.
Ayrıca dokunun baş aşağı render edilmesine yol açan bir skybox yönü sorunumuz vardı. Çözüm, eş dikdörtgensel dokuda flipY = true ayarını kullanmaktı. Bu, yüklenen dokular için Three.js'in varsayılanıdır ancak ilk kodumuzda false olarak ayarlanmıştı.
İlk sis uygulaması gökyüzünü neredeyse sabit bir yönden örnekliyor ve doğal bir gradyan yerine ufuk renginde ince bir şerit oluşturuyordu. Çözüm, positionWorld.sub(cameraPosition).normalize() kullanarak her piksel için kameradan fragment'a uzanan gerçek dünya yönünü hesaplamak ve sis rengi aramasında bunu equirectUV içine aktarmaktı. Böylece arazi fragment'ları gerçekten arkalarında bulunan gökyüzü rengine karışmaya başladı ve her kamera açısından doğru bir görünüm elde edildi.
Tüm bu ayrı düzeltmelerin altında temel sonuç değişmedi. Artık yakın alan hacimsel düzenlemeyi (Transvoxel birleşimleri kullanan marching cubes), orta alan heightmap chunk'larını ve uzak alan clipmap halkalarını bir araya getiren bir arazi sistemimiz var. Bunların tümü mod, LOD ve geçiş davranışını belirleyen bir politika katmanı tarafından yönetiliyor.
Bir sonraki projede tekrarlayacağım kalıpları sıralamam gerekseydi şunları seçerdim:
Özellik geliştirmeye başlamadan önce risk spike'larıyla başlayın. Spike 1, içerik pipeline'larına yatırım yapmadan önce "yeterince hızlı render yapabiliyor muyuz?" sorusunu ortadan kaldırdı.
Büyük entegrasyon sıçramalarından önce düzgün çalıştığı bilinen temel sürümleri dondurun. Spike 13 ve 14, regresyonları ikiye bölerek aramakla geçireceğimiz günleri kurtardı.
Uzun optimizasyon süreçlerinden önce politika katmanını ve gözlemlenebilirliği zorunlu kılın. Spike 23, gizemli hataları tetikleme kuralları olan, adlandırılmış koşullara dönüştürdü.
Ekran görüntüleriyle değil, hareket hâlinde test edin. Clipmap sıçramaları, birleşim titremeleri ve streaming takılmaları sabit karelerde gizlenir.
Ortalama FPS'yi değil, özellik başına frame süresi maliyetini ölçün. Ortalamalar, kullanıcıların gerçekten hissettiği sıçramaları gizler.
Ve dağınık kısımları da yayımlayın. Yanlış yolları, güncelliğini yitirmiş buffer hayaletlerini aramayı, draw range yanlışken geçiş mantığını suçlayarak geçirilen iki günü... İnsanların gerçekten ders çıkarabileceği kısımlar bunlardır.
Harici gerçeklik kontrolü: Vuntra City geliştirme günlükleri
Bu seriyi tamamladıktan sonra kendi açık dünya varsayımlarımızı harici bir uygulamayla karşılaştırmak için @VuntraCity geliştirme günlüklerini inceledik. Bu, tarayıcı stack'i değil yerel bir UE5 projesi; ancak sistem kalıpları yeterince örtüştüğü için karşılaştırma faydalı.
İlk işaret, hareket hızının yalnızca oynanışın bir parçası değil, bir streaming kontrolü olarak da ele alınması gerektiği. Vuntra City'de yüksek hızlı ulaşım bilinçli olarak çoğu iç mekânın üzerinden yönlendiriliyor; ayrıntı menzili de nesne oluşturma trafiğini ve takılmaları önlemek için hareket hızına göre ölçekleniyor (ulaşım sistemi, performans teknikleri). Bu, politika katmanı yönelimimizle örtüşüyor: hareket modu; chunk yarıçapını, iç mekân etkinleştirmeyi ve kare başına izin verilen iş miktarını doğrudan etkilemeli.
İkinci işaret mimariyle ilgili. Harita ve adres sistemleri, yüklenmemiş bölgelerde de genel sorguların çalışabilmesi için dünya topolojisinin render edilen nesnelerden ayrılmasını gerektirmiş (haritalar ve adresler). Bu, render'a bağlı veri yollarını zorunlu kılmadan dünya araması, görev rotalama, moderasyon taramaları ve ilgi noktası indeksleme yapabilmek için tarayıcıda ihtiyaç duyduğumuz ayrımla aynı.
Üçüncü işaret, simülasyonun katmanlara ayrılması. Bir milyon NPC'lik tasarımlarında kaba program durumu ucuz ve genel tutuluyor, pahalı davranış bütçesi ise yalnızca oyuncunun yakınında harcanıyor (bir milyon NPC'ye genel bakış, sistemin ayrıntılı incelemesi). Bu, yakın alan doğruluğu ile uzak alan determinizminin ayrı bütçelere sahip ayrı meseleler olduğu, AOI öncelikli simülasyon modelimizi destekliyor.
Dördüncü işaret ise ham ölçek değil, tasarım kalitesi. En güçlü keşif anları; sürekli UI katmanları yerine ağırlıklı dağılımlardan, nadir aykırı örneklerden ve oyun dünyasına ait yön bulma ipuçlarından doğuyor (prosedürel ortam notları, mini haritasız oynanış döngüsü). Bizim için bu, teknik sistemlerin yalnızca maksimum verim sağlamak üzere değil, keşfedilebilir çeşitlilik üretecek şekilde ayarlanması gerektiğini hatırlatıyor.
Bu bölümde değinilen teknolojiler
Clipmap halka geometrisi. Her halka, kameranın merkezinde yer alan ve vertex'leri heightmap örnekleriyle ötelenen düz bir grid mesh'tir. En içteki halka tam çözünürlük kullanır. Sonraki her halka vertex aralığını iki katına çıkarır ve daha geniş bir alanı kapsar. Zor kısım sınırdır: yüksek çözünürlüklü bir halkanın düşük çözünürlüklü bir halkayla buluştuğu yerde, daha ince mesh'in kenar vertex'leri daha kaba mesh kenarının orta noktasına hizalanır. Teknik, Losasso ve Hoppe'nin SIGGRAPH 2004 makalesinden (PDF) kaynaklanır ve GPU Gems 2, Bölüm 2 içinde ayrıntılı olarak açıklanır. Geometri clipmap'leri hakkındaki arazi rehberimize bakın.
2:1 kenar morflama. İki clipmap halkası arasındaki sınırda, daha ince halkada daha kaba halkanın paylaşmadığı konumlarda vertex'ler bulunur. Halka kenarı boyunca grid koordinatı tek sayı olan sınır vertex'leri tespit edilir ve yükseklikleri, komşu iki çift sayı koordinatlı vertex arasında interpolasyonla hesaplanır. Böylece özel geçiş geometrisine ihtiyaç duymadan aralıksız birleşimler elde edilir. Interpolasyon, vertex shader'da sınır vertex'leri için morphedHeight = mix(heightLeft, heightRight, 0.5) şeklinde çalışır ve rehberimizde açıklanan aynı geomorfing çerçevesini kullanır.
Eş dikdörtgensel skybox eşleme. Boylam-enlem projeksiyonu kullanarak gökyüzündeki tüm yönleri bir küre üzerine eşleyen tek bir 2D görüntüdür. Yatay eksen 0-360 dereceyi, dikey eksen ise 0-180 dereceyi kapsar. Normalize edilmiş bir görüş yönü
Three.js'te texture.mapping = EquirectangularReflectionMapping ayarını SRGBColorSpace ile birlikte kullanmak, bunu sahne arka planı olarak etkinleştirir. TSL'de equirectUV(direction) aynı dönüşümü uygular ve 3D görüş yönünü dokudan örnek almak için kullanılan 2D UV koordinatlarına dönüştürür.
Gökyüzünden fragment başına sis rengi. Standart sis, fragment'ları tek bir sabit renge doğru harmanlar. Ayrıntılı bir skybox'a sahip sahnede bu yanlış görünür çünkü gökyüzü rengi yöne göre değişir. Çözüm, her piksel için kameradan fragment'a uzanan dünya yönünü (positionWorld.sub(cameraPosition).normalize()) hesaplamak ve sis rengi için skybox'ı bu yönde örneklemektir. Her fragment gerçekten arkasında bulunan gökyüzü rengine doğru solar ve böylece her kamera açısından doğru harmanlama elde edilir. Sis faktörü, kamera uzayındaki derinlik için positionView.z.negate() ve smoothstep(nearDist, farDist, viewDepth) kullanır.
ES modülleri için import map'ler. Yalın modül belirteçlerini (three/tsl gibi) gerçek URL'lerle eşleyen, tarayıcıya özgü bir mekanizmadır (<script type="importmap">). Three.js 0.183.1 derleme çıktılarını yeniden yapılandırdığında three/tsl öğesinin three.tsl.js dosyasına çözümlenmesi gerekti ve TSL kendi içinde three/webgpu öğesini yalın bir belirteç olarak import etti. Her iki eşlemenin de import map'te açıkça tanımlanması gerekiyordu; aksi hâlde tarayıcı "does not provide an export" veya "failed to resolve module specifier" hataları üretiyordu.
Ek okumalar
Bu seri boyunca kullanılan teknolojiler hakkında daha ayrıntılı bilgi için tamamlayıcı rehberlerimize bakın:
- Tarayıcı Açık Dünyaları İçin Dinamik LOD ve Streaming ile Arazi Üretimi; heightmap'leri, SDF'leri, marching cubes'ı, Transvoxel'i, geometri clipmap'lerini, geomorfingi, streaming mimarisini, arazi materyallerini ve bitki örtüsü render sürecini kapsar.
- Çok Oyunculu Üretici Dünyaları İçin Tarayıcıda 3D Açık Dünya Teknolojileri; render stack'lerini, WebGPU'yu, fiziği, ağ iletişimini, çok oyunculu mimariyi ve Skyrim, The Witcher 3, Breath of the Wild ile GTA V'ten çıkarılan dersleri kapsar.
On iki bölümlük bu yolculuğu takip ettiğiniz için teşekkür ederiz.
Bölüm 1: Onu bozmaya çalışarak başladık
Bölüm 2: Worker fiziği ve input gecikmesi korkusu
Bölüm 3: Bizi kurtaran gösterişsiz spike'lar
Bölüm 4: Gösterişli araziden önce streaming
Bölüm 5: Görsel güzellikler için bütçe ayırmak
Bölüm 6: Clipmap'ler gidişatı değiştirdi
Bölüm 7: Marching cubes ve ilk gerçek mağaralar
Bölüm 8: Temel sürümümüzü kaybetmeden entegrasyon
Bölüm 9: Transvoxel bir iskeletle başladı
Bölüm 10: Birleşim kaosu ve köşe bölüm sonu canavarı
Bölüm 11: Sabit kodlanmış mod değil, politika modu
14 bölümün 12.'si.
Önceki: Bölüm 11 - Sabit kodlanmış mod değil, politika modu
Sonraki: Bölüm 13 - Arazi şekillendirme ve matematik fonksiyonunun ölümü
Seri rehberi: /tr/blog/2026-02-25-open-world-browser-series-guide