ब्राउज़र में एक open world बनाना, भाग 10: Seam का अफरा-तफरी और corner वाली boss fight
लिखा है Oleg Sidorkin ने, जो Cinevva के CTO और Co-Founder हैं
यहाँ पहली बार आए हैं? series guide देखें। यह समझाता है कि spike क्या होता है और सभी भागों को link करता है।
अगर पहले के भाग व्यवस्थित से लगते थे, तो यह अध्याय combat जैसा लगा।
Spikes 17 से 22 हमारा corner-case वाला दौर था। Dual-LOD marching cubes, heightmap-to-MC boundary seams, mixed-resolution corner chunks जहाँ तीन या चार LOD levels मिलते हैं, GPU seam generation, और fallback mode का व्यवहार। हर spike किसी एक खास failure scenario को संभालता था जिससे हम टकरा चुके थे या जिसका हमें अंदेशा था।
Spike 17 ने dual marching cubes को दो LOD levels एक साथ active रखकर test किया। चुनौती यह थी कि एक ही chunk के neighbors अलग-अलग faces पर अलग-अलग resolutions पर हो सकते थे। Spike 16 का transition cell logic एक समय में एक face के लिए काम करता था, लेकिन जब एक chunk को कई faces पर transition cells चाहिए होती थीं, तो vertex buffer का प्रबंधन उलझ जाता था। हर face की transition cells को इस तरह generate और append करना पड़ता था कि बाकी की पर असर न पड़े।
Spike 19 को नई tab में खोलें ↗ · Source देखें
पहला बार-बार आने वाला खलनायक था winding order। कई बार हमें लगा कि हमारे पास topology की समस्याएँ हैं, फिर पता चला कि वे orientation की समस्याएँ थीं। Backface culling valid seam triangles को खा रहा था क्योंकि winding main mesh के मुकाबले उल्टा हो गया था। एक ही असली वजह, पर camera angle के हिसाब से अलग-अलग दिखने वाला लक्षण। हल यह था कि transition cell emission code में एक तय winding convention लागू की जाए और उसे double-sided material toggle से verify किया जाए।
दूसरा खलनायक था आंशिक सहीपन से पैदा हुआ झूठा भरोसा। एक seam किसी एक camera angle से perfect दिख सकता था और तब टूट जाता था जब LOD roles higher और lower resolution वाले chunks के बीच आपस में बदल जाते थे। Transition cell asymmetric होती है। यह high-res side और low-res side से अलग-अलग तरीके से sample लेती है। अगर किसी एक configuration के लिए "कौन सा side high-res है" वाला logic उल्टा हो जाए, तो आपको bug सिर्फ तभी दिखता है जब camera किसी खास position पर जाता है।
फिर आया हमारी सबसे पसंदीदा recoveries में से एक। हम heightmap tiles पर एक seam cutoff artifact का पीछा कर रहे थे और इल्ज़ाम transition logic पर लगा रहे थे। इस पर दो दिन फूँक दिए। असली दोषी था stale overdraw। पिछले frame की higher-resolution geometry अब भी buffer tail में जीवित थी, उस chunk के lower LOD पर downscale हो जाने के बाद भी। Draw range अब भी पुराने, बड़े vertex count पर सेट था। जैसे ही हमने draw range को compute shader के atomic counter से रिपोर्ट किए गए active vertex count तक clip किया, वह "रहस्यमयी seam issue" गायब हो गया।
यह एक बढ़िया याद दिलाने वाली बात थी कि rendering bugs अक्सर meshing bugs का भेस धर लेते हैं। Geometry पूरे समय सही थी। Draw call बस valid data के अंत के पार पढ़ रहा था।
Spike 22 तक हम hybrid fallback test कर रहे थे, जहाँ chunks खास परिस्थितियों में marching cubes से heightmap mode में switch हो सकते थे, जैसे जब chunk में कोई volumetric edits न हों और वह camera से काफ़ी दूर बैठा हो। इससे हमें एक all-or-nothing policy के मुकाबले ज़्यादा व्यावहारिक रास्ता मिला। पास के edited chunks volumetric आज़ादी के लिए MC इस्तेमाल करते हैं। दूर के unedited chunks efficiency के लिए heightmaps इस्तेमाल करते हैं।
Spike 22 को नई tab में खोलें ↗ · Source देखें
यह अध्याय roller coaster की वह तीखी ढलान था। निराश करने वाला और productive, दोनों एक साथ। अलग-अलग fixes में से कई छोटे थे, कभी-कभी एक ही line जो किसी comparison operator या offset को बदल देती थी। लेकिन उन fixes से जो समझ बनी कि LOD transitions, buffer management, और draw ranges आपस में कैसे interact करते हैं, वह बिलकुल छोटी नहीं थी।
भाग 11 में हम उस stabilization layer को देखते हैं जो इस अफरा-तफरी के बाद आई: policy-based chunk modes और reactive bug-fixing से explicit system rules की ओर बदलाव।
इस अध्याय में जिस technology का ज़िक्र हुआ
Dual-LOD marching cubes. Marching cubes को दो resolution levels पर एक साथ चलाना, जहाँ transition cells boundary को सिल देती हैं। चुनौती यह है कि एक ही chunk के neighbors अलग-अलग faces पर अलग-अलग resolutions पर हो सकते हैं, जिसके लिए हर face के लिए अलग transition cell generation चाहिए। हर face की transition cells vertex buffer में इस तरह append होती हैं कि बाकी की पर असर न पड़े। Atomic counters सभी faces में मिलाकर कुल active vertex count track करते हैं।
Winding order. हर triangle के अंदर vertex का क्रम तय करता है कि कौन सा side "front" face है। Backface culling के लिए एक तय winding (आमतौर पर बाहर से देखने पर counter-clockwise) ज़रूरी है। जब transition cells triangles emit करती हैं, तो winding को main mesh की convention से मेल खाना चाहिए। इसे उल्टा कर देने पर backface culling valid seam triangles को खा जाता है, जो कुछ camera angles से गायब surfaces जैसा दिखता है। एक आम debugging तकनीक है material पर side: THREE.DoubleSide को toggle करना, ताकि यह पुष्टि हो सके कि artifacts winding की समस्याएँ हैं या असली topology gaps।
Heightmap-to-MC fallback. एक hybrid chunk mode जहाँ दूर या unedited chunks heightmap terrain (सस्ता, सपाट surface) इस्तेमाल करते हैं जबकि पास के या edited chunks marching cubes (volumetric, caves को support करता है) इस्तेमाल करते हैं। Fallback का फ़ैसला camera से दूरी पर और इस पर निर्भर करता है कि chunk में SDF edits हैं या नहीं। एक heightmap chunk और एक MC chunk के बीच के seam को अपनी खुद की transition geometry चाहिए, Transvoxel जैसी ही, पर दो अलग representations को जोड़ती हुई न कि दो LOD levels को। देखें hybrid heightmap + volumetric overlays।
Draw range और atomic counters. GPU-driven mesh generation में, compute shader vertices को एक buffer में लिखता है और एक atomic counter बढ़ाता है ताकि track हो सके कि कितने vertices emit हुए। Draw call को इस counter को vertex count के तौर पर इस्तेमाल करना चाहिए, buffer capacity को नहीं। अगर draw range active count तक clip नहीं हुआ, तो पिछले frames के stale vertices (जो अब भी buffer tail में जीवित हैं) ghost geometry पैदा करते हैं: पतले sliver और झिलमिलाते triangles जो topology errors जैसे दिखते हैं पर असल में valid data के पार पढ़ने से बने rendering artifacts होते हैं।
12 में से भाग 10।
पिछला: भाग 9 - Transvoxel की शुरुआत एक scaffold से हुई
अगला: भाग 11 - Policy mode, hardcoded mode नहीं
Series guide: /hi/blog/2026-02-25-open-world-browser-series-guide