Skip to content

Tarayıcıda açık dünya inşa etmek, bölüm 26: Su yapmanın üç yolu

Cinevva CTO'su ve Kurucu Ortağı Oleg Sidorkin tarafından

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.

Bölüm 25 avatarlara kıyafet giydirdi. Bu bölüm suyla ilgili ve üç spike'tan oluşuyor; çünkü su, ucuz bir kestirme yol ile doğru çözümün ekran görüntüsünde aynı, hareket hâlindeyken ise tamamen farklı göründüğü yüzeydir. Spike 51, cazip seçenek olan ekran alanı yöntemiyle yansımayı oluşturuyor ve doğrudan bu yöntemin yapısal sınırlamasına çarpıyor. Spike 52, yayımlanmış her oyunun gerçekte kullandığı yönteme geçiyor. Spike 53 ise “tamamlanmış” bir çözümün bizim bulunduğumuz noktadan ne kadar uzakta olduğunu görmek için hazır bir su kütüphanesini devreye alıyor. Üçü de aynı kırılma katmanını paylaşıyor; dolayısıyla ilk ikisi arasında değişen tek değişken, yansımanın nasıl hesaplandığı.

Elinizdeki ekrandan yansıma üretmek

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

Ekran alanı yansıması, daha önce işlediğiniz kareyi yeniden kullanır. Her su pikseli için görüş ışınını yüzeyden yansıtır, bu yansıyan ışını derinlik tamponu boyunca ilerletir ve ışın kaydedilmiş bir yüzeyin arkasına geçtiğinde suyun neyi yansıttığını bulursunuz; örnek doğrudan renk tamponundan alınır. Port, three.js'in SSRNode uygulamasını satır satır izliyor; o da lettier'in SSR giriş yazısını temel alıyor. İlerleme, ekran alanında yapılan bir DDA yürüyüşüdür: Işının başlangıcını ve sonunu piksel koordinatlarına yansıtıp daha uzun olan eksen boyunca, piksel başına bir örnek alarak ilerlersiniz. Her adımdaki yansımış ışın derinliği, perspektif düzeltmeli enterpolasyon gerektirir: 11/z0+s(1/z11/z0). Çünkü görünüm-Z değerini doğrusal olarak enterpole etmek düpedüz yanlıştır ve isabetlerin yanlış yerde oluşmasına yol açar.

İki şey bunu bir slayt gösterisi yerine kullanılabilir hâle getiriyor. Kaba ilerleme 64 adımla sınırlandırılıyor; aksi takdirde bin piksele yayılan uzun bir ışın, parça başına yüzlerce yineleme çalıştırır ve milyon parçalı bir su düzlemi, yüzlerce yineleme ve her yinelemede birkaç doku örneğiyle sahneyi 30 fps'ye düşürürdü. Kalite, yineleme sayısı yerine bu sınır içindeki etkili adım aralığını kontrol ediyor. Sınırlandırılmış kaba ilerleme gözle görülür basamaklı şeritler bıraktığı için, altı yinelemeli ikili hassaslaştırma son ıskalama ile isabet arasındaki aralığı ikiye bölüyor. Bu, 64 kat alt adım doğruluğu sağlayarak komşu su parçalarının aynı kaba isabet konumuna kilitlenmesini önlemeye yetiyor. Son bir noktadan doğruya uzaklık denetimi, adayın yalnızca aynı derinlikte bulunmak yerine gerçekten yansıma ışını üzerinde olduğunu doğruluyor. Bunun için, o derinlikteki tek pikselin görünüm alanındaki genişliğine göre otomatik ölçeklenen bir kalınlık toleransı kullanılıyor: yakında daha sıkı, uzakta daha gevşek.

Bu spike'ın dürüst yanı kendi yorumlarına yazılmış durumda: SSR, ana kameranın hiç örneklemediği bir şeyi yansıtamaz. Bir ağacın alt yüzü, ekran dışındaki veya başka bir nesne tarafından kapatılmış herhangi bir şey tamponlarda mevcut değildir; dolayısıyla yansımada da görünemez. Hiçbir ilerleme kalitesinin gideremeyeceği “yanlış taraftaki bilgi kaybı” budur ve bir sonraki spike'ın varlık nedeni de tam olarak budur.

Yalan söyleyemeyen ayna

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

Düzlemsel yansıma, sahneyi su düzlemine göre aynalanmış bir kameradan ikinci kez ekran dışı bir hedefe işler ve su gölgelendiricisi bu hedeften örnek alır. Bu, standart yaklaşımdır; UE5 Water, three.js'in kendi WaterMesh bileşeni, ABZÛ ve Sea of Thieves'in tümü bunu kullanır. Çünkü SSR'ın asla göremeyeceği geometri dâhil, sahnenin çizilebilecek her pikseline erişir. TSL'de neredeyse şaşırtıcı derecede basittir: reflector() aynalanmış yardımcı kamerayı ve onun işleme hedefini ayırır, her karede güncellenmesi için hedefini mesh'e eklersiniz ve rengini örneklersiniz. Dalgalar daha sonra eklendiğinde yansıma, yansıtıcının UV düğümüne bir bozulma ofseti eklenerek dalgalandırılır; bu, WaterMesh tarafından kullanılan satırın aynısıdır.

Kayda değer hata aynada değil, güvenlik ağındaydı. Önceki bir sürüm, sıfıra yakın bir yansımanın hedefte hiçbir şey bulunmadığı anlamına geldiği varsayımıyla yansıtıcının çıktısını, yansıyan rengin büyüklüğüne göre ağırlıklandırarak yedek bir prosedürel gökyüzüyle harmanlıyordu. Ancak koyu bir gölgelik gölgesinin büyüklüğü de düşüktür; bu nedenle sınırlama hiçbir zaman tam güce ulaşmıyor ve gerçekten koyu olan bu pikseller parlak gökyüzüyle karışıyordu. Kullanıcının fark ettiği belirti çok netti: Hata ayıklama modundaki ham ayna hedefi kusursuz koyu ağaçlar gösterirken birleştirilmiş görüntüde yansımalar soluk görünüyordu ve teşhis, gölgelendiricinin “koyu renkleri yok ettiği” yönündeydi. Çözüm, kodu silmekti. Yansıtıcı hedefi ilk kareden sonra güvenilirdir; dolayısıyla hiçbir yedeğe gerek yoktur. Her iki spike da altta aynı kırılmayı paylaşır: Yüzeyin arkasındaki sahne örneklenir, her pikselin su çizgisinin ne kadar altında olduğu yeniden oluşturulur ve kanal başına Beer-Lambert sönümlemesi uygulanır; böylece kırmızı birkaç metre içinde kaybolurken mavi kalıcı olur. Uzak düzlemdeki arka planın sislenmemesi için de bir gökyüzü maskesi kullanılır. Schlick Fresnel, suya doğrudan aşağı bakarken kırılmayı, yüzey boyunca bakarken ise yansımayı öne çıkaracak şekilde ikisini harmanlar.

Tamamlanmış bir çözüm nasıl görünür?

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

Spike 53, geliştirme ile satın alma arasındaki tercihi sınayan bir çalışmadır. threejs-water-pro kütüphanesini yayımlandığı hâliyle; tropikal ön ayarı, varsayılan okyanus clipmap'i, kamera takibi ve Rayleigh gökyüzüyle birlikte devreye alır ve kütüphanenin kendi demosunda kullanılan ada glTF'sini yükler. Amaç, elle yazılmış düz su gölgelendiricisi ile eksiksiz bir okyanus sistemi arasındaki farkı görmektir ve bu fark büyüktür: Bu sistem, geminin yalnızca yukarı aşağı sallanmak yerine baş-kıç ve yana yatması için gövdenin altındaki birkaç noktadan dalga yüksekliğini örnekleyen bir yüzdürme sistemine, dümen suyu üreticisine, kıyı ve yüzey köpüğüne ve dalgaların güvertenin içinden görünmesini önlemek amacıyla gemi gövdesinin içinde su işlenmesini engelleyen bir maske geçişine sahiptir.

Entegrasyon sırasında, yalnızca README dosyasını okumak yerine kütüphaneyi gerçekten kullanarak öğrenebileceğiniz türden ayrıntılar ortaya çıktı. Köpük dokularına ön ayarda dosya adlarıyla başvuruluyor ancak onları yüklemek tüketicinin görevi; bu dokular olmadan kıyı, parçalanan dalgalar yerine sert bir su çizgisi kenarı gibi görünüyor. Ada, su altındaki geometrisi okyanus tabanının altına inecek şekilde konumlandırılmıştır; böylece kütüphanenin taban mesh'i, modelin dış halkasını görünür bir düzlem kenarı bırakmadan örter. Kütüphanenin amaçladığı yaklaşım budur: Araziyi prosedürel olarak üretmeyin, bir 3B model getirin. Ayrıca kenar yumuşatma, işleme motorunda bilinçli olarak kapatılıp bunun yerine son işleme FXAA geçişi kullanılıyor. Çünkü MSAA, derinlik duyarlı atmosferik sis çalışmadan önce kenar parçalarını arka planla harmanlayarak geometrinin sisle buluştuğu her yerde ince, koyu bir saçak bırakıyor. Kenar yumuşatmayı sisten önce değil, sonra çözmek bu saçağı ortadan kaldırıyor. Bunun riski azalttığı asıl konu kararın kendisidir: Üretime hazır bir okyanus büyük ve uzmanlık gerektiren bir sistemdir. Böyle bir sisteme ihtiyaç duyduğumuz durumlarda bakımı yapılan bir kütüphaneyi benimsemek; dümen suyunu, yüzdürmeyi ve köpüğü sıfırdan yeniden oluşturmaktan daha iyidir. Spike 52'deki düzlemsel ayna gölgelendiricisi ise içerik üreticisinin kendi dünyasına yerleştirdiği daha küçük iç su kütleleri için doğru çözüm olmaya devam eder.

Bu bölümde değinilen teknolojiler

Ekran alanı yansımalı su. Yansıtılmış bir görüş ışını, perspektif düzeltmeli 1/z enterpolasyonu, parça başına maliyeti sınırlayan katı bir adım sınırı ve sınırlı ilerlemenin bıraktığı şeritlenmeyi kaldıran ikili hassaslaştırma geçişi kullanarak DDA aracılığıyla ekran alanındaki derinlik tamponunda ilerler. Derinliğe göre ölçeklenen kalınlıkla yapılan noktadan doğruya doğrulama, yanlış isabetleri eler. Yöntemin kesin sınırı, yalnızca ana kameranın daha önce örneklediği geometriyi yansıtabilmesidir; dolayısıyla ekran dışındaki ve yanlış taraftaki yüzeyler hiçbir zaman görünmez. Bkz. arazi malzemeleri.

Düzlemsel ayna yansıması. Su düzlemine göre aynalanmış yardımcı bir kamera, sahneyi su gölgelendiricisinin örneklediği ekran dışı bir hedefe işler. Böylece SSR'ın göremediği geometriler dâhil piksel düzeyinde kusursuz yansımalar sağlanır. UE5 Water ve three.js WaterMesh, dalga bozulmasını yansıtıcının UV düğümüne ofset olarak uygulayarak bu yaklaşımı kullanır. Renk büyüklüğüne göre ağırlıklandırılmış gökyüzü yedeği, koyu yansıma piksellerini hatalı biçimde yok ediyordu; yansıtıcı hedefi ilk kareden sonra güvenilir olduğundan çözüm, yedeği kaldırmaktı.

Beer-Lambert derinlik tonlu kırılma. Her iki gölgelendirici de yüzeyin arkasındaki sahneyi örnekler, arka plandaki her pikselin su çizgisinin altındaki derinliğini yeniden oluşturur ve kanal başına üstel sönümleme uygular; kırmızı metreler içinde kaybolurken mavi kalıcı olur ve sonuç bir su sisi rengine doğru birleştirilir. Uzak düzlemin sislenmemesi için de bir gökyüzü maskesi kullanılır. Schlick Fresnel, dik geliş açısındaki kırılmayı sıyırma açılarındaki yansımayla harmanlar.

Üretime hazır bir su kütüphanesini benimsemek. threejs-water-pro; okyanus clipmap'i, Rayleigh gökyüzü, geminin baş-kıç ve yana yatması için çok noktalı yüzdürme, dümen suyu, köpük ve gövde maskesi geçişi sunar. Tüketici tarafındaki ayrıntılar önemlidir: Köpük dokuları açıkça yüklenmeli, adanın su altındaki geometrisi okyanus tabanının altına inerek tabanın kenarları örtmesini sağlamalı ve geometri kenarlarında koyu bir sis saçağı oluşmasını önlemek için kenar yumuşatma MSAA yerine son işleme FXAA geçişi olarak çalıştırılmalıdır.


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