- आसमान की पृष्ठभूमि के लिए चंद्रमा और गैस ग्रहों में वास्तविक sphere mesh के बिना भी केवल वृत्ताकार डिस्क और pixel shader से सतह, घूर्णन और वायुमंडल की अभिव्यक्ति की जा सकती है
- पारंपरिक UV sphere में 2×1 texture mapping आसान होती है, लेकिन साथ में कोणीय outline, mipmap sampling loss, दोहराते पैटर्न, seam, ध्रुवीय distortion और atmospheric halo की सीमाएँ भी आती हैं
- डिस्क विधि में केंद्र UV से sphere surface position को पुनर्निर्मित करने के बाद
asin-आधारित spherical UV, rotation matrix और u-coordinate offset spin के जरिए सतह को pixel स्तर पर sphere जैसा बनाया जाता है
- shading की शुरुआत surface position को normal मानकर Lambertian lighting से होती है, और पथरीली सतहों पर TBN matrix और normal map लागू किया जाता है, जबकि वायुमंडल के लिए halo brightness और channel-वार absorption का approximation किया जाता है
- Substance Designer के static textures और Unreal के dynamic rendering textures को एक ही तरीके से संभाला जा सकता है, इसलिए छोटे dynamic textures और tiling adjustment से performance load घटाया जा सकता है
स्काईबॉक्स ग्रहों का दृश्य लक्ष्य
- प्रोजेक्ट का स्काईबॉक्स animated moon और gas planet के जरिए किसी परग्रही आकाश जैसी संरचना बनाने पर केंद्रित है
- दोनों ग्रह घूमते हैं, और gas planet में चलती हुई वायुमंडलीय धाराएँ भी शामिल हैं
- वास्तविक गति सूक्ष्म होती है, लेकिन दृश्य प्रभाव के लिए इसकी रफ्तार बढ़ाई गई है
- art direction 1970~80 के दशक की SF art से प्रेरित semi-realistic शैली है
सतह texture input और सीमाएँ
- gas planet की सतह pixel shader और render-to-texture विधि से real-time में बनाई जाती है
- चंद्रमा का texture Substance Designer में बनाया जाता है, जहाँ color palette और material seed बदलकर अलग-अलग look आज़माए जा सकते हैं
- dynamic gas planet में हर frame texture render करना पड़ता है, इसलिए performance cost अधिक होती है
- texture resolution को 2 गुना करने पर rendering cost 4 गुना हो जाती है
- ऐसा समाधान चाहिए जो static Substance Designer textures और Unreal में dynamically render किए गए textures—दोनों पर एकसमान काम करे
- cubemap पर शुरुआत में विचार किया गया था, लेकिन यह square texture की तुलना में 6 गुना resources ले सकता है, और Substance Designer cubemap generation को support नहीं करता, इसलिए यह व्यावहारिक नहीं था
UV sphere में सामने आई सीमाएँ
- UV sphere 2×1 rectangular texture और coordinates को मिलाने में आसान होने के कारण मूल विकल्प जैसा लगता है
- चूँकि एक समय में sphere का केवल एक ही पक्ष दिखता है, इसलिए पूरे unique rectangular texture की जगह tiling square texture भी उपयोग की जा सकती है
- वास्तविक उपयोग में कई समस्याएँ एक साथ सामने आती हैं
- outline साफ़ तौर पर कोणीय दिखती है, इसलिए subdivision बढ़ाने या pixel shader mask से छिपाने की ज़रूरत पड़ती है
- sphere की अधिकांश सतह तिरछे कोण से दिखती है, इसलिए lower mipmap से sampling होती है, और dynamic texture में हर frame बनाए गए काफी pixels लगभग अदृश्य क्षेत्रों पर खर्च हो जाते हैं
- tiling को पूरे पैटर्न की continuity के लिए integer multiples में बढ़ाना पड़ता है, और 2 गुना करने पर भी repetition साफ़ दिखने लगती है
- fractional tiling उत्तर ध्रुव से दक्षिण ध्रुव तक जाने वाली seam बनाती है
- ध्रुवों पर texture pinching और triangle mapping distortion होता है, जो PSX के हिलते texture जैसी विकृति से मिलता-जुलता दिखता है
- केवल सतह draw की जा सकती है, इसलिए atmospheric halo के लिए अलग model चाहिए
- UV sphere अब भी planetary modeling में उपयोगी हो सकती है, लेकिन स्काईबॉक्स उपयोग के लिए इसमें बहुत सारे modification और hack जुड़ जाते हैं
वृत्ताकार डिस्क और pixel shader दृष्टिकोण
- स्काईबॉक्स ग्रह दूर से एक ही स्थिति से देखे जाते हैं, इसलिए जटिल 3D sphere mesh अनिवार्य नहीं है
- एक सरल भरी हुई वृत्ताकार polygon disk का उपयोग किया जाता है, और texture mapping pixel shader में संभाली जाती है
- इसी mesh के भीतर atmospheric halo भी draw किया जा सकता है
- डिस्क का UV origin ठीक केंद्र में होना चाहिए
- यदि ग्रह का radius 1 माना जाए, तो वायुमंडल draw करने की जगह के लिए UV coordinates को उससे बाहर तक बढ़ना चाहिए
sphere surface का पुनर्निर्माण
- coordinate system DirectX के अनुरूप left-handed, Y-up system का उपयोग करता है
- Unreal Engine भी left-handed है, लेकिन उसका up direction Z है, इसलिए दिशा और normal map format की जाँच करनी चाहिए
- surface equation डिस्क plane की 2D position को sphere surface की 3D position में map करती है
- पहले यह जाँचने के लिए कि pixel sphere surface के भीतर है या नहीं, UV vector की लंबाई radius से कम है या नहीं, इसे परखा जाता है
float CircleMask( float2 uv, float radius)
{
return length(uv) < radius? 1.0: 0.0;
}
- यह विधि तभी सही काम करती है जब UV coordinates mesh के केंद्र के साथ aligned हों
- sphere surface की local position में UV के x और y को वैसे ही रखा जाता है और केवल z को पुनर्निर्मित किया जाता है
float3 ReconstructSurface(float2 uv)
{
float zSquared = 1.0 - dot(uv, uv);
float z = sqrt(zSquared);
return float3(uv, z);
}
spherical UV बनाना
- square texture को sphere surface पर map करने की प्रक्रिया तीन चरणों में बँटी है
- cylinder पर लपेटना
- y-axis पर भी वही प्रक्रिया दोहराना
- परिणाम को वृत्ताकार रूप में deform करना
- x texture coordinate cylinder को लपेटने वाले कोण के अनुपात में होता है, और
arcsine(x) निकालने के बाद [-π, π] को फिर से [0, 1] में map किया जाता है
- sphere की local width यानी generatrix का उपयोग करके poles पर coordinates को pinching किया जाता है
float2 GenerateSphericalUV(float3 position)
{
float width = sqrt(1.0 - position.y * position.y);
float generatrixX = position.x / width * sign(position.z);
float2 generatrix = float2(generatrixX, position.y);
float2 uv = asin(generatrix) / 3.14159 + float2(0.5, 0.5);
return float2(uv);
}
axis tilt और rotation
- अधिक स्वाभाविक planetary composition के लिए pitch और roll के माध्यम से planet axis को झुकाया जाता है
- SF art में ग्रह अक्सर झुके हुए दिखते हैं, जिससे composition अधिक dynamic लगती है और polar ice caps भी दिखाए जा सकते हैं
- yaw घूर्णन गति के अनुरूप है, लेकिन seam समस्या से बचने के लिए इसे अलग चरण में संभाला जाता है
- mesh sphere में आमतौर पर ग्रह को matrix से transform किया जाता है, लेकिन डिस्क विधि में हर pixel स्वयं surface position परिभाषित करता है, इसलिए केवल rotation वाला सरल 3×3 matrix इस्तेमाल किया जा सकता है
float3x3 CreateRotationMatrix(float pitch, float roll) {
float cosPitch = cos(pitch);
float sinPitch = sin(pitch);
float cosRoll = cos(roll);
float sinRoll = sin(roll);
return (float3x3)(
cosRoll, -sinRoll * cosPitch, sinRoll * sinPitch,
sinRoll, cosRoll * cosPitch, -cosRoll * sinPitch,
0.0, sinPitch, cosPitch
);
}
- Unreal के material editor में matrix को data type के रूप में support नहीं किया जाता, लेकिन इसके workaround मौजूद हैं
scale, seam, spin की हैंडलिंग
- texture scale बदलने पर वह UV seam दिखाई देने लगती है जो मूल texture किनारों के अनुसार छिपी हुई थी
- seam को पूरी तरह और आसानी से हटाना संभव नहीं है, लेकिन इसे पीछे ले जाने पर यह कम दिखाई देती है
- sphere को position sign के आधार पर quadrants में बाँटा जाता है, और back-left तथा back-right quadrants में u-direction offset लागू किया जाता है
float2 GenerateSphericalUV(float3 position, float spin, float scale)
{
float leftRightSign = sign(position.x);
float frontBackSign = sign(position.z);
float width = sqrt(1.0 - position.y * position.y);
float generatrixX = position.x / width * frontBackSign;
float2 generatrix = float2 (generatrixX, position.y);
float2 uv = asin(generatrix) / 3.14159 + float2 (spin, 0.5);
if(frontBackSign < 0.0)
{
uv = float2 (uv.x + 1.0 * leftRightSign, uv.y);
}
return float2 (uv / scale);
}
- seam अब भी उत्तर ध्रुव और दक्षिण ध्रुव को जोड़ती है, लेकिन पीछे खिसक जाने से यह बहुत कम नज़र आती है
- rotation sphere को वास्तव में घुमाकर नहीं, बल्कि surface texture को u-coordinate दिशा में shift करके किया जाता है
float2 GenerateSphericalUV(float3 position, float scale, float spin)
{
float width = sqrt(1.0 - position.y * position.y);
float generatrixX = position.x / width * sign(position.z);
float2 generatrix = float2(generatrixX, position.y);
float2 uv = asin(generatrix) / 3.14159 * scale + float2(0.5 + spin, 0.5);
return float2(uv);
}
float2 sphericalVU = GenerateSphericalUV(position, scale, time*speed)
surface shading और normal map
- ग्रह को गोल दिखाने के लिए shading बेहद महत्वपूर्ण है
- सामान्य engine shading से अलग, ग्रह में सतह और वायुमंडल की दो layers होती हैं, इसलिए दोनों layers की lighting और blending को manually परिभाषित करना पड़ता है
- यदि radius 1 है, तो rotation से पहले की surface position को ही surface normal की तरह उपयोग किया जा सकता है
- lighting के लिए साधारण Lambertian model का उपयोग किया जाता है
- लक्ष्य पूर्ण यथार्थवाद नहीं, बल्कि NASA फ़ोटो से प्रेरित stylized SF cover aesthetics है
float LambertianLight(float3 normal, float3 lightDirection) {
float NdotL = max(dot(normal, lightDirection), 0.0);
return NdotL;
}
- gas giant जैसी चिकनी सतहों के लिए साधारण normal पर्याप्त हो सकता है
- पथरीले ग्रहों के लिए पर्वत, ridge और crater जैसी भौगोलिक विशेषताओं को normal map से simulate करना अधिक उपयुक्त है
- normal map इस्तेमाल करने के लिए Tangent, Bitangent और Normal से बना TBN matrix चाहिए
- sphere पर यह गणना pixel स्तर पर करनी पड़ती है
- TBN matrix normal map के normals को World Space Coordinates में बदलने के लिए उपयोग होता है
UV discontinuity और mipmap artifacts
- pixel shader में बने UV में discontinuity की समस्या आ सकती है
- back seam पर horizontal UV component को सीमा पर 0.0 और 1.0 के बीच पूरी तरह continuous होना चाहिए, लेकिन व्यवहार में seam के साथ blocky artifacts बनते हैं
- यह रेखा UV के DDX से संबंधित है
- DDX और DDY screen space axes में UV के change rate को मापते हैं
- texture sampler इन्हीं मानों से तय करता है कि कौन-सा mipmap उपयोग करना है
- low UV derivative high-resolution mipmap से, और high derivative low-resolution mipmap से संबंधित होता है
- seam के दोनों ओर मानों में अचानक उछाल आने पर DDX बड़ा हो जाता है, और सबसे निचले mipmap से sampling होने के कारण यह धूसर रेखा जैसा दिख सकता है
- ग्रह में यह polar patch से ढक जाने वाला है, इसलिए बड़ी समस्या नहीं है, लेकिन यदि यह स्पष्ट दिखे तो seam के DDX और DDY को manually ठीक करके sampler को देना चाहिए
- उदाहरण वाला fix सिर्फ समझाने के लिए है; वास्तविक मान mapping के अनुसार सूत्र निकालकर स्वयं निर्धारित करने होंगे
polar patch और pinching correction
- pole distortion को परखी हुई polar patch विधि से ढका जाता है
- ग्रहों या चंद्रमाओं में ध्रुवीय क्षेत्र अक्सर बर्फ से ढके होते हैं और बाकी इलाकों से दृश्य रूप से अलग दिखते हैं, इसलिए अलग texture patch समस्या को छिपाते हुए दृश्य रुचि भी बढ़ाता है
- polar patch को rotation axis के लंबवत plane पर map करके और फिर परिणाम coordinates को rotate करके जोड़ा जा सकता है
float2 PolarPatchMapping(float3 position, float scale, float spin)
{
float cosSpin = cos(spin);
float sinSpin = sin(spin);
float scale = 0.4;
float2 uv = float2(position.x, position.z) * scale;
float2 spinningUV = float2(uv.x * cosSpin - uv.y * sinSpin,
uv.x * sinSpin + uv.y * cosSpin);
return spinningUV;
}
- चूँकि UV map को vertices में store नहीं किया जाता और हर pixel पर generate किया जाता है, इसलिए poles पर pinching correction में अधिक flexibility मिलती है
- एक साधारण division से polar texture के aspect ratio को ठीक किया जा सकता है
uv = float2(uv.x, uv.y/pow(width, 1/3));
- correction का परिणाम किसी दूसरे artifact जैसा दिख सकता है, लेकिन वास्तविक दृश्य में यह polar patch के साथ अधिक smoothly जुड़ता है और दृश्य असंगति कम करता है
atmospheric halo का approximation
- चूँकि डिस्क में ग्रह की सतह पूरे क्षेत्र को नहीं भरती, इसलिए बची हुई जगह में atmospheric halo draw किया जा सकता है
- उसी shader के भीतर halo draw करने से ग्रह के ऊपर का वायुमंडल सतह के साथ स्वाभाविक रूप से मिल जाता है
- physics-based atmospheric rendering आमतौर पर Rayleigh scattering और raymarching का उपयोग करती है, लेकिन यहाँ ऐसा नहीं किया गया है
- इसके बजाय Quake 1·2 की dynamic object lighting trick जैसी approximation का उपयोग किया जाता है
- Quake में static lighting को lightmap के रूप में पहले से calculate किया जाता था
- dynamic monsters नीचे की lightmap को sample करके अपना रंग समायोजित करते थे; यह भौतिक रूप से सटीक नहीं था, लेकिन दृश्य के साथ मेल खाता था
- उसी तरह ग्रह की सतह का उपयोग करके atmospheric halo की lighting का approximation किया जाता है
- bump mapping से पहले के surface normal को बढ़ाकर halo brightness निकाली जाती है
- channel-वार brightness को अलग ढंग से remap करके अलग-अलग प्रकाश तरंगदैर्घ्य के absorption का सरल simulation किया जा सकता है
- वायुमंडल तिरछे कोण पर अधिक दिखता है, इसलिए surface normal के Z component को remap किया जाता है
- halo fade effect के लिए sphere surface से दूरी भी निकाली जाती है
- अंत में सतह और वायुमंडल को alpha blending से मिलाया जाता है
अंतिम परिणाम और उपयोग
- अपेक्षा से अधिक चरणों की ज़रूरत पड़ी, लेकिन अंतिम परिणाम पूर्ण sphere जैसा दिखता है और इच्छित texture तथा shader manipulation को support करता है
- केवल एक Substance Designer graph से surface texture बनाई जा सकती है, और परिणाम पर अतिरिक्त post-processing की ज़रूरत नहीं होती
- animated textures को भी इसी तरीके से संभाला जा सकता है
- छोटे texture size का उपयोग करके performance बचाई जा सकती है
- ज़रूरत के अनुसार tiling adjust की जा सकती है
- परिणाम वाला वीडियो Video 3 में देखा जा सकता है
1 टिप्पणियां
Hacker News टिप्पणियाँ
लगता है लेखक ने cubemap को बहुत जल्दी खारिज कर दिया; मैंने एक personal project में dynamic gas planet render करते समय इसे इस्तेमाल किया था, और यह सबसे सरल समाधान निकला
cubemap इस्तेमाल करने से memory 6 गुना नहीं बढ़ती; यह एक बड़े rectangular texture को 6 छोटे rectangular faces में बाँटने जैसा है, इसलिए कुल texture detail वही रहती है
फायदा यह है कि poles पर squeezing की चिंता नहीं करनी पड़ती, और 3D या 4D noise function से texture animation/distortion के लिए seamless flow field आसानी से बनाया जा सकता है
https://www.junkship.net/News/2016/06/09/jupiter-jazz
हर hemisphere को disk के रूप में project किया जाता है, लेकिन texture को square boundary तक भरा जा सकता है, और हर hemisphere के कुछ हिस्से को दूसरी texture के किनारों पर duplicate किया जा सकता है
disk के center और edge के बीच 1:2 scale difference होता है, इसलिए minimum detail के हिसाब से इसे pixel waste माना जा सकता है; लेकिन यह conformal projection है, इसलिए steep perspective में target pixel color sample करने का तरीका बहुत कम पेचीदा बनता है, और forward/reverse calculations भी हर projection point पर 1 division और कुछ additions/multiplications से हो जाती हैं—cubemap के central projection से भी सस्ती
अगर scale variation और edge pixel waste को और घटाते हुए conceptually बहुत कठिन न होने वाला conformal तरीका चाहिए, तो एक-दूसरे के perpendicular और थोड़ा overlap करने वाले Mercator projections के 2 हिस्से इस्तेमाल करके sphere को baseball की चमड़े की दो पट्टियों की तरह cover किया जा सकता है
हर piece rectangular texture हो सकता है, और NOAA के कुछ papers में Earth weather simulation की differential-equation grid के लिए यह approach प्रस्तावित भी है
मुझे जो सबसे pixel-efficient projection पता है, वह sphere को octahedron में बाँटकर हर octant को “spherical area coordinates” आधारित hexagonal pixel grid से cover करने का तरीका है
हर octant को सामान्य square-pixel image में आधे square, यानी 45-45-90 right triangle के रूप में दिखाया जा सकता है, जिससे https://observablehq.com/@jrus/sac-quincuncial जैसे परिणाम मिलते हैं, और https://observablehq.com/@jrus/sphere-resample जैसी hexagonal grid भी इस्तेमाल की जा सकती है
हालांकि boundary lines के पार sampling करनी हो तो details stereographic के 2 हिस्सों वाले तरीके से कहीं ज्यादा झंझट भरी होती हैं, और seam artifacts भी आ सकते हैं
इसमें ज्यादा math चाहिए, लेकिन अगर आप किसी और वजह से surface को और subdivide करने की योजना नहीं रखते, तो आम तौर पर इसकी उतनी कीमत नहीं बनती
poles पर texture squeezing असल में पूरी surface पर मौजूद distortion का बेहद स्पष्ट रूप है
आम तौर पर यह सिर्फ poles पर साफ दिखता है, लेकिन अगर sphere को triangles में adaptive subdivision से बाँटा जाए, तो subdivision level बदलते समय distortion भी बदलता है और दूसरी जगहों पर भी दिख सकता है
समस्या यह है कि sphere को rectangles में बाँटा जाता है, और हर rectangle UV space में समान area वाले 2 triangles से represent होता है, लेकिन 3D space में एक triangle—खासकर वह जिसका horizontal edge pole के करीब है—छोटा होता है
फिर भी UV triangle के अंदर linearly interpolate होता है, इसलिए texture का आधा हिस्सा सिकुड़ता है और आधा फैलता है
pole पर एक triangle का 3D area सचमुच 0 हो जाता है, इसलिए texture का सिर्फ आधा हिस्सा render होता है, और triangles के बीच seam साफ दिखता है
सही solution vertex-level linear interpolation की जगह pixel shader में हर pixel के लिए UV coordinates calculate करना है, और ठीक से किया जाए तो poles भी seamless handle होते हैं
उदाहरण के लिए, “true north” pole वाले sphere को render करते समय, side से देखने पर true north की तरफ वाला rendering pole इस्तेमाल करें, और true north के करीब से देखने पर 0',0’ की equatorial rendering इस्तेमाल करें
displacement mapping को फिर से देखना चाहूँगा
यह शायद लेखक जिस समस्या को हल करना चाहते हैं उसका alternative नहीं होगा, लेकिन यह ज्यादा सरल और काफी मजेदार है
करीब 25 साल पहले मैंने SoundJam के लिए “Eclipse” नाम का music visualizer बनाया था; input audible frequency range के level array और left/right channels थे
लक्ष्य corona emissions वाले eclipsed sun जैसा look बनाना था, और music data वही “emissions” बनता था
data की frequency तय करती थी कि वह sun disk की circumference पर कहाँ दिखेगा
समय के साथ emissions sun से दूर जाते और “ठंडे” होकर black में fade हो जाते; strong signals white से शुरू होते और कमजोर होने पर yellow, orange, red, brown बनते थे
मुझे sound data values का array एक इतने बड़े circular buffer में रखना पड़ता था कि emissions के black में fade होने तक का समय cover हो जाए
पूरा “Eclipse” और emissions display आखिरकार displacement map ही था, और मैंने एक bitmap precompute किया था जिसमें हर pixel value sound level buffer का offset बनती थी
sun surface के पास वाले pixels के पास newly incoming data offsets होते थे, और बाहर के pixels के पास जल्द expire होने वाली buffer tail के offsets
circular/radial emissions को Cartesian coordinates में map करने की वजह से displacement values generate करने में थोड़ा math लगा
main loop नया sound value लेकर सबसे पुरानी value overwrite करता, फिर displacement map को rows और columns में traverse करके संबंधित sound data fetch करता, उसे fixed palette colors में map करता और display buffer में डालता था
यह दूसरे visualizers जितना flashy नहीं था, लेकिन इसमें एक शांत सुंदरता थी और यह music data को काफी अच्छी तरह reflect करता था
बाद के visualizers की तरह नहीं, जिनमें silence डालने पर भी वे स्थिर नहीं रह पाते थे
लेख अच्छा है, लेकिन नीचे scroll करने पर यह बहुत सारे shaders load करता है, इसलिए बहुत powerful computer न हो तो browser लगभग freeze हो सकता है
यह देखना दिलचस्प है कि लोग “realism” की कितनी अलग-अलग व्याख्या करते हैं
जिन दिनों resources कहीं ज़्यादा सीमित थे, उस दौर के games में यह फर्क खास तौर पर साफ दिखता था; कुछ games सिर्फ चौकोर, pixelated, low-palette graphics से भी आज के बड़े-budget games से ज़्यादा immersion दे देते थे
इसलिए लगता है कि हम उनमें ज़्यादा सक्रिय रूप से डूब जाते थे
इसी तरह के approach पर यह लेख याद आता है: https://bgolus.medium.com/rendering-a-sphere-on-a-quad-13c92...
क्या GPU या 3D library में ऐसा कोई function नहीं होता जो circle की हर horizontal line को scan करते हुए texture के X,Y को sphere पर मौजूद “3D” position से map करे?
sphere तो circle है और circle draw करने के algorithms सरल हैं, लेकिन triangles के लिए लाखों vertices को rotate और project करना resources की भारी बर्बादी जैसा लगता है
horizontally scan करो, एक line नीचे जाओ और draw करते रहो, तो सारी lines draw हो सकती हैं
मुझे याद है कि 80s के आखिर में Second Reality के magnifying lens जैसी चीज़ों को precompute करने के लिए मैंने ऐसा कुछ किया था
पूरा rendering pipeline पहले से ही linear interpolation handle करने के लिए बना है
सिर्फ एक shader program और 1 triangle से भी perfect antialiasing sphere draw किया जा सकता है
अगर GPU पर render कर रहे हैं, तो दिखने वाली complexity के उलट लेख का approach काफी अच्छा लगता है
GPU-based rendering architecture पर बेहतरीन लेखों का एक सेट है: https://fgiesen.wordpress.com/2011/07/09/a-trip-through-the-...
part 6 rasterization पर है
background पर 2D pixel shader imposter planet procedural universe जैसी, बहुत सारे planets वाली work में सच में बहुत मदद करता है
memory और GPU bandwidth की वजह से sphere-cube-based planet को 1 instance तक सीमित रखा जाता है, और पास के celestial bodies को background technique से draw किया जाता है
space में एक distance ऐसा होता है जहाँ दो celestial bodies के बीच लगभग दोनों imposter के रूप में render होते हैं, और उसके बाद सबसे नज़दीकी body को spherical quadtree तरीके से handle किया जाता है
यह perfect नहीं है, लेकिन illusion में कमी निकालना लगभग मुश्किल है
और चूँकि यह plane पर है, इसलिए दूर की galaxies या celestial bodies के लिए camera lens के हल्के physical effects डालना भी आसान हो जाता है
जैसे moon के उगते समय उसका असल से बड़ा दिखना, या दूर की galaxy का gravity से मुड़ा हुआ दिखना
gas planets से जुड़ा यह page पसंद आया: https://emildziewanowski.com/flowfields/
icosphere को कहीं ज़्यादा smoothly unwrap किया जा सकता है, और vertex positions व element sizes भी regular होते हैं
unwrap करना मामूली नहीं है, लेकिन किया जा सकता है