1,00,000 तारे बनाना

नमस्ते! मेरा नाम माइकल चैंग है और मैं Google में डेटा आर्ट्स टीम के साथ काम करता/करती हूं. हाल ही में, हमने 1,00,000 स्टार का काम पूरा किया है. यह एक Chrome एक्सपेरिमेंट है, जिसमें आस-पास के सितारों को दिखाया जाता है. यह प्रोजेक्ट THREE.js और CSS3D के साथ बनाया गया था. इस केस स्टडी में, हम कॉन्टेंट के खोजे जाने की प्रोसेस के बारे में बताएंगे. साथ ही, प्रोग्रामिंग की कुछ तकनीकों के बारे में बताएंगे. साथ ही, आने वाले समय में इसे बेहतर बनाने के लिए कुछ विचार बताएंगे.

यहां चर्चा किए गए विषय काफ़ी बड़े होंगे. इनके बारे में THREE.js की जानकारी होना ज़रूरी है. हालांकि, हमें उम्मीद है कि आप तकनीकी पोस्ट-मॉर्टम के तौर पर अब भी इसका आनंद लेंगे. दाईं ओर मौजूद विषय सूची बटन का इस्तेमाल करके, अपनी पसंद की जगह पर जाएं. सबसे पहले, मैं प्रोजेक्ट का रेंडरिंग वाला हिस्सा दिखाऊँगी. इसके बाद, शेडर मैनेजमेंट और आखिर में WebGL के साथ सीएसएस टेक्स्ट लेबल इस्तेमाल करने का तरीका दिखाऊँगी.

1,00,000 स्टार, डेटा आर्ट्स टीम का Chrome एक्सपेरिमेंट
1,00,000 स्टार, मिल्की वे में आस-पास के तारों को देखने के लिए, THREE.js का इस्तेमाल करते हैं

अंतरिक्ष का पता लगाना

Small आर्म्स ग्लोब तैयार करने के कुछ समय बाद, मैंने फ़ील्ड की गहराई से जुड़े THREE.js पार्टिकल डेमो के साथ प्रयोग किया. मैंने देखा कि लागू किए गए इफ़ेक्ट की मात्रा में बदलाव करके, मैं सीन के इंटरप्रेटेड "स्केल" को बदल सकती हूं. जब फ़ील्ड की गहराई वाकई बहुत ज़्यादा होती थी, तब दूर की चीज़ें वाकई धुंधली हो जाती थीं. उसी तरह, जिस तरह टिल्ट-शिफ़्ट फ़ोटोग्राफ़ी के लिए हमें सूक्ष्म दृश्य देखने का भ्रम होता था. इसके ठीक उलट, इस इफ़ेक्ट को बंद करने से ऐसा लगा जैसे आप किसी गहरे अंतरिक्ष में देख रहे हों.

मैंने ऐसा डेटा ढूंढना शुरू किया जिसकी मदद से पार्टिकल की पोज़िशन इंजेक्ट की जा सके, यह एक पाथ है जो मुझे astronexus.com के HYG डेटाबेस तक ले जाता है. यह पाथ मुझे तीन डेटा सोर्स (हिप्परकोस, येल ब्राइट स्टार कैटलॉग, और ग्लिस/जहरिस कैटलॉग) के साथ-साथ xyz कार्टेज़ियन निर्देशांक के पहले से कैलकुलेशन में इस्तेमाल करता है. आइए शुरू करें!

तारा डेटा प्लॉट करना.
पहला कदम है, कैटलॉग के हर स्टार को एक पार्टिकल के तौर पर दिखाना.
नाम वाले स्टार.
कैटलॉग में कुछ स्टार के नाम सही हैं. इन्हें यहां लेबल किया गया है.

स्टार डेटा को 3D स्पेस में रखने वाली किसी चीज़ को हैक करने में करीब एक घंटा लगा. डेटा सेट में ठीक 1,19,617 स्टार होते हैं. इसलिए, हर स्टार को किसी पार्टिकल के साथ दिखाना, आधुनिक जीपीयू के लिए कोई समस्या नहीं है. यहां अलग-अलग 87 तारों की पहचान भी की गई है. इसलिए, मैंने उसी तकनीक का इस्तेमाल करके एक सीएसएस मार्कर ओवरले बनाया है जिसके बारे में स्मॉल आर्म्स ग्लोब में बताया गया है.

इस दौरान मैंने मास इफ़ेक्ट सीरीज़ पूरी की थी. गेम में खिलाड़ी को गैलेक्सी को एक्सप्लोर करने, उसके अलग-अलग ग्रहों को स्कैन करने और उनके पूरी तरह से काल्पनिक और विकीपीडिया लगने वाले इतिहास के बारे में पढ़ने का न्योता दिया जाता है: जैसे कि ग्रह पर कौनसी प्रजातियां पाई गईं, उसका भूवैज्ञानिक इतिहास वगैरह.

तारों के बारे में उपलब्ध वास्तविक डेटा की संपत्ति को जानकर, कोई भी व्यक्ति आकाशगंगा के बारे में वास्तविक जानकारी उसी तरह प्रस्तुत कर सकता है. इस प्रोजेक्ट का अंतिम लक्ष्य इस डेटा को जीवंत करना होगा, दर्शकों को गैलेक्सी ए ला मास इफ़ेक्ट को एक्सप्लोर करने, तारों और उनके वितरण के बारे में जानने, और उम्मीद है कि अंतरिक्ष के बारे में आश्चर्य की भावना पैदा करने की प्रेरणा देना होगा. वाह!

शायद मुझे इस केस-स्टडी के बाकी हिस्से की भूमिका यह कहकर पेश करनी चाहिए कि मैं किसी भी तरह से खगोलविज्ञानी नहीं हूं और यह शौकिया तौर पर की गई रिसर्च का काम है. इसे बाहरी विशेषज्ञों की सलाह से मदद मिलती है. इस प्रोजेक्ट को किसी कलाकार के लिए अंतरिक्ष का मतलब समझाना चाहिए.

गैलेक्सी बनाना

मेरी योजना थी कि व्यवस्थित तरीके से आकाशगंगा का एक मॉडल तैयार किया जाए, जो तारों के डेटा को कॉन्टेक्स्ट के हिसाब से रख सके. साथ ही, उम्मीद है कि इससे मिल्की वे में हमारी जगह का शानदार नज़ारा दिखेगा.

आकाशगंगा का एक शुरुआती प्रोटोटाइप.
मिल्की वे पार्टिकल सिस्टम का शुरुआती प्रोटोटाइप.

मिल्की वे का निर्माण करने के लिए, मैंने 1,00,000 कण पैदा किए और उन्हें आकाशगंगा की बाहों के बनने के तरीके की नकल करके एक घुमावदार आकार में रखा. मुझे स्पाइरल आर्म बनाने की खास बातों की चिंता नहीं थी, क्योंकि यह गणितीय मॉडल के बजाय, प्रतिनिधित्व करने वाला मॉडल होगा. हालाँकि, मैंने घुमावदार आर्म की संख्या को कमोबेश सही और "सही दिशा" में घुमाने की कोशिश की.

मिल्की वे मॉडल के बाद के वर्शन में, मैंने कणों के साथ गैलेक्सी की समतल इमेज के इस्तेमाल के लिए, कणों के इस्तेमाल पर ज़ोर दिया. उम्मीद है कि इससे कणों का एक जैसा दिखने लगेगा. असल में बनी तस्वीर में स्पाइरल गैलेक्सी NGC 1232 की तस्वीर है, जो हमसे करीब 7 करोड़ प्रकाश वर्ष दूर है. इस इमेज को कई रूपों में ऐसा दिखाया गया है, जो मिल्की वे जैसा दिखता है.

आकाशगंगा के पैमाने का पता लगाना.
हर जीएल यूनिट एक प्रकाश वर्ष होती है. इस मामले में, यह गोले 1,10,000 प्रकाश वर्ष चौड़ा है और इसमें पार्टिकल सिस्टम शामिल है.

मैंने शुरुआत में ही एक जीएल यूनिट, मूल रूप से 3D में पिक्सल को एक लाइट ईयर के तौर पर पेश करने का फ़ैसला किया था - एक ऐसा कन्वेंशन जो हर चीज़ के विज़ुअल को एक जगह पर दिखाने में मदद करता था. साथ ही, इससे मुझे बाद में गंभीर सटीक मामले मिल गए.

दूसरा तरीका मैंने यह तय किया कि कैमरे को चलाने के बजाय पूरे सीन को घुमाया जाए. ऐसा मैंने कुछ और प्रोजेक्ट में किया है. इसका एक फ़ायदा यह है कि हर चीज़ एक "टर्नटेबल" पर रखी जाती है, ताकि माउस-ड्रैग करने से वह ऑब्जेक्ट घूम जाए जिसके बारे में बात की जा रही है. हालांकि, ज़ूम इन करना सिर्फ़ Cam.position.z बदलने की ज़रूरत है.

कैमरे के लिए फ़ील्ड ऑफ़ व्यू (या एफ़ओवी) भी डाइनैमिक होता है. जैसे-जैसे एक व्यक्ति बाहर निकलता है, दृश्य का क्षेत्र चौड़ा हो जाता है, और यह आकाशगंगा का अधिक से ज़्यादा हिस्सा घेरने लगता है. सितारे की ओर जाने पर इसका विपरीत सत्य होता है, दृश्य का क्षेत्र संकरा हो जाता है. इससे कैमरा उन चीज़ों को देख सकता है जो अपरिमित (आकाशगंगा की तुलना में) हैं. इसमें FOV को भगवान के जैसे मैग्नीफ़ाइंग ग्लास से नीचे की ओर धकेल दिया जाता है. साथ ही, कैमरे को प्लेन के कटाई की समस्याओं से जूझने की ज़रूरत नहीं पड़ती.

आकाशगंगा को रेंडर करने के अलग-अलग तरीके.
(ऊपर) शुरुआती पार्टिकल गैलेक्सी. (नीचे) छोटे-छोटे पार्टिकल के साथ इमेज प्लेन.

यहां से मैं सूर्य को आकाशगंगा के केंद्र से कुछ इकाई दूर "रख" पाने में सफल रहा. इसके बजाय, मैंने काइपर क्लिफ़ के दायरे को मैप करके, सौर मंडल के साइज़ को विज़ुअलाइज़ भी किया. आखिर में, मैंने ओर्ट क्लाउड को विज़ुअलाइज़ करने का विकल्प चुना. इस मॉडल में, मैं सौर मंडल के इस मॉडल में, पृथ्वी की एक सरल कक्षा और तुलना के हिसाब से सूर्य की असल त्रिज्या को भी देख सकता था.

सौर मंडल.
सूरज, ग्रहों से घिरा है और एक गोला है, जो काइपर बेल्ट को दर्शाता है.

सूरज को रेंडर करना मुश्किल था. मुझे पता था कि रीयल-टाइम ग्राफ़िक्स की तकनीक की मदद से मुझे धोखाधड़ी करनी पड़ी. सूर्य की सतह पर प्लाज़्मा का गर्म झाग पैदा होता है और यह समय के साथ पल्स और बदलने के लिए ज़रूरी है. इसे बिटमैप टेक्सचर की मदद से, सोलर सरफ़ेस की इन्फ़्रारेड इमेज की तरह सिम्युलेट किया गया था. सरफ़ेस शेडर इस टेक्सचर के ग्रेस्केल के आधार पर रंग का लुक-अप बनाता है और एक अलग कलर रैंप में लुक-अप करता है. जब इस लुक-अप में समय के साथ बदलाव किया जाता है, तो यह लावा जैसा डिस्टॉर्शन पैदा करता है.

इसी तरह की तकनीक का इस्तेमाल सूर्य के कोरोना के लिए किया गया था. हालांकि, यह एक फ़्लैट स्प्राइट कार्ड होगा, जिसमें https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js का इस्तेमाल करके हमेशा कैमरे की तरफ़ होता है.

रेंडर हो रहा है सोल.
सूरज का शुरुआती वर्शन.

सोलर फ़्लेयर बनाने के लिए, वर्टेक्स और फ़्रैगमेंट शेडर का इस्तेमाल किया जाता है. ये टॉरस पर लगाए जाते हैं और ये सोलर की सतह के किनारे के चारों ओर घूमते हैं. वर्टेक्स शेडर में नॉइज़ फ़ंक्शन है, जिसकी वजह से यह ब्लॉब की तरह बुन जाता है.

यहां पर मुझे जीएल सटीक होने की वजह से, z-फ़ाइटिंग में कुछ समस्याएं आने लगी थीं. सटीक जानकारी के सभी वैरिएबल THREE.js में पहले से तय थे. इसलिए, ज़्यादा मेहनत किए बिना मुझे असल में सटीक जानकारी नहीं मिल सकी. सटीक समस्याएं उतनी ज़्यादा खराब नहीं थीं जितनी ऑरिजिन के आस-पास थीं. हालांकि, जब मैंने दूसरे स्टार सिस्टम को मॉडल करना शुरू किया, तब यह समस्या आने लगी.

स्टार मॉडल.
सूरज को रेंडर करने के कोड को बाद में, दूसरे तारों को रेंडर करने के लिए सामान्य बनाया गया.

z-फ़ाइटिंग को कम करने के लिए मैंने कुछ हैक का इस्तेमाल किया. THREE की Material.polygonoffset एक ऐसी प्रॉपर्टी है जो पॉलीगॉन को किसी दूसरी जगह पर रेंडर करने की अनुमति देती है (जहां तक मुझे पता है). इसका इस्तेमाल, कोरोना प्लेन को हर समय सूर्य की सतह पर रेंडर होने के लिए किया जाता था. इसके नीचे, तेज़ रोशनी की किरणों को गोले से दूर ले जाने के लिए सूर्य "प्रक्षेपण" बनाया गया था.

सटीक होने से जुड़ी एक दूसरी समस्या यह थी कि सीन ज़ूम इन होने पर स्टार मॉडल हिलने लगेंगे. इसे ठीक करने के लिए, मुझे सीन के रोटेशन को "ज़ीरो आउट" करना पड़ा था. साथ ही, मुझे स्टार मॉडल और पर्यावरण के मैप को अलग से घुमाना पड़ा, ताकि यह भ्रम पैदा हो सके कि आप स्टार की कक्षा में घूम रहे हैं.

Lensflare बनाया जा रहा है

बड़ी ताकत के साथ ज़िम्मेदारी भी बढ़ जाती है.
ज़्यादा ताकत के साथ ज़िम्मेदारी भी बढ़ जाती है.

स्पेस विज़ुअलाइज़ेशन, लेंसफ़्लेयर का बहुत ज़्यादा इस्तेमाल करने पर, मुझे लगता है कि उससे आराम मिल सकता है. THREE.LensFlare इस मकसद को पूरा करता था, मुझे बस एनामॉर्फ़िक हेक्सागॉन और जेजे एब्रम के एक डैश का इस्तेमाल करना था. नीचे दिया गया स्निपेट दिखाता है कि उन्हें आपके सीन में कैसे बनाया जाए.

// This function returns a lesnflare THREE object to be .add()ed to the scene graph
function addLensFlare(x,y,z, size, overrideImage){
var flareColor = new THREE.Color( 0xffffff );

lensFlare = new THREE.LensFlare( overrideImage, 700, 0.0, THREE.AdditiveBlending, flareColor );

// we're going to be using multiple sub-lens-flare artifacts, each with a different size
lensFlare.add( textureFlare1, 4096, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );
lensFlare.add( textureFlare2, 512, 0.0, THREE.AdditiveBlending );

// and run each through a function below
lensFlare.customUpdateCallback = lensFlareUpdateCallback;

lensFlare.position = new THREE.Vector3(x,y,z);
lensFlare.size = size ? size : 16000 ;
return lensFlare;
}

// this function will operate over each lensflare artifact, moving them around the screen
function lensFlareUpdateCallback( object ) {
var f, fl = this.lensFlares.length;
var flare;
var vecX = -this.positionScreen.x _ 2;
var vecY = -this.positionScreen.y _ 2;
var size = object.size ? object.size : 16000;

var camDistance = camera.position.length();

for( f = 0; f < fl; f ++ ) {
flare = this.lensFlares[ f ];

flare.x = this.positionScreen.x + vecX * flare.distance;
flare.y = this.positionScreen.y + vecY * flare.distance;

flare.scale = size / camDistance;
flare.rotation = 0;

}
}

टेक्स्चर स्क्रोल करने का आसान तरीका

यह गेम दुनिया भर के लोगों से प्रेरित है.
अंतरिक्ष में स्पेशल ओरिएंटेशन (स्क्रीन की दिशा) में मदद करने वाला कार्टिज़न प्लेन.

"स्पेशल ओरिएंटेशन प्लेन" के लिए, एक बहुत बड़ा THREE.CylinderGeometry() बनाया गया. साथ ही, इसे सूर्य पर रखा गया. बाहर की ओर लहराते हुए "लाइट की लहर" बनाने के लिए, मैंने समय के साथ इसकी टेक्स्चर ऑफ़सेट में इस तरह से बदलाव किया है:

mesh.material.map.needsUpdate = true;
mesh.material.map.onUpdate = function(){
this.offset.y -= 0.001;
this.needsUpdate = true;
}

map सामग्री से संबंधित बनावट है, जिसे एक onUpdate फ़ंक्शन मिलता है जिसे आप ओवरराइट कर सकते हैं. ऑफ़सेट सेट करने से, टेक्सचर को उस ऐक्सिस पर "स्क्रोल किया" जा सकता है और स्पैम करने के लिए needUpdate = true इस व्यवहार को लूप में चलने के लिए मजबूर करेगा.

कलर रैंप का इस्तेमाल करना

हर तारे का रंग अलग होता है, जो खगोलविज्ञानियों ने उसके "कलर-इंडेक्स" के आधार पर तय किया है. आम तौर पर, लाल तारे ठंडे और नीले/बैंगनी रंग के तारे गर्म होते हैं. इस ग्रेडिएंट में सफ़ेद और बीच के नारंगी रंगों का एक बैंड मौजूद है.

तारों को रेंडर करते समय, मैं इस डेटा के आधार पर हर कण को उसे एक अलग रंग देना चाहता था. ऐसा करने का तरीका, कणों पर लगाए गए शेडर मटीरियल को दिए गए "एट्रिब्यूट" के ज़रिए किया गया.

var shaderMaterial = new THREE.ShaderMaterial( {
uniforms: datastarUniforms,
attributes: datastarAttributes,
/_ ... etc _/
});
var datastarAttributes = {
size: { type: 'f', value: [] },
colorIndex: { type: 'f', value: [] },
};

colorIndex के कलेक्शन को भरने से, शेडर में हर पार्टिकल को एक खास रंग मिलेगा. आम तौर पर, एक कलर vec3 में पास होगा, लेकिन इस मामले में मैं आखिर में कलर रैंप लुक-अप के लिए फ़्लोट में पास कर रही हूं.

कलर रैंप.
किसी स्टार के कलर इंडेक्स से, दिखने वाले रंग को खोजने के लिए इस्तेमाल किया जाने वाला कलर रैंप.

कलर रैंप ऐसा दिखता था, हालांकि मुझे इसके बिटमैप कलर डेटा को JavaScript से ऐक्सेस करना था. मैंने ऐसा किया, पहले इमेज को DOM पर लोड करना, उसे कैनवस एलिमेंट में बनाना, फिर कैनवस बिटमैप को ऐक्सेस करना.

// make a blank canvas, sized to the image, in this case gradientImage is a dom image element
gradientCanvas = document.createElement('canvas');
gradientCanvas.width = gradientImage.width;
gradientCanvas.height = gradientImage.height;

// draw the image
gradientCanvas.getContext('2d').drawImage( gradientImage, 0, 0, gradientImage.width, gradientImage.height );

// a function to grab the pixel color based on a normalized percentage value
gradientCanvas.getColor = function( percentage ){
return this.getContext('2d').getImageData(percentage \* gradientImage.width,0, 1, 1).data;
}

इसी विधि का उपयोग फिर स्टार मॉडल दृश्य में अलग-अलग तारों को रंग देने के लिए किया जाता है.

मेरी आंखें!
इसी तकनीक का इस्तेमाल, किसी स्टार की स्पेक्ट्रल क्लास के लिए कलर लुकअप के लिए किया जाता है.

शेडर को हिलाना

पूरे प्रोजेक्ट के दौरान मुझे पता चला कि सभी विज़ुअल इफ़ेक्ट को पूरा करने के लिए, मुझे ज़्यादा से ज़्यादा शेडर लिखने की ज़रूरत थी. मैंने इस काम के लिए कस्टम शेडर लोडर लिखा क्योंकि मैं index.html में शेडर को लाइव रखकर थका हुआ था.

// list of shaders we'll load
var shaderList = ['shaders/starsurface', 'shaders/starhalo', 'shaders/starflare', 'shaders/galacticstars', /*...etc...*/];

// a small util to pre-fetch all shaders and put them in a data structure (replacing the list above)
function loadShaders( list, callback ){
var shaders = {};

var expectedFiles = list.length \* 2;
var loadedFiles = 0;

function makeCallback( name, type ){
return function(data){
if( shaders[name] === undefined ){
shaders[name] = {};
}

    shaders[name][type] = data;

    //  check if done
    loadedFiles++;
    if( loadedFiles == expectedFiles ){
    callback( shaders );
    }

};

}

for( var i=0; i<list.length; i++ ){
var vertexShaderFile = list[i] + '.vsh';
var fragmentShaderFile = list[i] + '.fsh';

//  find the filename, use it as the identifier
var splitted = list[i].split('/');
var shaderName = splitted[splitted.length-1];
$(document).load( vertexShaderFile, makeCallback(shaderName, 'vertex') );
$(document).load( fragmentShaderFile,  makeCallback(shaderName, 'fragment') );

}
}

loadShaders() फ़ंक्शन शेडर फ़ाइल के नामों की सूची लेता है (फ़्रैगमेंट के लिए .fsh और वर्टेक्स शेडर के लिए .vsh की उम्मीद होती है), उनके डेटा को लोड करने की कोशिश करता है, और फिर सूची को ऑब्जेक्ट से बदल देता है. आखिरी नतीजा आपकी THREE.js यूनिफ़ॉर्म में होगा, जिसे आप इस तरह से शेडर पास कर सकते हैं:

var galacticShaderMaterial = new THREE.ShaderMaterial( {
vertexShader: shaderList.galacticstars.vertex,
fragmentShader: shaderList.galacticstars.fragment,
/_..._/
});

हालांकि, मुझे ज़रूरत हो.हालांकि, इस काम के लिए कोड को फिर से असेंबल करने की ज़रूरत पड़ती थी. हालांकि, यह समाधान बहुत आसान है, लेकिन इसे THREE.js एक्सटेंशन के तौर पर भी बेहतर बनाया जा सकता है. अगर इसे बेहतर बनाने के लिए आपके पास कोई सुझाव या तरीका है, तो कृपया मुझे बताएं!

THREE.js के ऊपर सीएसएस टेक्स्ट लेबल

हमारे पिछले प्रोजेक्ट, Small Arms Globe में, मैंने THREE.js के सीन के ऊपर टेक्स्ट लेबल दिखाने का काम किया था. मैंने जिस तरीके का इस्तेमाल किया वह उस जगह के ऐब्सलूट मॉडल पोज़िशन को कैलकुलेट करता है जहां मुझे टेक्स्ट दिखाना है. इसके बाद, यह THREE.Projector() का इस्तेमाल करके स्क्रीन की स्थिति को ठीक करता है. इसके बाद, सीएसएस एलिमेंट को अपनी पसंद की जगह पर रखने के लिए, CSS "top" और "left" का इस्तेमाल करता है.

इस प्रोजेक्ट पर शुरुआत में बार-बार इसी तकनीक का इस्तेमाल किया जाता था. हालांकि, मुझे लुइस क्रूज़ के बताए गए इस दूसरे तरीके को आज़माने में काफ़ी परेशानी हो रही है.

बुनियादी आइडिया: CSS3D के मैट्रिक्स को THREE के कैमरा और सीन में बदलने से मैच करें. साथ ही, आपके पास 3D में सीएसएस एलिमेंट को इस तरह से "प्लेस" करने का विकल्प है जैसे वह तीन के सीन के ऊपर हो. हालांकि, इसकी कुछ सीमाएं हैं. उदाहरण के लिए, THREE.js ऑब्जेक्ट के नीचे टेक्स्ट नहीं डाला जा सकता. यह तरीका "top" और "left" सीएसएस एट्रिब्यूट का इस्तेमाल करके, लेआउट का इस्तेमाल करने की तुलना में अब भी ज़्यादा तेज़ है.

टेक्स्ट लेबल.
CSS3D का इस्तेमाल करने से टेक्स्ट लेबल, WebGL के सबसे ऊपर दिखते हैं.

इसके लिए डेमो (और व्यू सोर्स में मौजूद कोड) को यहां देखा जा सकता है. हालांकि, मुझे पता चला कि उसके बाद से THREE.js के लिए मैट्रिक्स ऑर्डर बदल गया है. मैंने जिस फ़ंक्शन को अपडेट किया है:

/_ Fixes the difference between WebGL coordinates to CSS coordinates _/
function toCSSMatrix(threeMat4, b) {
var a = threeMat4, f;
if (b) {
f = [
a.elements[0], -a.elements[1], a.elements[2], a.elements[3],
a.elements[4], -a.elements[5], a.elements[6], a.elements[7],
a.elements[8], -a.elements[9], a.elements[10], a.elements[11],
a.elements[12], -a.elements[13], a.elements[14], a.elements[15]
];
} else {
f = [
a.elements[0], a.elements[1], a.elements[2], a.elements[3],
a.elements[4], a.elements[5], a.elements[6], a.elements[7],
a.elements[8], a.elements[9], a.elements[10], a.elements[11],
a.elements[12], a.elements[13], a.elements[14], a.elements[15]
];
}
for (var e in f) {
f[e] = epsilon(f[e]);
}
return "matrix3d(" + f.join(",") + ")";
}

टेक्स्ट पूरी तरह बदल गया है, इसलिए अब वह कैमरे के सामने नहीं है. इसका समाधान THREE.Gyroscope() का इस्तेमाल करने पर था. यह Object3D को सीन से इनहेरिट किए गए ओरिएंटेशन को "कम" करने के लिए मजबूर करता था. इस तकनीक को "बिलबोर्डिंग" कहा जाता है और इसके लिए जाइरोस्कोप सबसे सही है.

अच्छी बात यह है कि इसमें अब भी सभी सामान्य डीओएम और सीएसएस चलते हैं. जैसे, किसी 3D टेक्स्ट लेबल पर माउस ले जाना और ड्रॉप शैडो के साथ उसे चमकाना.

टेक्स्ट लेबल.
टेक्स्ट लेबल, कैमरे को THREE.Gyroscope() से अटैच करने पर हमेशा स्क्रीन पर दिखते हैं.

ज़ूम इन करने पर मुझे पता चला कि टाइपोग्राफ़ी में बदलाव की वजह से, पोज़िशन तय करने में समस्या आ रही है. क्या ऐसा टेक्स्ट की करिंग और पैडिंग की वजह से हो सकता है? एक और समस्या यह थी कि ज़ूम इन करने पर टेक्स्ट पिक्सलेट हो जाता है, क्योंकि DOM रेंडरर रेंडर किए गए टेक्स्ट को टेक्सचर्ड क्वाड की तरह इस्तेमाल करता है. इस तरीके का इस्तेमाल करते समय आपको इसका ध्यान रखना चाहिए. कुल मिलाकर देखें, तो मैं एक बड़े फ़ॉन्ट साइज़ का टेक्स्ट इस्तेमाल कर सकता था. शायद यह आने वाले समय में एक्सप्लोर करने के लिए सही है. इस प्रोजेक्ट में मैंने, सौर मंडल में मौजूद ग्रहों के साथ मौजूद छोटे एलिमेंट के लिए, ऊपर/बाईं ओर मौजूद सीएसएस प्लेसमेंट टेक्स्ट लेबल का भी इस्तेमाल किया. इनके बारे में ऊपर बताया गया है.

संगीत चलाना और लूप करना

मैस इफ़ेक्ट के 'गैलैक्टिक मैप' के दौरान, बायोवेयर कंपोज़र सैम हलिक और जैक वॉल ने जो संगीत बजाया था, उसमें कुछ ऐसा ही भाव है जिसे मैं दर्शकों को महसूस कराना चाहता था. हमें अपने प्रोजेक्ट में कुछ संगीत शामिल करना था, क्योंकि हमें लगा कि संगीत उस माहौल का एक अहम हिस्सा है. इससे हमें हैरान होने और हैरान करने की ऐसी भावना पैदा करने में मदद मिली जिसे हम टारगेट करने की कोशिश कर रहे थे.

हमारे निर्माता वाल्डेन क्लंप ने सैम से संपर्क किया, जिन्होंने Mass Impact से "कटिंग फ़्लोर" संगीत सुना था. उन्होंने बड़ी ही विनम्रता के साथ हमें इस्तेमाल करने दिया. इस ट्रैक का टाइटल "इन अ स्ट्रेंज लैंड" है.

मैंने संगीत प्लेबैक के लिए ऑडियो टैग का इस्तेमाल किया था, हालांकि Chrome में भी "लूप" एट्रिब्यूट भरोसेमंद नहीं था -- कभी-कभी यह लूप में काम नहीं करता. आखिर में, ड्यूअल ऑडियो टैग के हैक का इस्तेमाल यह पता लगाने के लिए किया गया कि वीडियो कब खत्म हो रहा है और अन्य टैग पर चलने के लिए उस पर क्या चल रहा है. निराशाजनक बात यह थी कि यह अब भी पूरी तरह से लूप नहीं कर रहा था, लेकिन मुझे लगता है कि यह मेरे लिए सबसे अच्छा था.

var musicA = document.getElementById('bgmusicA');
var musicB = document.getElementById('bgmusicB');
musicA.addEventListener('ended', function(){
this.currentTime = 0;
this.pause();
var playB = function(){
musicB.play();
}
// make it wait 15 seconds before playing again
setTimeout( playB, 15000 );
}, false);

musicB.addEventListener('ended', function(){
this.currentTime = 0;
this.pause();
var playA = function(){
musicA.play();
}
// otherwise the music will drive you insane
setTimeout( playA, 15000 );
}, false);

// okay so there's a bit of code redundancy, I admit it
musicA.play();

सुधार की गुंजाइश

कुछ समय तक THREE.js के साथ काम करने के बाद, मुझे लगता है कि मैं उस बिंदु पर पहुंच गया/गई हूं जहां मेरे कोड के साथ मेरा डेटा बहुत ज़्यादा मिल रहा था. उदाहरण के लिए, सामग्री, बनावट, और ज्यामिति के निर्देशों को इन-लाइन परिभाषित करते समय, मेरे लिए "कोड के साथ 3D मॉडलिंग" होना ज़रूरी था. यह बहुत बुरा लगा. आने वाले समय में, THREE.js का इस्तेमाल करने पर किए जाने वाले काम काफ़ी बेहतर हो सकते हैं. उदाहरण के लिए, मटीरियल डेटा को किसी अलग फ़ाइल में डालना, कुछ मामलों में देखने लायक और छोटा किया जा सकने वाला डेटा शामिल करना, और मुख्य प्रोजेक्ट में वापस लाना.

हमारे सहयोगी रे मैकक्लूर ने भी कुछ ज़बरदस्त जनरेटिव "स्पेस नॉइज़" बनाया है. इसे वेब ऑडियो एपीआई के अस्थिर होने, Chrome के बार-बार क्रैश होने, और बंद होने की वजह से काटना पड़ता था. यह दुख की बात है... लेकिन इसने हमें भविष्य में काम करने के लिए सही जगह में और सोचने को मजबूर कर दिया. इस लेख को लिखते हुए मुझे जानकारी मिली है कि Web Audio API को पैच कर दिया गया है. इसलिए, हो सकता है कि यह अभी काम कर रहा हो और आने वाले समय में इस पर ध्यान देने की ज़रूरत है.

WebGL के साथ जोड़े गए टाइपोलॉजिकल एलिमेंट अब भी एक चुनौती हैं और मैं पूरी तरह से पक्का नहीं हूं कि हम यहां जो कर रहे हैं वह सही है. यह अब भी एक हैक की तरह लगता है. THREE के आने वाले समय में लॉन्च होने वाले और आने वाले सीएसएस रेंडरर के आने वाले वर्शन, दोनों का इस्तेमाल बेहतर तरीके से करने के लिए किए जा सकते हैं.

क्रेडिट देखें

इस प्रोजेक्ट के लिए मुझे शहर जाने की अनुमति देने के लिए, ऐरन कोबलिन का धन्यवाद. बेहतरीन यूज़र इंटरफ़ेस (यूआई) डिज़ाइन और लागू करने के तरीके, टाइप ट्रीटमेंट, और टूर को लागू करने के लिए जोनो ब्रैंडेल. प्रोजेक्ट को नाम देने और इसकी पूरी कॉपी देने के लिए वैल्डियन क्लंप. साबाह अहमद को डेटा और इमेज सोर्स के इस्तेमाल के ज़्यादा अधिकारों को हटाने के लिए धन्यवाद. क्लेम राइट का इस्तेमाल किया. इससे वे पब्लिकेशन के लिए सही लोगों से संपर्क कर पाए. तकनीकी महारत के लिए डग फ़्रित्स. जॉर्ज ब्रॉवर ने मुझे JS और सीएसएस सिखाने के लिए. और हां, THREE.js के लिए मिस्टर डूब.

रेफ़रंस