ब्राउज़र में open world बनाना, भाग 22: ऐसे बादल जिन्हें आप रोशन कर सकें, और culling जिसे खिलाना पड़ता है
लिखा है Oleg Sidorkin ने, Cinevva के CTO और को-फाउंडर
यहाँ नए हैं? सीरीज़ गाइड देखिए। यह बताती है कि spike क्या होता है और सारे भागों के लिंक देती है।
भाग 21 एक ऐसी rendering technique के बारे में था जिसने फ़ायदा नहीं दिया। इस भाग में एक ऐसी है जिसने दिया, और एक जिसे काम करने के लिए एक सावधान fix की ज़रूरत पड़ी। Spike 43 sky है: एक physically based atmosphere और volumetric clouds, वह नींव जो किसी scene को tech demo की जगह एक असली जगह जैसा दिखाती है। Spike 44 meshlet-style GPU culling है, जहाँ सबक़ यह था कि occlusion test उतना ही अच्छा होता है जितने अच्छे occluders आप उसे खिलाते हैं।
physics से बनी sky, gradient से नहीं
Spike 43 नए tab में खोलें ↗ · सोर्स देखें
लगभग हर cinematic weather effect दो हिस्सों की infrastructure पर टिका होता है: एक physically based atmosphere, ताकि sky का रंग और sun का रंग किसी हाथ से tune किए gradient की जगह physics से time of day का पीछा करें, और एक volumetric cloud volume, ताकि sky में किसी baked cubemap की जगह 3D structure हो। Spike 43 ठीक यही जोड़ी मौजूदा WebGPU और TSL stack पर बनाता है, इससे ज़्यादा कुछ नहीं, क्योंकि एक बार ये दोनों मौजूद हो जाएँ तो बाक़ी weather stack (fog, god rays, गीली सतहें, बर्फ़) छोटे और जाने-पहचाने follow-ons की एक कतार बन जाती है।
Atmosphere Hillaire 2020 model है, lookup tables का एक सेट जो WGSL compute shaders में compute होता है। एक transmittance table sun light को Rayleigh, Mie, और ozone density profile के आरपार integrate करती है और सिर्फ़ तब फिर से compute होती है जब sun हिलता है। एक sky-view table हर frame फिर से bake होती है क्योंकि यह इतनी सस्ती है कि इसे gate करना code के लायक़ नहीं, horizon के आसपास एक non-linear parameterization के साथ ताकि banding न आए। Multiple scattering अभी proper table की जगह एक analytic fit इस्तेमाल करता है, और sunsets सही दिखते हैं, तो यह शॉर्टकट अच्छे से छिपा हुआ है। बादल एक Schneider Nubis-शैली का ray-march हैं जो एक horizontal slab के आरपार जाता है, shape एक 128³ Perlin-Worley texture से आता है जिसे एक 32³ Worley texture ने eroded किया है, दोनों boot पर compute में bake होते हैं बिना किसी network fetch के, Beer's law extinction, एक dual-lobe phase function, और एक powder approximation से रोशन। मुख्य coupling यह है कि बादल का sun color हर step पर वही transmittance table sample करता है, तो cloud lighting दूसरे tuning pass के बिना sunset का पीछा करती है।
एक M1 पर पूरी चीज़ half-resolution clouds पर 1.1 से 2.0 ms पर बैठती है, 6 ms के budget से ख़ासा नीचे, लगभग 14 MB GPU memory इस्तेमाल करते हुए, 100 FPS से ऊपर चलते हुए। दोनों thesis के दावे practice में टिके रहे। Sunset हीरो शॉट है, वह पल जो renderer को cinematic महसूस कराता है, और यह per-time-of-day tuning के बिना physics से ही निकल आता है। और एक ख़ुशनुमा इत्तेफ़ाक़: cloud slab को 800 m और 4000 m के बीच parameterize करने पर, horizon पर दूर के बादल एक नीची camera से dark mountain ridges जैसे दिखते हैं, जो दुनिया को background terrain देता है बिना किसी के background terrain model किए।
एक architecture नोट याद रखने लायक़ है। स्वाभाविक तरीक़ा यह है कि पहले sky को swap chain में paint किया जाए, फिर three.js को autoClear = false के साथ उसके ऊपर geometry draw करने दिया जाए। यह r184 पर WebGPU renderer के साथ नहीं टिकता, क्योंकि वह flag color load op को उस तरह gate नहीं करता जैसे WebGL में करता है, तो three.js हर frame sky को मिटा देता है। Fix यह है कि three.js को एक offscreen target में render किया जाए और final composite (mix(skyCloud, scene, scene.alpha) फिर ACES फिर sRGB) अपने ख़ुद के एक pass में किया जाए जो swap chain का मालिक हो।
ऐसी culling जो अपने occluders जितनी ही अच्छी है
Spike 44 नए tab में खोलें ↗ · सोर्स देखें
Spike 44 चार rendering modes को एक-दूसरे के ख़िलाफ़ bench करता है: plain forward, CPU cluster culling, GPU compute culling, और Hi-Z occlusion culling के साथ एक visibility buffer। Hi-Z path दिलचस्प वाला है, और इसमें एक चुपचाप छिपा bug था: इसका HUD कहता था occlusion चालू है, मगर "Hi-Z killed" counter हमेशा बिल्कुल 0.0% पर बैठा रहता था। Frustum culling काम कर रहा था, तो cull shader का upstream ठीक था। Occlusion वाला आधा हिस्सा एक no-op था जो पूरी क़ीमत चुका रहा था।
एक Hi-Z occlusion test एक cluster के bounding box को screen पर project करता है, एक depth-pyramid mip level चुनता है ताकि screen rectangle लगभग 2×2 texels का हो, उस rectangle में सबसे गहरे occluder की depth sample करता है, और cluster को reject कर देता है अगर उसका सबसे क़रीबी point अब भी उस occluder से दूर हो। Depth pyramid हर frame बनती है, mip 0 को एक opaque-depth pre-pass से seed करके और ऊपर तक max-reduce करके। Opaque pre-pass जानबूझकर सिर्फ़ ठोस occluders शामिल करता है, ज़मीन और per-tree trunk proxies, क्योंकि alpha-tested foliage ऐसे gaps बना देती जो एक max-reduce को धोखा दे जाते।
Bug geometric था, logical नहीं। Trunk proxy 0.5 m गुणा 4 m गुणा 0.5 m का box था। 30 m पर यह screen पर लगभग 17 pixels पर project होता है। मगर एक आम 50 m grass cluster mip 5 चुनता है, जहाँ हर texel 32 source pixels ढकता है। एक 17-pixel का trunk एक भी mip-5 texel को पूरा नहीं ढकता, तो trunk को छूता हर texel आसपास की ज़मीन को भी छूता है। सबसे पहला 2×2 max-reduce बड़ी depth value चुन लेता है, जो trunk के पीछे की दूर वाली ज़मीन है, और trunk की depth पहली ही reduction पर मिट जाती है। mip 5 तक pyramid लगभग हर जगह ground depth रखती है, cluster कभी ज़मीन से दूर नहीं होता, और कुछ भी कभी occlude नहीं होता।
Fix यह है कि proxy को इतना बड़ा बनाया जाए कि वह जिन texels में गिरे उन पर हावी हो जाए, उसे पेड़ की लकड़ी की जगह उसके silhouette के हिसाब से size दिया जाए। लगभग 2 m गुणा 6 m गुणा 2 m का proxy असली canopy से अब भी छोटा है, तो gaps के बीच से दिखने वाली पत्तियाँ कभी over-cull नहीं होतीं, मगर यह इतना बड़ा है कि उन दूरियों तक max-reduce में टिक जाए जो मायने रखती हैं, और occlusion counter फ़ौरन non-zero हो गया। यह सीख production engine के लिए एक नियम में बदल जाती है: जिस भी चीज़ पर Hi-Z occluder के तौर पर भरोसा किया जाए, उसे उसके on-screen silhouette के हिसाब से size देना होगा, क्योंकि खुली foliage वाले scenes में Hi-Z की असरदारी relevant mip पर occluder coverage से तय होती है, depth-test math की elegance से नहीं। Grass-बनाम-grass occlusion तो वैसे भी fire नहीं हो सकता, क्योंकि एक पत्ती उसके नीचे की ज़मीन जितनी ही depth पर बैठती है, तो असली जीत पेड़ों के दूर की foliage को occlude करने और पेड़ों के पेड़ों को occlude करने में है।
इस अध्याय में बताई गई technology
Hillaire 2020 atmosphere LUTs. एक transmittance table (Rayleigh, Mie, और ozone profile के आरपार sun light) जो सिर्फ़ sun के हिलने पर फिर से compute होती है, साथ में एक per-frame sky-view table एक non-linear horizon parameterization के साथ, physically based sky और sun colors देती है जो किसी हाथ से tune किए gradient के बिना time of day का पीछा करते हैं। एक analytic multiple-scattering fit पूरी table की जगह तब तक काम चलाता है जब तक कोई artifact proper bake पर मजबूर न कर दे। Sunset per-time-of-day tuning के बिना physics से ही निकल आता है।
Schneider Nubis volumetric clouds. एक horizontal slab के आरपार एक ray-march, जिसे एक boot-baked 128³ Perlin-Worley texture shape देता है और एक 32³ Worley texture eroded करता है, Beer's law extinction, एक dual-lobe phase, और एक powder term से रोशन। हर step पर उसी transmittance table से cloud sun color sample करने से cloud lighting बिना किसी ख़र्च के sunrise और sunset का पीछा करती है। Half-resolution ray-march आम दूरी पर बिना किसी दिखने वाले quality loss के full से लगभग 4× सस्ता चलता है, यही standard production trade है।
r184 पर raw WebGPU को three.js के साथ compositing करना. Sky को swap chain में paint करके और three.js geometry को autoClear = false के साथ उसके ऊपर draw करना नाकाम रहता है क्योंकि वह flag WebGPU backend में color load op को gate नहीं करता। three.js को एक offscreen RGBA16F target में render करें और final mix साथ ही tone-map साथ ही sRGB एक ऐसे pass में करें जो swap chain का मालिक हो।
Hi-Z occlusion culling और occluder sizing. Max-reduce से बनी एक depth pyramid एक GPU cull pass को उन clusters को reject करने देती है जिनका सबसे क़रीबी point उनके screen rectangle में सबसे गहरे occluder के पीछे हो। यह test चुपचाप कुछ नहीं करता अगर occluders इतने छोटे हों कि चुने गए mip पर एक texel पर हावी न हो सकें, क्योंकि पहला max-reduce occluder की depth को उसके पीछे के दूर वाले background से बदल देता है। Occluders को उनके on-screen silhouette के हिसाब से size देना होगा, उनके physical core के हिसाब से नहीं। देखें GPU-driven LOD।
29 में से भाग 22। पिछला: भाग 21 - एक तेज़ renderer जो तेज़ नहीं निकला अगला: भाग 23 - पचास avatars और कमरे में एक आवाज़ सीरीज़ गाइड: /hi/blog/2026-02-25-open-world-browser-series-guide