Skip to content

Membangun dunia terbuka di browser, bagian 22: Awan yang bisa diterangi, dan culling yang harus diberi asupan

Oleh Oleg Sidorkin, CTO dan Salah Satu Pendiri Cinevva

Baru mengikuti seri ini? Gunakan panduan seri. Panduan tersebut menjelaskan apa itu spike dan memuat tautan ke semua bagian.

Bagian 21 membahas teknik rendering yang tidak memberikan hasil sepadan. Bagian ini memuat satu teknik yang berhasil, serta satu lagi yang memerlukan perbaikan cermat agar bisa berfungsi sama sekali. Spike 43 membahas langit: atmosfer berbasis fisika dan awan volumetrik, fondasi yang membuat sebuah adegan terasa seperti tempat sungguhan, bukan sekadar demo teknologi. Spike 44 membahas GPU culling bergaya meshlet, dengan pelajaran bahwa pengujian oklusi hanya akan sebaik occluder yang diberikan kepadanya.

Langit dari fisika, bukan gradien

Buka Spike 43 di tab baru ↗ · Lihat kode sumber

Hampir setiap efek cuaca sinematik bergantung pada dua bagian infrastruktur: atmosfer berbasis fisika, agar warna langit dan matahari mengikuti waktu berdasarkan fisika, bukan gradien yang disetel manual, serta volume awan volumetrik, agar langit memiliki struktur 3D alih-alih cubemap yang sudah di-bake. Spike 43 membangun tepat kedua hal tersebut di atas stack WebGPU dan TSL yang sudah ada, tanpa tambahan lain, karena setelah keduanya tersedia, bagian lain dari stack cuaca (kabut, god ray, permukaan basah, salju) menjadi serangkaian pengembangan lanjutan yang lebih kecil dan sudah dipahami.

Atmosfernya menggunakan model Hillaire 2020, yaitu sekumpulan lookup table yang dihitung dalam compute shader WGSL. Tabel transmittance mengintegrasikan cahaya matahari melalui profil kepadatan Rayleigh, Mie, dan ozon, lalu dihitung ulang hanya ketika matahari bergerak. Tabel sky-view di-bake ulang setiap frame karena biayanya cukup murah sehingga membatasi pembaruannya tidak sepadan dengan kode tambahan, dengan parameterisasi nonlinier di sekitar horizon untuk menghindari banding. Untuk saat ini, multiple scattering menggunakan pendekatan analitis sebagai pengganti tabel yang semestinya, dan matahari terbenam tetap terlihat tepat, jadi jalan pintas ini tersamarkan dengan baik. Awannya menggunakan ray-march bergaya Schneider Nubis melalui lapisan horizontal, dengan bentuk dari tekstur Perlin-Worley 128³ yang dierosi oleh tekstur Worley 32³. Keduanya di-bake saat startup menggunakan compute tanpa pengambilan data melalui jaringan, lalu diterangi dengan ekstingsi hukum Beer, fungsi fase dua lobus, dan pendekatan powder. Keterkaitan utamanya adalah warna matahari pada awan mengambil sampel dari tabel transmittance yang sama di setiap langkah, sehingga pencahayaan awan mengikuti matahari terbenam tanpa tahap penyetelan kedua.

Pada M1, keseluruhan proses ini selesai dalam 1,1 hingga 2,0 ms dengan awan beresolusi setengah, jauh di bawah anggaran 6 ms, menggunakan sekitar 14 MB memori GPU, dan berjalan di atas 100 FPS. Kedua klaim utama terbukti dalam praktik. Matahari terbenam adalah adegan andalan, momen yang membuat renderer terasa sinematik, dan hasilnya muncul secara alami dari fisika tanpa penyetelan untuk setiap waktu. Ada juga keuntungan tak terduga: dengan lapisan awan yang diparameterkan antara 800 m dan 4.000 m, awan jauh di horizon terlihat seperti jajaran pegunungan gelap dari posisi kamera yang rendah. Hal ini memberi dunia tersebut medan latar tanpa perlu ada yang memodelkan medan latar.

Ada satu catatan arsitektur yang patut dipertahankan. Struktur yang paling alami adalah menggambar langit ke swap chain terlebih dahulu, lalu membiarkan three.js menggambar geometri di atasnya dengan autoClear = false. Cara itu tidak bertahan pada renderer WebGPU di r184, karena flag tersebut tidak mengendalikan color load op seperti pada WebGL, sehingga three.js menimpa langit setiap frame. Solusinya adalah merender three.js ke target offscreen dan melakukan komposit akhir (mix(skyCloud, scene, scene.alpha) lalu ACES lalu sRGB) dalam pass milik kita sendiri yang menguasai swap chain.

Culling yang hanya sebaik occluder-nya

Buka Spike 44 di tab baru ↗ · Lihat kode sumber

Spike 44 membandingkan performa empat mode rendering: forward biasa, culling klaster CPU, compute culling GPU, serta visibility buffer dengan culling oklusi Hi-Z. Jalur Hi-Z adalah bagian yang menarik, dan ternyata memiliki bug tersembunyi: HUD-nya menyatakan bahwa oklusi aktif, tetapi penghitung "Hi-Z killed" selamanya berada tepat di 0,0%. Frustum culling berfungsi, jadi proses hulu cull shader tidak bermasalah. Bagian oklusinya tidak melakukan apa-apa meskipun tetap menanggung seluruh biayanya.

Pengujian oklusi Hi-Z memproyeksikan bounding box klaster ke layar, memilih level mip pada piramida kedalaman agar persegi panjang di layar berukuran sekitar 2×2 texel, mengambil sampel kedalaman occluder terdalam dalam persegi panjang tersebut, lalu menolak klaster jika titik terdekatnya masih lebih jauh daripada occluder itu. Piramida kedalaman dibangun setiap frame dengan mengisi mip 0 dari opaque-depth pre-pass, lalu melakukan max-reduce ke level-level berikutnya. Opaque pre-pass sengaja hanya menyertakan occluder solid, yaitu tanah dan proksi batang untuk setiap pohon, karena dedaunan dengan alpha test akan menciptakan celah yang mengecoh max-reduce.

Bug-nya bersifat geometris, bukan logis. Proksi batang berupa kotak berukuran 0,5 m × 4 m × 0,5 m. Pada jarak 30 m, proyeksinya di layar hanya sekitar 17 piksel. Namun, klaster rumput berukuran 50 m biasanya memilih mip 5, tempat setiap texel mencakup 32 piksel sumber. Batang selebar 17 piksel tidak sepenuhnya menutupi satu pun texel mip 5, sehingga setiap texel yang menyentuh batang juga menyentuh tanah di sekitarnya. Max-reduce 2×2 pertama memilih nilai kedalaman yang lebih besar, yaitu tanah yang lebih jauh di belakang batang, sehingga kedalaman batang terhapus pada reduksi pertama. Pada mip 5, piramida akhirnya memuat kedalaman tanah hampir di semua tempat, klaster tidak pernah lebih jauh daripada tanah, dan tidak ada apa pun yang pernah dianggap teroklusi.

Solusinya adalah membuat proksi cukup besar untuk mendominasi texel yang ditempatinya, dengan ukuran mengikuti siluet pohon, bukan kayunya. Proksi berukuran sekitar 2 m × 6 m × 2 m masih lebih kecil daripada kanopi sebenarnya, sehingga daun yang terlihat melalui celah tidak mengalami culling berlebihan, tetapi ukurannya cukup besar untuk bertahan dari max-reduce hingga jarak yang relevan, dan penghitung oklusi langsung naik dari nol. Pelajaran ini dapat digeneralisasikan menjadi aturan untuk engine produksi: segala sesuatu yang dipercaya sebagai occluder Hi-Z harus berukuran sesuai siluetnya di layar, karena efektivitas Hi-Z dalam adegan terbuka penuh dedaunan didominasi oleh cakupan occluder pada mip yang relevan, bukan oleh keanggunan matematika pengujian kedalamannya. Oklusi rumput terhadap rumput memang tidak bisa terjadi, karena bilah rumput berada pada kedalaman yang sama dengan tanah di bawahnya. Jadi, keuntungan sebenarnya berasal dari pohon yang mengoklusi dedaunan jauh dan pohon yang mengoklusi pohon lain.

Teknologi yang dirujuk dalam bab ini

LUT atmosfer Hillaire 2020. Tabel transmittance (cahaya matahari melalui profil Rayleigh, Mie, dan ozon) yang dihitung ulang hanya ketika matahari bergerak, ditambah tabel sky-view per frame dengan parameterisasi horizon nonlinier, menghasilkan warna langit dan matahari berbasis fisika yang mengikuti waktu tanpa gradien yang disetel manual. Pendekatan analitis untuk multiple scattering menggantikan tabel lengkap sampai ada artefak yang mengharuskan proses bake yang semestinya. Matahari terbenam muncul secara alami dari fisika tanpa penyetelan untuk setiap waktu.

Awan volumetrik Schneider Nubis. Ray-march melalui lapisan horizontal, yang dibentuk oleh tekstur Perlin-Worley 128³ hasil bake saat startup dan dierosi oleh tekstur Worley 32³, lalu diterangi dengan ekstingsi hukum Beer, fase dua lobus, serta komponen powder. Mengambil sampel warna matahari pada awan dari tabel transmittance yang sama di setiap langkah membuat pencahayaan awan mengikuti matahari terbit dan terbenam tanpa biaya penyetelan tambahan. Ray-march beresolusi setengah berjalan sekitar 4× lebih murah daripada resolusi penuh tanpa penurunan kualitas yang terlihat pada jarak umum, sebuah kompromi standar untuk produksi.

Mengompositkan WebGPU mentah dengan three.js pada r184. Menggambar langit ke swap chain dan menggambar geometri three.js di atasnya dengan autoClear = false gagal karena flag tersebut tidak mengendalikan color load op di backend WebGPU. Render three.js ke target offscreen RGBA16F, lalu lakukan mix akhir beserta tone mapping dan sRGB dalam pass yang menguasai swap chain.

Culling oklusi Hi-Z dan penentuan ukuran occluder. Piramida kedalaman yang dibangun melalui max-reduce memungkinkan GPU cull pass menolak klaster yang titik terdekatnya berada di belakang occluder terdalam dalam persegi panjang layarnya. Pengujian tersebut diam-diam tidak melakukan apa pun jika occluder terlalu kecil untuk mendominasi satu texel pada mip yang dipilih, karena max-reduce pertama mengganti kedalaman occluder dengan latar belakang yang lebih jauh di belakangnya. Occluder harus berukuran sesuai siluetnya di layar, bukan inti fisiknya. Lihat LOD berbasis GPU.


Bagian 22 dari 29. Sebelumnya: Bagian 21 - Renderer lebih cepat yang ternyata tidak lebih cepat Berikutnya: Bagian 23 - Lima puluh avatar dan satu suara di dalam ruangan Panduan seri: /blog/2026-02-25-open-world-browser-series-guide