3D ब्रश तकनीकें और इन-गेम वर्ल्ड स्कल्प्टिंग
हम चाहते हैं कि players वर्ल्ड को स्कल्प्ट करें। ग्रिड पर prefabs रखें ऐसा नहीं। ब्लॉक चालू-बंद करें ऐसा नहीं। असल में टेरेन को फिर से आकार दें: नदियां काटें, पहाड़ उठाएं, चट्टानों के किनारे smooth करें, गुफाएं खोदें। वैसी ही चीज़ जो ZBrush और Blender का sculpt mode आर्टिस्ट्स के लिए करते हैं, लेकिन एक मल्टीप्लेयर browser game के अंदर 60fps पर चलते हुए।
यह एक मुश्किल इंजीनियरिंग समस्या है जो डेटा रिप्रेज़ेंटेशन, mesh extraction, GPU compute, ब्रश के गणित और नेटवर्क सिंक्रोनाइज़ेशन तक फैली है। यह गाइड वह सब कुछ दर्ज करती है जो हमें मिला।
टेरेन रिप्रेज़ेंटेशन की दो दुनिया
हर स्कल्प्टिंग सिस्टम इस चुनाव से शुरू होता है कि टेरेन डेटा कैसे स्टोर किया जाए। यह चुनाव तय करता है कि किस तरह के edits मुमकिन हैं, वे कितनी तेज़ चलते हैं, और कितनी मेमोरी खर्च करते हैं।
हाइटमैप
एक हाइटमैप हर ग्रिड पॉइंट पर एक height value स्टोर करता है। आप इसे एक ग्रेस्केल इमेज की तरह सोच सकते हैं जहां brightness का मतलब elevation है। हमारा मौजूदा world/client टेरेन ठीक इसी तरह काम करता है: noise.ts FBM value noise के ज़रिए height बनाता है, और हर Chunk एक Float32Array हाइटमैप स्टोर करता है जो एक PlaneGeometry पर प्रोजेक्ट होता है।
हाइटमैप तेज़ होते हैं। सैंपलिंग bilinear interpolation के साथ एक ही array lookup है। LOD आसान है क्योंकि आप बस ग्रिड का रिज़ॉल्यूशन कम कर देते हैं। Splat-आधारित टेक्सचर ब्लेंडिंग सीधे UV ग्रिड पर मैप होती है। फिज़िक्स collisions एक height query तक सिमट जाती हैं।
सीमा topology है। एक हाइटमैप हर (x, z) coordinate पर सिर्फ एक height दर्शा सकता है। कोई गुफाएं नहीं। कोई overhangs नहीं। कोई arches नहीं। कोई tunnels नहीं। अगर कोई player एक ऐसी चट्टान स्कल्प्ट करे जो खुद पर मुड़ जाती है, तो हाइटमैप उसे स्टोर नहीं कर सकता। ऐसे टेरेन के लिए जो ज़्यादातर ढलवां पहाड़ियां और पहाड़ हैं, यह ठीक है। ऐसी freeform स्कल्प्टिंग के लिए जहां players ज़मीन के अंदर खोद सकें, यह एक dead end है।
वॉल्यूमेट्रिक (3D Scalar Fields)
विकल्प यह है कि 3D स्पेस के हर पॉइंट पर एक value स्टोर की जाए। अगर value ठोस मटेरियल के अंदर negative और बाहर positive है (या उल्टा), तो आपके पास एक Signed Distance Field (SDF) है। अगर value बस एक density है (किसी threshold से ऊपर ठोस, नीचे खाली), तो आपके पास एक density field है।
वॉल्यूमेट्रिक रिप्रेज़ेंटेशन किसी भी topology को संभाल लेते हैं। गुफाएं, overhangs, तैरते द्वीप, पहाड़ों के बीच से tunnels। इसका मोल मेमोरी और जटिलता है। 32-bit floats वाला एक 256^3 ग्रिड 64 MB खर्च करता है। एक 512^3 ग्रिड 512 MB खर्च करता है। और यह एक ही chunk के लिए है। इसे व्यावहारिक बनाने के लिए आपको sparse data structures (octrees, brick maps) चाहिए।
Mesh extraction का चरण भी आसान नहीं है। आप बस vertex Y positions सेट करके बात खत्म नहीं कर सकते। आपको एक ऐसा algorithm चाहिए जो scalar field को पढ़े और एक triangle mesh बनाए जो सतह का अनुमान लगाए वहां जहां field शून्य को पार करता है।
Mesh Extraction: Marching Cubes, Surface Nets, और Dual Contouring
Marching Cubes
Marching Cubes सबसे पुराना और सबसे ज़्यादा लागू किया गया isosurface extraction algorithm है। Lorensen और Cline द्वारा 1987 में प्रकाशित, यह voxel ग्रिड के हर cube की जांच करके काम करता है जहां हर corner की एक scalar value होती है। अगर कुछ corners सतह के अंदर (negative) और कुछ बाहर (positive) हों, तो उस cube के अंदर एक triangle mesh patch रखा जाता है।
हर cube के 8 corners होते हैं, हर एक या तो अंदर या बाहर, जिससे 256 संभावित कॉन्फ़िगरेशन (2^8) बनते हैं। ये symmetry के ज़रिए 15 अनोखे cases में सिमट जाते हैं। एक lookup table हर case को triangles के एक सेट से मैप करता है। Edge intersection points वहां linear interpolation से खोजे जाते हैं जहां edges पर sign बदलता है।
हाल की GPU implementations ने Marching Cubes को रियल टाइम स्कल्प्टिंग के लिए काफी तेज़ बना दिया है। UE5 पर 2025 की एक implementation हर GPU thread को एक cube देती है, हज़ारों को एक साथ process करते हुए। मुख्य बात यह है कि हर cube का triangulation उसके पड़ोसियों से स्वतंत्र है, जो इस algorithm को बेहद आसानी से parallel बना देता है।
MCHex (arxiv 2511.02064, 2025) Marching Cubes को adaptive hexahedral mesh generation तक बढ़ाता है, जिसमें positive Jacobian values की गारंटी होती है, जो सिमुलेशन meshes के लिए boundary approximation सुधारता है।
rupMC serial implementations से दर्जनों गुना तेज़ performance हासिल करता है और CPU/GPU heterogeneous architectures का इस्तेमाल करके parallel DMC variants से 4x तेज़ है।
मुख्य सीमा: Marching Cubes तीखे फ़ीचर्स के साथ संघर्ष करता है। एक 90-डिग्री edge गोल होकर एक smooth curve बन जाता है। टेरेन स्कल्प्टिंग के लिए यह आम तौर पर ठीक रहता है (प्राकृतिक टेरेन ज़्यादातर smooth होता है), लेकिन आर्किटेक्चरल फ़ीचर्स के लिए यह एक समस्या है।
Surface Nets
Surface Nets algorithms का एक नया परिवार है जो discrete scalar fields से ज़्यादा smooth meshes बनाता है। cube edges पर vertices रखने (Marching Cubes की तरह) के बजाय, Surface Nets हर उस cube में एक vertex रखता है जिसमें सतह होती है, फिर quads बनाने के लिए आसपास के vertices को जोड़ता है।
नतीजा स्वाभाविक रूप से ज़्यादा smooth होता है। एक 2024 paper (arxiv 2401.14906) ने एक हाई-परफ़ॉर्मेंस parallel Surface Nets implementation दिखाई जो sequential algorithms से एक से दो orders of magnitude तेज़ चलती है। fast-surface-nets Rust crate एक ही 2.5 GHz core पर छोटे lookup tables और SIMD acceleration का इस्तेमाल करके करीब 2 करोड़ triangles प्रति सेकंड बनाता है।
bevy-sculpter (v0.18.0, जनवरी 2026) अपनी मुख्य meshing strategy के तौर पर Surface Nets का इस्तेमाल करता है। यह crate चार ब्रश टाइप के साथ SDF-आधारित वॉल्यूमेट्रिक स्कल्प्टिंग देता है: hard CSG (तुरंत add/remove), smooth continuous (held input के लिए), blur (सतह smoothing), और flatten (target height पर सेट)। इसमें edits के बाद उचित signed distance field properties बहाल करने के लिए Fast Sweeping Method के ज़रिए SDF redistancing भी शामिल है।
Surface Nets, Marching Cubes (सरल, तेज़, लेकिन binary data पर aliased meshes बनाता है) और Dual Contouring (फ़ीचर-संरक्षित लेकिन जटिल) के बीच एक अच्छा बीच का रास्ता है।
Dual Contouring
Dual Contouring उन तीखे फ़ीचर्स को बनाए रखता है जो Marching Cubes और Surface Nets नहीं रख सकते। यह सिर्फ हर corner पर field के sign का नहीं, बल्कि edge intersections पर gradient (normal) का भी इस्तेमाल करके ऐसा करता है। एक QEF (Quadratic Error Function) minimization हर cell के अंदर vertex को उस position पर रखता है जो सभी edge intersection constraints को सबसे अच्छी तरह संतुष्ट करता है।
नतीजा: extracted mesh में तीखे edges और corners बने रहते हैं। perpendicular faces वाला एक cube एक cube ही रहता है।
इसका मोल जटिलता है। QEF solve में inter-cell dependencies होती हैं जो GPU parallelization को मुश्किल बनाती हैं। यह non-manifold meshes बना सकता है (वे edges जो दो से ज़्यादा polygons में शेयर होते हैं)। और implementation, Marching Cubes से ज़्यादा पेचीदा है, हालांकि Johannes Jendersie कहते हैं कि एक काम करने वाली Dual Contouring implementation करीब 200 लाइन कोड की है, जबकि एक मज़बूत Marching Cubes के लिए 500+ चाहिए।
Cubical Marching Squares (CMS) को एक बीच के रास्ते के तौर पर प्रस्तावित किया गया है: intercell-independent (GPU-अनुकूल) रहते हुए भी कुछ feature preservation देता है।
किसका इस्तेमाल करें
एक browser में player-सामने वाली टेरेन स्कल्प्टिंग के लिए:
Surface Nets सबसे मज़बूत उम्मीदवार है। यह binary data पर Marching Cubes के aliasing artifacts के बिना smooth meshes (प्राकृतिक दिखने वाला टेरेन) बनाता है। यह रियल टाइम re-meshing के लिए काफी तेज़ है। और इसे लागू करना Dual Contouring से आसान है।
Marching Cubes तब एक ठोस विकल्प बना रहता है जब GPU parallelism प्राथमिकता हो (हर cube स्वतंत्र है) या जब आपको सबसे ज़्यादा library support चाहिए। Reinder Nijhoff का WebGPU SDF Editor (जनवरी 2026) अपनी extraction pipeline में Marching Cubes और Surface Nets दोनों लागू करता है, जो पूरी तरह GPU पर चलता है।
Dual Contouring उन मामलों के लिए सबसे अच्छा रखा जाता है जहां performance से ज़्यादा आर्किटेक्चरल सटीकता मायने रखती हो। browser के संदर्भ में रियल टाइम टेरेन स्कल्प्टिंग के लिए आदर्श नहीं।
Transvoxel Algorithm: LOD Stitching का हल
जब voxel टेरेन को अलग-अलग रिज़ॉल्यूशन पर mesh किया जाता है (player के पास LOD0, दूर LOD2), तो boundaries पर दरारें बनती हैं। हाइटमैप के लिए यह एक सरल समस्या है: कम-रिज़ॉल्यूशन वाले पड़ोसी से मिलाने के लिए edge vertices को interpolate करें। हमारा मौजूदा chunk.ts ठीक यही stitchEdge() में करता है।
वॉल्यूमेट्रिक टेरेन के लिए, समस्या कहीं ज़्यादा कठिन है। LOD0 पर एक गुफा का मुंह boundary पर 30 triangles बना सकता है। वही region LOD1 पर पूरी तरह अलग topology के साथ 8 triangles बना सकता है। इनके बीच linear interpolation का कोई सरल तरीका नहीं है।
Eric Lengyel का Transvoxel Algorithm (2009) इसे "transition cells" से हल करता है। दो LOD levels के बीच की boundary पर, algorithm 9 हाई-रिज़ॉल्यूशन samples पर विचार करता है (8 cube corners के बजाय), जो 512 संभावित कॉन्फ़िगरेशन बनाते हैं जो 73 equivalence classes में आते हैं। हर class एक पूर्वनिर्धारित triangle pattern से मैप होता है जो दोनों रिज़ॉल्यूशन के बीच की खाली जगह को पूरी तरह भरता है।
यह algorithm स्थानीय voxel data पर काम करता है, इसलिए एक बदले हुए region को फिर से triangulate करना तेज़ है। यह रियल टाइम स्कल्प्टिंग के लिए ज़रूरी है: जब कोई player किसी LOD boundary के पास टेरेन edit करे, तो सिर्फ transition cells को फिर से बनाने की ज़रूरत होती है।
एक Rust implementation transvoxel crate के तौर पर मौजूद है। मूल lookup tables transvoxel.org पर उपलब्ध हैं।
ब्रश का गणित
एक स्कल्प्टिंग ब्रश एक function है जो किसी target point के आसपास एक radius के भीतर scalar field values को बदलता है। गणित सभी implementations में हैरानी की हद तक एक जैसा है, Blender से लेकर Unreal Engine से लेकर runtime game systems तक।
Falloff Functions
ब्रश का falloff तय करता है कि edit की strength केंद्र से किनारे तक कैसे घटती है। Blender 5.1 इन मानक profiles को परिभाषित करता है:
Smooth: f(d) = 3d^2 - 2d^3 (Hermite interpolation, हमारी noise.ts जैसा ही smoothstep)
Sphere: केंद्र पर तेज़, border के पास तीखा falloff। इसका अनुमान f(d) = sqrt(1 - d^2) के तौर पर लगाया जाता है।
Sharp: f(d) = (1 - d)^n जहां n > 2। एक बारीक point बनाता है।
Linear: f(d) = 1 - d जहां d केंद्र से normalized distance है (केंद्र पर 0, किनारे पर 1)।
Constant: f(d) = 1 जब d < 1, ब्रश की boundary पर hard cutoff।
Inverse Square: smooth और sphere के बीच hybrid, एक प्राकृतिक "मिट्टी जैसी" feel के लिए।
सभी मामलों में, d = distance_to_center / brush_radius, जिसे [0, 1] पर clamp किया जाता है। falloff value ब्रश की strength को गुणा करके हर पॉइंट पर असली field modification बनाती है।
Falloff Space
Blender sphere falloff (distance 3D world space में गणना की गई) और projected falloff (distance 2D screen space में गणना की गई) के बीच फ़र्क करता है। Projected falloff का मतलब है कि दो पॉइंट जो स्क्रीन पर पास दिखते हैं, एक-दूसरे को बराबर प्रभावित करते हैं, भले ही वे world space में बहुत अलग depth पर हों। टेरेन स्कल्प्टिंग के लिए, 3D world space falloff आम तौर पर ज़्यादा intuitive होता है।
मुख्य ब्रश ऑपरेशन
Raise/Lower (Displacement): ब्रश radius के भीतर scalar field में जोड़ें या घटाएं, falloff से weighted। हाइटमैप के लिए: height[i] += strength * falloff(d)। SDFs के लिए: sdf[i] -= strength * falloff(d) (घटाने से मटेरियल ज़्यादा ठोस होता है, सतह उठती है)।
Smooth (Laplacian): हर value को उसके पड़ोसियों के औसत से बदलें, falloff से weighted। यह detail मिटाता है और noise कम करता है। Laplacian filter एक छोटा kernel sample करता है (हाइटमैप के लिए 3x3, volumes के लिए 3x3x3) और mean की ओर ब्लेंड करता है। HC (Humphrey's Classes) smoothing एक variant है जो raw Laplacian से volume को बेहतर बनाए रखता है।
Flatten: field value को एक target height (या SDF space में distance) पर सेट करें, falloff से ब्लेंड करते हुए। target आम तौर पर stroke शुरू होने पर ब्रश के केंद्र पर sample किया जाता है, फिर स्थिर रखा जाता है। यह सपाट plateaus बनाता है।
Pinch/Inflate: vertices को surface normal की ओर या उससे दूर ले जाएं। SDF space में, यह gradient की दिशा में displace करने के बराबर है।
Grab: field के एक region को translate करें, जैसे आप मिट्टी खींच रहे हों। displacement vector, world space में प्रोजेक्ट किया गया mouse delta है, जो radius के भीतर field values पर लागू होता है।
Noise: ब्रश radius के भीतर field में procedural noise जोड़ें। smooth सतहों को खुरदरा करने के लिए उपयोगी।
Stamp: एक 2D ग्रेस्केल इमेज को displacement के तौर पर लागू करें, उसे cursor के नीचे की सतह पर प्रोजेक्ट करते हुए। Unreal Engine का Landscape tool टेरेन ब्रश के लिए इसका समर्थन करता है।
Adaptive Tessellation
sculpt-3D (React + Three.js browser sculpting) adaptive tessellation लागू करता है: जैसे-जैसे ब्रश mesh पर घूमता है, ब्रश के केंद्र के पास के triangles subdivide होते हैं ताकि deformation के लिए ज़्यादा vertices मिलें। यह "low-poly stretch" समस्या को रोकता है जहां एक मोटा mesh स्कल्प्टिंग से विकृत हो जाता है। subdivision एकसमान triangle quality के लिए symmetric splitting का इस्तेमाल करता है।
वॉल्यूमेट्रिक systems के लिए, adaptive tessellation की उसी तरह ज़रूरत नहीं होती क्योंकि mesh field से फिर से बनता है। इसके बजाय, आप उसी असर के लिए edits के पास स्थानीय रूप से voxel रिज़ॉल्यूशन बढ़ा सकते हैं (adaptive octrees)।
SDF स्कल्प्टिंग: Dreams का तरीका
Media Molecule का Dreams (PS4, 2020) अब तक शिप किया गया सबसे महत्वाकांक्षी इन-गेम स्कल्प्टिंग सिस्टम है। Alex Evans ने SIGGRAPH 2015 में इसका तकनीकी तरीका पेश किया।
रिप्रेज़ेंटेशन
Dreams ज्योमेट्री को 83^3 fp16 volume texture blocks में एक compound SDF function के तौर पर स्टोर करता है। हर sculpt 1 से 100,000 "edits" की एक सूची है, जहां हर edit एक प्रिमिटिव shape (sphere, cube, cylinder, cone, ellipsoid, torus, वगैरह) और एक blend mode के साथ एक CSG operation (add, subtract, color) है।
Blend modes soft-max और soft-min functions का इस्तेमाल करते हैं। एक "soft" blend प्रिमिटिव्स के बीच गोल transitions बनाता है (जैसे मिट्टी को एक साथ दबाया गया हो)। एक "hard" blend साफ़ boolean cuts बनाता है। blend radius user के नियंत्रण में होता है।
रेंडरिंग
Dreams एक triangle mesh extract नहीं करता। इसके बजाय, यह एक custom point-cloud renderer ("flecks") का इस्तेमाल करके सीधे SDF से render करता है। हर fleck surface normal के साथ एक छोटी disc है। सतहें खोजने के लिए SDF को sample किया जाता है, और उन पर flecks बांटे जाते हैं। यह mesh extraction की रुकावट को पूरी तरह टाल देता है लेकिन एक custom renderer की ज़रूरत होती है।
एक Three.js/WebGL वर्ल्ड के लिए, यह तरीका सीधे लागू नहीं होता। हमें meshes extract करनी होंगी। लेकिन CSG edit list वाली अवधारणा undo/redo और नेटवर्क सिंक्रोनाइज़ेशन के लिए बहुत प्रासंगिक है।
Mike Turitzin का Dynamic SDF Engine (2026)
Mike Turitzin द्वारा अभी विकसित हो रहा एक game engine dynamic SDFs को मुख्य रिप्रेज़ेंटेशन के तौर पर इस्तेमाल करता है। यह engine इनका समर्थन करता है:
गेमप्ले के दौरान विस्तृत बदलाव: smooth तरीके से या तीखे edges के साथ मटेरियल जोड़ना और हटाना। non-destructive बदलाव जैसे holes को हिलाना या अस्थायी tunnels बनाना जो player के पीछे गायब हो जाते हैं।
Brick maps और brick atlases sparse caching के लिए। पूरे SDF field को एक dense 3D ग्रिड में स्टोर करने के बजाय, field को "bricks" (छोटी 3D tiles) में बांटा जाता है। सिर्फ वे bricks आवंटित होते हैं जिनमें surface boundary होती है। यह ज़्यादातर खाली या ठोस स्पेस वाले दृश्यों के लिए मेमोरी को काफ़ी कम कर देता है।
Geometry clipmaps (Losasso & Hoppe, SIGGRAPH 2004) LOD के लिए। बढ़ते रिज़ॉल्यूशन पर nested regular grids camera की position को घेरते हैं। सबसे अंदरूनी ग्रिड का रिज़ॉल्यूशन सबसे बारीक होता है; बाहरी ग्रिड क्रमशः मोटे होते हैं। यह विशाल स्पेस का समर्थन करते हुए मेमोरी में भारी कमी देता है। Clipmaps जैसे-जैसे camera हिलता है incrementally अपडेट होते हैं, जो उन्हें streaming open worlds के लिए कारगर बनाता है।
Physics और collision सीधे SDF के विरुद्ध काम करते हैं। Sphere-tracing (step size के तौर पर SDF distance के साथ ray marching) कारगर raycasting देता है। Collision detection surface normal के तौर पर SDF gradient और penetration depth के तौर पर distance value का इस्तेमाल करता है।
Teardown: बड़े पैमाने पर Voxel Destruction
Teardown (Voxagon) इस spectrum का दूसरा छोर दर्शाता है: वर्ल्ड में हर ऑब्जेक्ट एक voxel volume है जिसे टुकड़े-टुकड़े नष्ट किया जा सकता है।
आर्किटेक्चर
Objects नियमित spacing पर voxel grids के तौर पर स्टोर होते हैं। engine रेंडरिंग के लिए Marching Cubes या SDFs का इस्तेमाल नहीं करता। इसके बजाय, यह fragment shaders में एक संशोधित DDA (Digital Differential Analyzer) algorithm का इस्तेमाल करके voxels को सीधे ray-trace करता है, जो OpenGL 3.3 पर बना है। Mipmaps एक dense octree structure बनाते हैं ताकि ray intersection के दौरान खाली स्पेस को पार करना तेज़ हो।
हर ऑब्जेक्ट के लिए, engine उसके oriented bounding box (OBB) को rasterize करता है और voxel intersections खोजने के लिए उसमें से एक ray trace करता है। सिर्फ OBB के backfaces render होते हैं, जिससे camera bounding volume में clip कर सकता है।
Destruction Synchronization (मल्टीप्लेयर)
Teardown का मार्च 2026 का मल्टीप्लेयर अपडेट एक semi-deterministic तरीका इस्तेमाल करता है। Structural destruction (holes काटना, ownership बदलना, joints फिर से जोड़ना) एक reliable network stream पर fixed-point integer math के ज़रिए संभाला जाता है। सभी clients वही deterministic commands चलाते हैं और वही world state पर पहुंचते हैं। Non-structural बदलाव (मलबा, particles) unreliable state synchronization इस्तेमाल करते हैं।
यह हमारे मल्टीप्लेयर वर्ल्ड के लिए एक ज़रूरी बात है: टेरेन edits deterministic होने चाहिए। अगर Player A एक पहाड़ स्कल्प्ट करे, तो सभी clients को वही field data से वही mesh बनाना होगा। edit commands (ब्रश position, radius, strength, operation type) authoritative data होने चाहिए, नतीजे वाला mesh नहीं।
ALICE-SDF: Compression और CSG Trees
ALICE-SDF (Adaptive Lightweight Implicit Compression Engine, v1.3.0 मार्च 2026) SDF-आधारित spatial data की एक Rust implementation देता है जो polygon meshes के मुकाबले 10-1000x compression करता है। यह इनका समर्थन करता है:
126 building blocks: 72 primitives, 24 operations, 7 transforms, और 23 modifiers। Smooth blending operations (union, subtraction, intersection), hard-edge bevels और stepped CSG transitions के लिए chamfer और stairs blends।
CSG tree diff/patch undo/redo और नेटवर्क सिंक्रोनाइज़ेशन के लिए। यह मल्टीप्लेयर स्कल्प्टिंग के लिए मुख्य फ़ीचर है: पूरी field state भेजने के बजाय, आप दो CSG trees के बीच का structural diff भेजते हैं। client नई state को फिर से बनाने के लिए patch लागू करता है। यह raw voxel data को delta-compress करने से कहीं ज़्यादा bandwidth-कारगर है।
CSG tree optimization जिसमें identity transform हटाना, nested transform merging, और modifier demotion शामिल है। यह जैसे-जैसे edits जमा होते हैं, tree को compact रखता है।
Mesh generation Marching Cubes और Dual Contouring दोनों के ज़रिए। Physics collision detection सीधे SDF के विरुद्ध काम करता है।
WebAssembly support browser integration को मुमकिन बनाता है। engine Rust में WASM bindings के साथ लिखा गया है, जो इसे एक Three.js application के लिए एक व्यावहारिक विकल्प बनाता है।
टेरेन के लिए WebGPU Compute
WebGPU अब 2025 के अंत तक सभी प्रमुख browsers में उपलब्ध है। Chrome 113+, Edge 113+, Firefox 141+, और Safari 26+ सभी इसे enabled के साथ शिप करते हैं। यह compute shader pipelines खोलता है जो पहले सिर्फ GPU-only थीं।
Performance
WebGPU compute shaders भारी parallelization के ज़रिए CPU methods से करीब 100x तेज़ टेरेन बनाते हैं। GPU एक साथ हज़ारों calculations चलाता है, और टेरेन generation लगभग पूरी तरह parallel है (हर vertex/voxel स्वतंत्र है)।
काम तीन tiers में संगठित होता है: Dispatch Level (GPU के पार workload बांटना), Workgroup Level (एक processing unit के भीतर shared memory), और Thread Level (अलग-अलग calculations)। WGSL (WebGPU Shading Language) shader language है।
रियल टाइम टेरेन स्कल्प्टिंग Pipeline
एक WebGPU स्कल्प्टिंग pipeline ऐसी दिखेगी:
ब्रश application (compute shader): ब्रश radius के भीतर scalar field values अपडेट करें। हर thread एक voxel संभालता है। एक uniform buffer से ब्रश parameters (position, radius, strength, falloff type, operation) पढ़ता है और modification लागू करता है।
Mesh extraction (compute shader): बदले हुए region पर Surface Nets या Marching Cubes चलाएं। Nijhoff का WebGPU SDF Editor इसे एक multi-stage pipeline के तौर पर लागू करता है: 16,384 cells के पार space partitioning, octree-आधारित cell splitting, फिर surface extraction।
Vertex buffer अपडेट (GPU-side): extracted vertices को CPU memory से होकर गए बिना सीधे एक render buffer में लिखें।
Normal computation (compute shader): mesh से या SDF gradient से vertex normals की गणना करें।
Render (मानक pipeline): mesh को मानक PBR materials के साथ draw करें।
चरण 1-4 सभी GPU पर बिना किसी data के JavaScript में लौटे चल सकते हैं। CPU को सिर्फ हर frame पर ब्रश parameters भेजने की ज़रूरत होती है।
WebGPU SDF Editor
Reinder Nijhoff का WebGPU SDF Editor (जनवरी 2026) Chrome में चलते हुए इस तरीके को दिखाता है। यह छह primitives (cone, cylinder, capsule, torus, box, sphere), configurable smooth blending के साथ तीन blend operations (union, subtraction, intersection), और hierarchical scene graphs का समर्थन करता है। हर primitive एक ही GPU buffer में 112 bytes लेता है।
रेंडरिंग pipeline ambient occlusion और temporal anti-aliasing के लिए 1,024 shadow maps का इस्तेमाल करता है। यह high-end GPUs पर interactive framerates पर चलता है।
हाइटमैप स्कल्प्टिंग: सरल रास्ता
अगर गुफा और overhang support ज़रूरी नहीं है, तो हाइटमैप स्कल्प्टिंग पूरी वॉल्यूमेट्रिक pipeline को टाल देती है। ज़्यादातर शिप किए गए games टेरेन editing को ऐसे ही संभालते हैं।
Runtime Implementation Pattern
Unity Runtime Terrain (JohannHotzel, जनवरी 2026) मानक pattern दिखाता है:
- Raycast camera से mouse position के ज़रिए टेरेन hit point खोजने के लिए।
- Hit point को मैप करें हाइटमैप coordinates पर।
- ब्रश लागू करें आसपास के हाइटमैप values पर, falloff से weighted।
- Mesh अपडेट करें बदले हुए हाइटमैप से vertex Y positions सेट करके।
- Physics collider फिर से बनाएं नए mesh से मिलाने के लिए।
हमारे Three.js टेरेन के लिए, चरण 1-4 सीधे मौजूदा आर्किटेक्चर पर मैप होते हैं। Chunk class पहले से ही हाइटमैप स्टोर करती है और उनसे meshes बनाती है। स्कल्प्टिंग जोड़ने का मतलब होगा:
- chunk meshes के विरुद्ध एक raycasting सिस्टम (Three.js
Raycaster) - ब्रश application functions जो
chunk.heightmapvalues बदलते हैं - Mesh vertex अपडेट (Y positions सेट करें, normals फिर से calculate करें)
- प्रभावित region के लिए Splat map फिर से calculate करना (ताकि टेक्सचर ब्लेंडिंग नई slope/height दर्शाए)
- मौजूदा WebSocket protocol के ज़रिए दूसरे clients को edit का नेटवर्क broadcast (ब्रश position, radius, strength, operation)
Clipmap का तरीका
Landow.dev हाइटमैप टेरेन के लिए एक "wandering clipmap" बताता है: variable subdivision density वाला एक ही mesh जो player का पीछा करता है। हाइटमैप को अलग-अलग LOD levels वाली अलग meshes में chunk करने (जो हम अभी करते हैं) के बजाय, clipmap एक continuous mesh है जो camera के पास dense और किनारों पर मोटा है।
यह LOD stitching को पूरी तरह खत्म कर देता है। mesh में बस वहां ज़्यादा triangles होते हैं जहां आपको चाहिए और जहां नहीं चाहिए वहां कम। इसका मोल यह है कि स्कल्प्टिंग के लिए एक बड़े mesh को अपडेट करना पड़ता है, अलग chunks नहीं, जो बड़े edits के लिए महंगा हो सकता है।
Non-Destructive SDF हाइटमैप
Landow.dev एक तकनीक भी बताता है जहां हाइटमैप खुद एक SDF composition से बनता है। Shape instances (spheres, boxes, noise functions) एक compute shader में CSG operations का इस्तेमाल करके compose होते हैं, और output को एक हाइटमैप के तौर पर sample किया जाता है। यह हाइटमैप रेंडरिंग की सरलता बनाए रखते हुए non-destructive editing देता है (आप किसी भी समय किसी भी shape instance को हिला या मिटा सकते हैं)।
यह एक दमदार hybrid है: data रिप्रेज़ेंटेशन वॉल्यूमेट्रिक (SDF CSG tree) है, लेकिन रेंडरिंग का रास्ता एक मानक हाइटमैप mesh है। आपको SDF की तरफ़ से undo/redo और नेटवर्क-अनुकूल edit operations मिलते हैं, और हाइटमैप की तरफ़ से सरल रेंडरिंग और physics। सीमा बनी रहती है: कोई गुफाएं या overhangs नहीं।
बड़े वर्ल्ड के लिए Sparse Voxel Octrees
Dense 3D grids scale नहीं करते। हर तरफ़ 1 km वाला वर्ल्ड 0.5m रिज़ॉल्यूशन पर 8 अरब voxels मांगेगा। Sparse Voxel Octrees (SVOs) इसे space को बार-बार subdivide करके और सिर्फ़ उन octants के लिए storage आवंटित करके हल करते हैं जिनमें surface boundary होती है।
एक SVO स्वाभाविक रूप से hierarchical LOD देता है: किसी भी पॉइंट पर tree की गहराई effective रिज़ॉल्यूशन तय करती है। player के पास, tree पूरी तरह फैला होता है (अधिकतम detail)। दूर, यह एक मोटे level पर कटा होता है।
रेंडरिंग के लिए, SVOs को सीधे ray-trace किया जा सकता है (कोई mesh extraction की ज़रूरत नहीं)। एक GPU ray marcher हर tree level पर axis-aligned boxes के साथ rays को intersect करता है, खाली subtrees को पूरी तरह छोड़ते हुए। यह chunk-आधारित रेंडरिंग की overdraw समस्याओं को खत्म करता है और greedy meshing artifacts को टालता है।
AdamYuan का Vulkan-आधारित SVO builder काफ़ी performance दिखाता है: एक GTX 1660 Ti पर 2^10 रिज़ॉल्यूशन पर Crytek Sponza के लिए 19ms build time।
स्कल्प्टिंग के लिए, SVO modification कारगर है: सिर्फ़ ब्रश radius के भीतर leaf nodes अपडेट करने की ज़रूरत होती है, और tree structure अलग-अलग रिज़ॉल्यूशन को स्वाभाविक रूप से संभालता है। जहां player स्कल्प्ट करता है वहां detail जोड़ना (higher रिज़ॉल्यूशन पर nodes splitting) और जहां वे smooth करते हैं वहां detail हटाना (lower रिज़ॉल्यूशन पर nodes merging) data structure से अपने आप निकल आता है।
Browser deployment के लिए चुनौती यह है कि WebGL compute shaders का समर्थन नहीं करता, और जबकि WebGPU करता है, SVO construction और traversal algorithms को WGSL में लागू करना जटिल है।
मल्टीप्लेयर स्कल्प्टिंग के लिए नेटवर्क सिंक्रोनाइज़ेशन
हमारे वर्ल्ड में पहले से ही Cloudflare Durable Objects (world-chunk-do.ts) के ज़रिए मल्टीप्लेयर है। स्कल्प्टिंग जोड़ने का मतलब है सभी जुड़े clients के पार टेरेन modifications को सिंक्रोनाइज़ करना।
Delta Compression
Raw voxel data भेजना महंगा है। Oulu University के 2024 के एक अध्ययन ने delta-encoding को DEFLATE compression के साथ मिलाकर 2-8x payload सुधार हासिल किया, voxel updates को प्रति voxel एक byte से कम में पैक करते हुए। SDEC codec bit-packed delta encoding दिखाता है जो generic serialization के 1114 bytes के मुकाबले 259-byte औसत packets बनाता है।
Operation-आधारित Sync (सुझावित)
Field state सिंक्रोनाइज़ करने के बजाय, operations सिंक्रोनाइज़ करें। हर स्कल्प्टिंग action एक message बन जाता है:
interface TerrainEditMsg {
t: MsgType.TerrainEdit
brush: {
position: [number, number, number]
radius: number
strength: number
falloff: 'smooth' | 'linear' | 'sharp' | 'constant'
operation: 'raise' | 'lower' | 'smooth' | 'flatten' | 'noise'
targetHeight?: number
}
}server इसे सभी clients को broadcast करता है, और हर client अपने स्थानीय टेरेन data पर वही deterministic ब्रश operation लागू करता है। यह वही तरीका है जो Teardown structural destruction के लिए इस्तेमाल करता है: एक reliable stream पर deterministic commands।
ALICE-SDF का CSG tree diff/patch इसे और आगे ले जाता है: अलग-अलग ब्रश strokes के बजाय, diff पूरे CSG tree में structural बदलाव को दर्शाता है। यह नेटवर्क के पार कारगर undo/redo को मुमकिन बनाता है (inverse patch भेजें) और देर से जुड़ने वाले clients operation log को replay करके पूरी world state फिर से बना सकते हैं।
Priority और Throttling
जुड़े players के पास के टेरेन edits हाई-प्रायोरिटी होने चाहिए (तुरंत broadcast)। किसी भी player से दूर के edits को batch करके कम frequency पर भेजा जा सकता है। Enshrouded की voxel networking इस pattern का इस्तेमाल करती है: player के पास के टेरेन के लिए 60Hz updates, background regions के लिए 10Hz।
टेरेन update messages के लिए wire पर ZSTD compression packet sizes को 60% तक कम कर देता है।
Collaborative स्कल्प्टिंग: Concurrent Edits
जब कई players एक ही region को एक साथ स्कल्प्ट करें, तो आपको conflict resolution चाहिए। cSculpt (CNR Visual Computing Lab, 2016) ने इसे एक multiresolution merge algorithm से हल किया। हर edit को कई scales पर दर्शाया जाता है, और concurrent overlapping edits को उनके multiresolution representations को ब्लेंड करके merge किया जाता है।
हमारे मकसद के लिए, एक सरल तरीका काम करता है: server ordering के साथ last-write-wins। Durable Object हर edit को timestamp करता है और उन्हें क्रम में broadcast करता है। सभी clients edits को एक ही sequence में लागू करते हैं। चूंकि ब्रश strokes छोटे, स्थानीय, और additive/subtractive होते हैं, थोड़े पुनः-क्रमबद्ध concurrent edits का दृश्य नतीजा आम तौर पर "सही" क्रम से अलग नहीं दिखता।
INST-Sculpt: Neural SDF Editing (अनुसंधान का अग्रिम मोर्चा)
INST-Sculpt (arxiv 2502.02891, फ़रवरी 2025) neural SDFs के stroke-आधारित editing को मुमकिन बनाता है। Users सतह पर strokes बनाते हैं, और सिस्टम stroke के रास्ते के आसपास tubular neighborhoods के साथ अंतर्निहित neural field को deform करता है। Custom ब्रश profiles (configurable cross-sections) deformation के आकार को नियंत्रित करते हैं।
यह AI-जनित टेरेन के लिए दिलचस्प है: अगर base वर्ल्ड को एक neural SDF (एक छोटा neural network जो 3D coordinates को signed distance पर मैप करता है) के तौर पर दर्शाया जाए, तो स्कल्प्टिंग explicit voxel data के बजाय network weights बदलती है। रिप्रेज़ेंटेशन बेहद compact होता है (पूरे वर्ल्ड के लिए कुछ MB) लेकिन evaluation एक lookup table से ज़्यादा महंगा होता है।
यह अनुसंधान-चरण की तकनीक है। consumer hardware पर neural SDFs की inference लागत आज रियल टाइम game इस्तेमाल के लिए बहुत ज़्यादा है। लेकिन इस पर नज़र रखना सही है, खासकर जैसे-जैसे WebGPU shader क्षमताएं सुधरती हैं और model inference तेज़ होता है।
World Creator 2026.3: Commercial Terrain का अत्याधुनिक स्तर
World Creator (BiteTheBytes, मार्च 2026) टेरेन authoring tools के लिए commercial अत्याधुनिक स्तर दर्शाता है। Version 2026.3 ने automatic terrain adaptation (टेरेन रखे गए objects के अनुरूप ढलता है) के साथ GPU-आधारित टेरेन generation, LOD optimization के लिए camera-केंद्रित object distribution, और real-world elevation data import (GeoTIFF, HGT, DTED) जोड़ा।
उसके बाद से, World Creator 2026.4 (28 अप्रैल, 2026) ने numerical fields में mathematical expressions, hero objects को सतह में बैठाने के लिए terrain normal blending, पूरा decal support, और VRAM scaling जोड़ा जो उपलब्ध GPU memory के हिसाब से अधिकतम object count को समायोजित करता है। BiteTheBytes ने एक मुफ़्त Community Edition भी शिप किया जो feature-complete है लेकिन export-disabled है, असल में एक असीमित trial।
उनका तरीका सभी टेरेन operations के लिए GPU compute इस्तेमाल करता है: erosion simulation, river carving, texture painting। ब्रश tools रियल टाइम viewport feedback के साथ GPU-accelerated हैं। यह ऊपर बताई गई WebGPU compute pipeline से मेल खाता है, जो desktop GPUs पर चलती है।
हमने पहले ही क्या बनाया है: 24 Spikes और एक Production World
world/spikes/ directory में 24 self-contained prototypes हैं। ये खिलौना demos नहीं हैं। ये एक progressive R&D pipeline है जहां हर spike ने एक खास समस्या हल की, उसे एक target के विरुद्ध benchmark किया, और अगले को राह दिखाई। स्कल्प्टिंग सिस्टम इन सब पर बना है, सिर्फ़ बाद वाले वॉल्यूमेट्रिक पर नहीं।
Production Heightmap Terrain (world/client/)
लाइव वर्ल्ड Three.js WebGL में एक chunked हाइटमैप सिस्टम इस्तेमाल करता है:
noise.tsFBM value noise (पहाड़ियों के लिए 5 octaves, ridges के लिए 4, micro-detail के लिए 3) के ज़रिए एक deterministicterrainHeight(wx, wz)function के साथ टेरेन height बनाता हैchunk.tsFloat32Arrayहाइटमैप से 3 LOD levels (32/8/4 segments प्रति 64-unit chunk) के साथPlaneGeometrymeshes बनाता है, और seeded random placement और per-object colliders के साथ instanced trees/billboards रखता हैchunk-manager.tsplayer के आसपास rings में chunks stream करता है (LOD0 पर radius 1, LOD1 पर radius 3, LOD2 पर radius 6) जिसमेंstitchEdge()में linear interpolation के ज़रिए edge stitching होती है, और physics layer के लिएgetHeight(),getNormal(), औरresolveCollisions()देता हैterrain-material.tsslope और height-संचालित weights के साथMeshStandardMaterial.onBeforeCompileके ज़रिए 4-layer splat-आधारित टेक्सचर ब्लेंडिंग (grass/rock/sand/dirt) करता है, साथ ही per-layer normal map blendingcharacter-controller.tsहर frame पर gravity, grounding, और slope rejection (अधिकतम slope cos 50 degrees) के लिए टेरेन height sample करता है। स्कल्प्टिंग को बदली हुई heights इस सिस्टम में तुरंत feed करनी होंगी वरना player edit किए गए टेरेन से होकर गिर जाएगाplacement.tsमें object placement tool के लिए chunk meshes पर hit करने वाला एकRaycasterपहले से है। ब्रश tool को raycasting को शून्य से बनाने के बजाय ठीक इसी pattern का पालन करना चाहिएprotocol.tsworld-chunk-do.tsDurable Object के ज़रिए मल्टीप्लेयर sync के लिए MessagePack-encoded messages परिभाषित करता है, जो अभीPlayerState,PlaceObject,RemoveObject, औरSnapshotmessages संभालता है। टेरेन edits के लिए एक नई message type चाहिए होगीworld-chunk-do.ts(Cloudflare Worker) रखे गए objects को Durable Object storage में persist करता है और जुड़े players को 50ms अंतराल पर broadcast करता है। इसके पास अभी टेरेन modifications की कोई अवधारणा नहीं है
Spikes 01-11: Foundation Layer
इन spikes ने उन मुख्य systems को validate किया जिन पर स्कल्प्टिंग निर्भर होगी। इन्हें छोड़ने का मतलब है उन constraints को चूकना जिनका स्कल्प्टिंग सिस्टम को सम्मान करना होगा।
Spike 01 (Terrain + Instancing): Three.js में पहला टेरेन prototype। उस PlaneGeometry + हाइटमैप pattern और instanced object placement को स्थापित किया जो chunk.ts आज भी इस्तेमाल करता है।
Spike 02 (Rapier Physics Worker): एक ColliderDesc.heightfield() collider के साथ एक Web Worker में चलता Rapier 3D। autostep, slope limits, और snap-to-ground के साथ एक kinematic character controller बनाया। इस spike ने साबित किया कि physics एक heightfield के विरुद्ध main thread से अलग चल सकती है। अगर हम टेरेन स्कल्प्ट करें, तो physics heightfield को फिर से बनाना होगा या MC chunks के लिए एक trimesh collider से बदलना होगा।
Spike 05 (LLM Behaviors): सीधे टेरेन से जुड़ा नहीं, लेकिन game objects के लिए JSON behavior schema स्थापित किया। प्रासंगिक इसलिए कि स्कल्प्ट किए गए टेरेन फ़ीचर्स behaviors trigger कर सकते हैं (जैसे, एक कटी हुई नदी पानी के effects spawn करती है)।
Spike 06 (Chunk Streaming): player के हिलने पर dynamic loading के साथ पहला chunk load/swap सिस्टम। उस pattern को स्थापित किया जो chunk-manager.ts इस्तेमाल करता है: रंगीन regions जो load और unload होते हैं। chunks के unload और reload होने पर स्कल्प्टिंग को edit state बनाए रखनी होगी।
Spike 07 (GPU Vegetation from Density Maps): density maps के ज़रिए रखे गए instanced grass और trees जो टेरेन height और slope sample करते हैं। स्कल्प्टिंग vegetation placement को invalid कर देती है: अगर टेरेन height बदले, तो trees तैरते या दबे हुए हो सकते हैं। edit किए गए chunks के लिए density map को फिर से बनाना होगा।
Spike 08 (Terrain Material Shader Cost): triplanar projection, normal maps, और 4-layer blending को benchmark किया। हर फ़ीचर की सटीक ms लागत मापी। पाया कि triplanar + normals + 4 layers 45+ FPS पर budget के भीतर रहते हैं। यह budget स्कल्प्ट किए गए टेरेन के लिए मायने रखता है: अगर हम "edited soil" के लिए 5वीं layer जोड़ें या कटी हुई सतहों के लिए blending बदलें, तो हमें ठीक-ठीक पता है कितनी headroom मौजूद है।
Spike 09 (CSM Shadows Budget): 1024^2 रिज़ॉल्यूशन पर 3 cascades के साथ cascaded shadow maps। shadow लागत को ~1.5ms पर मापा। स्कल्प्ट किया गया टेरेन shadow maps बदलता है, लेकिन लागत टेरेन के आकार से बेपरवाह स्थिर रहती है।
Spike 10 (Geometry Clipmaps + Geomorphing): pop-in खत्म करने के लिए LOD levels के बीच geomorphing के साथ nested clipmap rings। स्थिर triangle count का मतलब है अनुमानित GPU लागत। Geomorphing स्कल्प्टिंग के लिए मायने रखता है: जब player किसी LOD boundary के पास स्कल्प्ट करे, तो LOD levels के बीच का morph edit दर्शाना चाहिए। अगर edit सिर्फ़ high-res ring में मौजूद है, तो geomorph target गलत है।
Spike 11 (Heightmap Chunk Streaming): ज़्यादा उन्नत chunk streaming जिसमें per LOD level loaded/loading/unloaded states दिखाता एक visual grid है। streaming budget स्थापित किया: प्रति frame अधिकतम chunks loaded, उन chunks की prioritization जिन्हें LOD upgrades चाहिए। स्कल्प्टिंग एक नया priority signal जोड़ती है: जिन chunks को player सक्रिय रूप से edit कर रहा है उन्हें कभी unload नहीं करना चाहिए।
Spikes 12-14: WebGPU + Three.js Integration
Spike 12 (WebGPU Marching Cubes): पहला वॉल्यूमेट्रिक spike। animated sphere caves के साथ चार 64^3 SDF chunks, जो पूरी तरह GPU पर चलते हैं। raw WebGPU इस्तेमाल करता है: SDF evaluation के लिए compute pipelines, Twinklebear case table (256 कॉन्फ़िगरेशन, हर एक में 16 entries) के साथ MC extraction, atomic vertex counter, और indirect draw। Performance target प्रति chunk <4ms, सभी 4 के लिए <12ms था। इसने validate किया कि GPU MC browser में रियल टाइम re-meshing के लिए काफी तेज़ है। हर बाद का वॉल्यूमेट्रिक spike यहां परिभाषित MC case table और WGSL shaders का फिर से इस्तेमाल करता है।
Spike 13 (Reset Baseline from Spike 12): Spike 12 के raw WebGPU draw path को Three.js के WebGPURenderer के अंदर चलने के लिए port किया, backend के device को सीधे access करते हुए। render pipeline अभी भी raw WebGPU इस्तेमाल करता है (drawIndirect struct Vertex vec4+vec4 के साथ)। इसने साबित किया कि custom compute और Three.js scene rendering एक ही GPU device पर साथ रह सकते हैं।
Spike 14 (Three.js WebGPU Incremental Hardening): raw render pipeline को positions और normals के लिए Three.js StorageBufferAttribute से बदला। MC compute सीधे इन GPU-resident buffers में लिखता है। drawIndirect buffer नियंत्रित करता है कि Three.js कितने vertices draw करता है। यह वह pattern है जो हर बाद का spike इस्तेमाल करता है: compute raw WebGPU रहता है, rendering Three.js scene graph से होकर जाती है। इन spikes में Three.js version 0.170.0 से 0.172.0 तक बढ़ा जैसे-जैसे WebGPU backend स्थिर हुआ।
Spike 15-17: Transvoxel LOD Stitching
Spike 15 (Transvoxel Seam Scaffold): तीन-zone आर्किटेक्चर जोड़ा: MC chunk (वॉल्यूमेट्रिक केंद्र), transition strip (MC boundary और हाइटमैप के बीच seam), और terrain ring (आसपास का हाइटमैप)। तीनों एक material pass शेयर करते हैं। इस चरण पर transition strip एक placeholder mesh है, असली Transvoxel cells नहीं।
Spike 16 (Transvoxel +X Face with Shared Heightmap): एक ही spike में दो ज़रूरी सफलताएं। पहली, SDF में सपाट टेरेन plane को एक shared Perlin हाइटमैप से बदला: एक 257x257 Float32Array जो GPU पर एक storage buffer के तौर पर upload हुआ, SDF compute shader में bilinear interpolation के ज़रिए sample किया गया। MC surface और हाइटमैप mesh अब एक ही ground truth पर सहमत हैं। दूसरी, GitHub से Eric Lengyel के reference data tables (transitionCellClass, transitionVertexData, transitionCellData) और npm transvoxel-data package fetch करके +X face के लिए असली Transvoxel transition cells लागू कीं। CPU 9-sample transition cells (512 कॉन्फ़िगरेशन, 73 equivalence classes) evaluate करता है, grid points पर SDF values को interpolate करके vertices रखता है, और mirrored cases के लिए winding inversion संभालता है।
Spike 17 (Dual MC 1x/2x LOD): अलग-अलग रिज़ॉल्यूशन पर साथ-साथ दो MC chunks। High-res: cell_scale=1.0 के साथ 62 cells। Low-res: cell_scale=2.0 के साथ 31 cells। MC shader को cell_scale और grid_points uniforms मिले। transition_shrink पेश किया: low-res chunk के face-0 boundary vertices को cell_scale के 15% अंदर खींचा जाता है, जिससे एक पतली खाली जगह बनती है जिसे Transvoxel transition cells z-fighting के बिना भरते हैं। यह वही LOD model है जो production सिस्टम को चाहिए: पास के chunks पूरी रिज़ॉल्यूशन पर, दूर के chunks आधी रिज़ॉल्यूशन पर, हर boundary पर Transvoxel।
Spikes 18-21: Transvoxel Corner Cases और GPU Acceleration
इन चारों spikes ने Transvoxel implementation में एक खास failure case हल किया। इन्हें एक साथ रखना अलग-अलग समस्याओं को छिपा देता है।
Spike 18 (Heightmap 2:1 Seam): Transvoxel को एक pure हाइटमैप boundary पर लागू किया जहां एक तरफ़ दूसरी से दोगुनी रिज़ॉल्यूशन है। कोई MC शामिल नहीं। एक 62-cell और 31-cell हाइटमैप chunk के बीच का seam Transvoxel transition tables से 15% low-face shrink के साथ बनता है। इसने validate किया कि Transvoxel सिर्फ़ MC ही नहीं, हाइटमैप-only case के लिए भी काम करता है।
Spike 19 (64/32/32/16 Corner Grid): सबसे कठिन stitching case: एक corner point पर मिलने वाले अलग-अलग रिज़ॉल्यूशन के चार chunks (64, 32, 32, और 16 cells)। seam सिस्टम को चार edges (A-B, A-C, B-D, C-D) के साथ हर दिशा के लिए सही winding के साथ transition cells बनानी होंगी। इस spike ने साबित किया कि Transvoxel tables custom-case logic के बिना multi-resolution corner संभालते हैं।
Spike 20 (GPU Transvoxel Corner): 64/32/32/16 corner layout के लिए Transvoxel transition cell generation को GPU पर ले गया। animated टेरेन के लिए हर frame transition cells फिर से बनाते समय CPU एक रुकावट थी। GPU compute MC extraction के उसी pass में seam vertices बनाता है।
Spike 21 (GPU MC + Transvoxel Corner): एक ही compute dispatch sequence में पूरी GPU MC extraction को GPU Transvoxel seam generation के साथ मिलाया। MC chunks और चारों seams दोनों GPU पर बनते हैं, vertex counts atomic counters के ज़रिए संभाले जाते हैं और drawIndirect के साथ draw होते हैं। यह seamless LOD transitions के साथ multi-resolution वॉल्यूमेट्रिक टेरेन के लिए पूरी GPU pipeline है।
Spikes 22-24: Hybrid Architecture
Spike 22 (Hybrid MC/Heightmap Policy): मुख्य architecture spike। Chunks डिफ़ॉल्ट रूप से हाइटमैप होते हैं। जब animated deformation sphere किसी chunk के AABB को intersect करता है, तो वह chunk MC mode में बदल जाता है। बाकी static हाइटमैप meshes रहते हैं। Layout: अलग-अलग रिज़ॉल्यूशन पर 64, 32/32, और 16-cell chunks। Transvoxel seams MC-to-heightmap transitions समेत हर boundary संभालते हैं। spike प्रति frame MC chunk count बनाम HM chunk count और vertex overflow track करता है।
Spike 23 (Policy-Driven Chunk Modes): Spike 22 के ऊपर एक patch के तौर पर load हुआ। camera-distance hysteresis जोड़ा (जब camera किसी threshold के पास हो तो chunks modes के बीच flicker नहीं करते) और एक edit mask (deform किए गए chunks MC mode में रहते हैं भले ही deformation source दूर चला जाए)। यह वह "sticky edit" behavior है जो स्कल्प्टिंग को चाहिए: एक बार player एक गुफा काट दे, तो वह chunk हमेशा वॉल्यूमेट्रिक रहता है।
Spike 24 (Policy + Clipmap Rings): सबसे उन्नत spike। Spike 23 के near-field policy सिस्टम को Spike 10 के far-field geometry clipmap rings के साथ मिलाता है। Three.js 0.183.1 पर upgrade किया गया। Near-field 64/32/16 रिज़ॉल्यूशन पर Transvoxel seams के साथ HM/MC hybrid इस्तेमाल करता है। Far-field static-center clipmap rings इस्तेमाल करता है जो camera का पीछा करते हैं। यह पूरी टेरेन रेंडरिंग आर्किटेक्चर है: जहां ज़रूरी हो वहां chunked वॉल्यूमेट्रिक स्कल्प्टिंग, बाकी हर जगह सस्ता clipmap टेरेन।
Marching Cubes क्यों, Surface Nets क्यों नहीं
इस गाइड का बाहरी अनुसंधान section browser टेरेन स्कल्प्टिंग के लिए Surface Nets को सबसे मज़बूत उम्मीदवार बताता है। लेकिन pipeline का हर spike Marching Cubes इस्तेमाल करता है। यह कोई संयोग नहीं है।
MC का परिभाषित फ़ायदा embarrassing parallelism है: हर cube पूरी तरह स्वतंत्र है। Spikes 12-24 के WGSL compute shaders शून्य inter-cell communication के साथ प्रति cube एक thread dispatch करते हैं। Atomic counters vertex allocation संभालते हैं। यह GPU workgroups पर पूरी तरह मैप होता है।
Surface Nets प्रति surface-वाले cell एक vertex रखता है, फिर पड़ोसियों को जोड़ता है। वह neighbor connectivity एक inter-cell dependency है। fast-surface-nets crate इसे CPU पर सावधान iteration order के साथ संभालता है। GPU पर, इसके लिए या तो दो-pass तरीका (vertices खोजें, फिर जोड़ें) या workgroups के भीतर shared memory चाहिए। दोनों WebGPU में मुमकिन हैं लेकिन जटिलता जोड़ते हैं।
व्यावहारिक सुझाव: स्कल्प्टिंग pipeline के लिए Marching Cubes के साथ ही रहें। यह हमारे codebase में सिद्ध है, WGSL shaders मौजूद और benchmarked हैं, और Transvoxel seam सिस्टम MC के edge-आधारित vertex placement के इर्द-गिर्द बना है। Surface Nets पर फिर से विचार करना तब सही है जब binary data पर MC का aliasing एक दिखने वाली समस्या बन जाए, लेकिन SDF टेरेन के लिए जहां values smooth gradients हैं, MC साफ़ नतीजे देता है।
स्कल्प्टिंग के लिए व्यावहारिक आर्किटेक्चर
spike sequence ने रेंडरिंग pipeline हल किया। जो बचा है वह है ब्रश सिस्टम, game systems के पार cascading side effects, और मल्टीप्लेयर सिंक्रोनाइज़ेशन। यहां योजना है, हर spike पर बनती हुई।
Phase 1: Heightmap स्कल्प्टिंग (न्यूनतम बदलाव, अधिकतम पहुंच)
production world/client/ कोड में chunk हाइटमैप बदलने वाले ब्रश tools जोड़ें। यह मौजूदा WebGL renderer के भीतर काम करता है और WebGPU की ज़रूरत नहीं है।
ब्रश input: placement.ts में PlacementTool pattern का पालन करें। इसके पास पहले से ही एक Raycaster है जो chunkManager.getChunkMeshes() पर hit करता है और hit point पर एक ghost mesh track करता है। एक TerrainBrushTool वही raycast करेगा लेकिन एक object रखने के बजाय chunk हाइटमैप बदलेगा। World.onMouseDown handler पहले से ही tool state के आधार पर dispatch करता है।
Chunk modification (Chunk.applyBrush): ब्रश world position को हाइटमैप grid coordinates पर मैप करें। ब्रश radius के भीतर हर grid point के लिए, falloff-weighted displacement की गणना करें और हाइटमैप value से जोड़ें/घटाएं। फिर mesh अपडेट करें: बदले हुए हाइटमैप से vertex Y positions सेट करें, central differencing के ज़रिए normals फिर से calculate करें (वही terrainHeight(wx +/- eps, wz) pattern जो chunk.ts line 155-158 में पहले से इस्तेमाल होता है), और प्रभावित region के लिए terrain-material.ts में createSplatMap() के ज़रिए splat map फिर से बनाएं ताकि slope-संचालित टेक्सचर ब्लेंडिंग अपडेट हो।
Character controller: CharacterController.update() grounding के लिए हर frame getHeight() call करता है। ChunkManager.getHeight() Chunk.sampleHeight() को delegate करता है, जो chunk के heightmap Float32Array से पढ़ता है। चूंकि हम उस array को सीधे बदल रहे हैं, character controller अगले frame पर शून्य अतिरिक्त wiring के साथ बदलाव उठा लेता है।
Object invalidation: chunk.ts में tree instances spawn के समय terrainHeight() sample करके रखे जाते हैं। स्कल्प्टिंग के बाद, प्रभावित इलाके के trees गलत height पर हो सकते हैं। Phase 1 इसे टाल सकता है (trees छोटे edits पर थोड़ा तैरते हैं)। Phase 2 को एक chunk.invalidateObjects() चाहिए जो heights फिर से sample करे और instance matrices फिर से बनाए। resolveCollisions() में इस्तेमाल होने वाले colliders के लिए भी यही।
Rapier physics (अगर integrated हो): Spike 02 ने साबित किया कि heightfield colliders काम करते हैं। अगर Rapier सक्रिय है, तो बदले हुए chunk के लिए heightfield collider को फिर से बनाना या patch करना होगा। Rapier का ColliderDesc.heightfield() एक flat Float32Array लेता है, इसलिए यह एक सीधा replacement है।
Network sync: protocol.ts में MsgType.TerrainEdit = 10 जोड़ें:
interface TerrainEditMsg {
t: MsgType.TerrainEdit
cx: number
cz: number
brush: {
wx: number
wz: number
radius: number
strength: number
falloff: number
operation: number
}
}WorldChunkDO इसे सभी clients को broadcast करता है और इसे Durable Object storage में रखे एक per-chunk edit log में append करता है। देर से जुड़ने वाले clients Snapshot message में edit log पाते हैं और टेरेन state फिर से बनाने के लिए इसे replay करते हैं। सभी clients वही deterministic ब्रश function लागू करते हैं, इसलिए वे वही हाइटमैप पर converge करते हैं।
Chunk unload/reload: Spike 06 और Spike 11 ने streaming pattern स्थापित किया। जब एक chunk unload और बाद में reload हो, तो उस chunk के edit log को base procedural हाइटमैप के विरुद्ध replay करना होगा। edit log server-side (Durable Object) स्टोर होता है और Snapshot message में शामिल होता है।
Phase 2: Spike 22-24 Architecture के साथ Volumetric स्कल्प्टिंग
Spike 24 pipeline को production world में port करें। जब कोई player सतह के नीचे स्कल्प्ट करे (एक गुफा काटना, एक tunnel खोदना), तो प्रभावित chunk हाइटमैप mode से MC mode में बदल जाता है।
WebGPU renderer migration: Spikes 13-14 ने साबित किया कि Three.js का WebGPURenderer scene graph के साथ custom compute host कर सकता है। production world MC chunks के लिए StorageBufferAttribute के साथ WebGLRenderer से WebGPURenderer पर जाता है। जब WebGPU उपलब्ध न हो तो Phase 1 हाइटमैप-only path पर fallback।
Per-chunk SDF allocation: Spike 22 के hybrid pattern का पालन करें। हर chunk एक हाइटमैप के तौर पर शुरू होता है। पहले वॉल्यूमेट्रिक ब्रश stroke पर, एक 64^3 Float32Array आवंटित करें, इसे हाइटमैप sample करके initialize करें (हर पॉइंट पर SDF value world.y - heightmap_value है), और MC rendering पर switch करें। Spike 23 का policy सिस्टम सुनिश्चित करता है कि chunk हमेशा के लिए MC mode में रहे (edit mask से "sticky edit" behavior)।
SDF shader में shared heightmap: Spike 16 का height_at() function। chunk के हाइटमैप को एक GPU storage buffer पर upload करें। SDF compute shader max(height_sdf, edit_sdf) evaluate करता है जहां height_sdf = world.y - height_at(world.xz) और edit_sdf में ब्रश modifications होते हैं। MC और हाइटमैप chunks अपनी boundaries पर ground truth पर सहमत होते हैं।
Transvoxel seams: Spikes 15-21 का पूरा stack। MC-to-heightmap boundaries shrink gap के साथ transition cells इस्तेमाल करती हैं। अलग-अलग रिज़ॉल्यूशन पर MC-to-MC boundaries Spike 17 का dual-LOD pattern इस्तेमाल करती हैं। Spike 19 का corner case 4-way intersections संभालता है। Spike 21 का GPU compute सारी seam geometry उसी dispatch में बनाता है।
Clipmap far-field: sculpting range के बाहर के टेरेन के लिए Spike 24 के clipmap rings। स्कल्प्टिंग इन rings को कभी नहीं छूती; वे base procedural हाइटमैप sample करते हैं।
Geomorphing: Spike 10 का geomorphing LOD transitions पर pop-in खत्म करता है। edit किए गए chunks के लिए, geomorph target में edit शामिल होना चाहिए। अगर एक chunk LOD0 पर MC है और उसका LOD1 पड़ोसी एक हाइटमैप है, तो geomorph दोनों representations के बीच ब्लेंड करता है। इसके लिए कम LODs पर भी edit log sample करना होता है।
Material budget: Spike 08 ने 4-layer triplanar + normals को 45+ FPS पर benchmark किया। MC chunks को वही material चाहिए। splat map को हाइटमैप slope के बजाय SDF gradient से बनाया जा सकता है (तीखा = rock, सपाट = grass)। यह 4-layer budget के भीतर रहता है।
Vegetation invalidation: Spike 07 का density-map vegetation टेरेन height और slope पर निर्भर करता है। जब एक chunk MC mode में बदले, तो tree instances को SDF surface sample करके फिर से बनाना होगा। overhangs पर या गुफाओं के अंदर के trees को cull करना होगा। chunk.ts के instanced mesh matrices नई surface से फिर से बनते हैं।
Phase 3: Undo/Redo और Network Sync के लिए CSG Edit Trees
raw SDF mutation को एक CSG operation tree से बदलें। हर ब्रश stroke एक operation (add, subtract, smooth blend) के साथ एक primitive (sphere, capsule, box) append करता है। SDF tree से फिर से calculate होता है।
फ़ायदे:
- Non-destructive: किसी भी edit को undo करने के लिए tree से हटाया जा सकता है
- Network-कारगर: raw field values नहीं, CSG operation broadcast करें
- Deterministic: सभी clients वही operation sequence से वही SDF बनाते हैं
- ALICE-SDF का CSG tree diff/patch नेटवर्क के पार bandwidth-कारगर सिंक्रोनाइज़ेशन और undo/redo देता है
Durable Object storage: प्रति chunk edit tree, Phase 1 के flat edit log की जगह लेता है। WorldChunkDO CSG tree structure स्टोर करता है, raw हाइटमैप deltas नहीं। Snapshot messages में tree शामिल होता है, और देर से जुड़ने वाले clients इसे evaluate करके स्थानीय SDF बनाते हैं।
Phase 4: Collaborative स्कल्प्टिंग
server-ordered operation replay के साथ concurrent edit support जोड़ें। Durable Object हर edit को timestamp करता है और क्रम में broadcast करता है। देर से जुड़ने वाले clients operation log पाते हैं और world state फिर से बनाते हैं। मौजूदा Snapshot message type प्रति chunk टेरेन edit history शामिल करने के लिए फैलती है।
चूंकि ब्रश strokes छोटे, स्थानीय, और additive/subtractive होते हैं, थोड़े पुनः-क्रमबद्ध concurrent edits का दृश्य नतीजा आम तौर पर "सही" क्रम से अलग नहीं दिखता। server ordering के साथ last-write-wins पर्याप्त है। Durable Object का tick() function (अभी player state के लिए 50ms अंतराल पर चलता है) उसी loop में टेरेन edit broadcasts जोड़ता है।
मुख्य संदर्भ
Algorithms:
- Lorensen & Cline, "Marching Cubes" (1987)
- Eric Lengyel, "Transvoxel Algorithm" (2009), transvoxel.org
- Losasso & Hoppe, "Geometry Clipmaps" (SIGGRAPH 2004)
- "A High-Performance SurfaceNets Discrete Isocontouring Algorithm" (arxiv 2401.14906, 2024)
- MCHex (arxiv 2511.02064, 2025)
Implementations:
- bevy-sculpter v0.18.0 (Rust, Surface Nets + SDF brushes)
- fast-surface-nets (Rust, 20M tri/sec)
- ALICE-SDF v1.3.0 (Rust + WASM, CSG tree diff/patch)
- WebGPU SDF Editor (Nijhoff, January 2026)
- SculptingPro (Unity runtime sculpting API)
- TerraBrush (Godot terrain sculpting GDExtension)
Games:
- Dreams (Media Molecule, SDF + point cloud rendering, SIGGRAPH 2015)
- Teardown (Voxagon, voxel DDA ray tracing, deterministic multiplayer destruction)
- Mike Turitzin's SDF engine (brick maps + geometry clipmaps, January 2026)
Network:
- "Optimizing payload size for voxel state synchronization" (Oulu, 2024)
- Teardown multiplayer (semi-deterministic destruction sync, March 2026)
- cSculpt (collaborative mesh sculpting with multiresolution merge)