नमस्कार! मेरा नाम माइकल चांग है और मैं Google की डेटा आर्ट्स टीम के साथ काम करता हूं. हाल ही में, हमने 1,00, 000 Stars पूरा किया है. यह आस-पास के तारों को दिखाने वाला Chrome Experiment है. इस प्रोजेक्ट को THREE.js और CSS3D की मदद से बनाया गया था. इस केस स्टडी में, मैं खोज की प्रोसेस के बारे में बताऊंगी. साथ ही, प्रोग्रामिंग की कुछ तकनीकें शेयर करूंगी. इसके अलावा, आने वाले समय में सुधार करने के लिए कुछ सुझाव भी दूंगी.
यहां जिन विषयों पर चर्चा की जाएगी वे काफ़ी बड़े होंगे. साथ ही, इनके लिए THREE.js के बारे में कुछ जानकारी होना ज़रूरी है. हालांकि, हमें उम्मीद है कि आप इसे तकनीकी पोस्ट-मॉर्टम के तौर पर अब भी पसंद करेंगे. दाईं ओर मौजूद, 'विषय सूची' बटन का इस्तेमाल करके, अपनी पसंद के सेक्शन पर जाएं. सबसे पहले, मैं प्रोजेक्ट का रेंडरिंग वाला हिस्सा दिखाऊंगा. इसके बाद, शेडर मैनेजमेंट और आखिर में, WebGL के साथ सीएसएस टेक्स्ट लेबल का इस्तेमाल करने का तरीका बताऊंगा.

स्पेस के बारे में जानकारी
स्मॉल आर्म्स ग्लोब को पूरा करने के कुछ समय बाद, मैं डेप्थ ऑफ़ फ़ील्ड के साथ THREE.js पार्टिकल डेमो आज़मा रहा था. मैंने देखा कि इफ़ेक्ट की मात्रा को अडजस्ट करके, सीन के "स्केल" को बदला जा सकता है. जब डेप्थ ऑफ़ फ़ील्ड इफ़ेक्ट बहुत ज़्यादा होता था, तो दूर की चीज़ें बहुत धुंधली हो जाती थीं. यह कुछ हद तक टिल्ट-शिफ़्ट फ़ोटोग्राफ़ी की तरह काम करता था, जिसमें किसी छोटी चीज़ को बड़ा करके दिखाया जाता है. इसके उलट, इफ़ेक्ट को कम करने पर ऐसा लग रहा था कि आप गहरे अंतरिक्ष में देख रहे हैं.
मैंने ऐसा डेटा ढूंढना शुरू किया जिसका इस्तेमाल करके, पार्टिकल की पोज़िशन को इंजेक्ट किया जा सके. इस प्रोसेस में, मुझे astronexus.com का HYG डेटाबेस मिला. इसमें तीन डेटा सोर्स (हिप्पार्कस, येल ब्राइट स्टार कैटलॉग, और ग्लिसे/जारेइस कैटलॉग) का डेटा शामिल है. साथ ही, इसमें पहले से कैलकुलेट किए गए xyz कार्टेशियन कोऑर्डिनेट भी शामिल हैं. आइए, शुरू करें!


स्टार डेटा को 3D स्पेस में रखने के लिए, एक घंटे में एक साथ कई प्रोग्राम बनाए गए. डेटा सेट में कुल 1,19,617 तारे हैं. इसलिए, हर तारे को एक पार्टिकल के तौर पर दिखाने में, मॉडर्न जीपीयू को कोई समस्या नहीं होती. इसके अलावा, 87 तारों की पहचान अलग-अलग तौर पर की गई है. इसलिए, मैंने सीएसएस मार्कर ओवरले बनाया. इसके लिए, मैंने वही तकनीक इस्तेमाल की जिसके बारे में मैंने Small Arms Globe में बताया था.
इस दौरान, मैंने Mass Effect सीरीज़ पूरी की थी. इस गेम में, खिलाड़ी को गैलेक्सी एक्सप्लोर करने का न्योता दिया जाता है. साथ ही, उसे अलग-अलग ग्रहों को स्कैन करने और उनके बारे में पढ़ने का मौका मिलता है. इन ग्रहों के बारे में दी गई जानकारी पूरी तरह से काल्पनिक होती है और यह विकिपीडिया पर मौजूद जानकारी से मिलती-जुलती होती है. जैसे, ग्रह पर कौनसी प्रजातियां फली-फूलीं, इसका भूवैज्ञानिक इतिहास क्या है वगैरह.
तारों के बारे में मौजूद असल डेटा को ध्यान में रखते हुए, कोई व्यक्ति उसी तरह से आकाशगंगा के बारे में असल जानकारी दे सकता है. इस प्रोजेक्ट का मुख्य मकसद, इस डेटा को लोगों तक पहुंचाना है. साथ ही, दर्शकों को Mass Effect की तरह गैलेक्सी को एक्सप्लोर करने की अनुमति देना है, ताकि वे तारों और उनके डिस्ट्रिब्यूशन के बारे में जान सकें. साथ ही, उम्मीद है कि इससे लोगों को अंतरिक्ष के बारे में जानने की प्रेरणा मिलेगी. वाह!
इस केस स्टडी के बारे में आगे बताने से पहले, हम आपको यह बता दें कि हम खगोलशास्त्री नहीं हैं. यह काम, शौकिया तौर पर की गई रिसर्च का नतीजा है. इसमें बाहरी विशेषज्ञों से मिली सलाह भी शामिल है. इस प्रोजेक्ट को, कलाकार के नज़रिए से स्पेस की व्याख्या के तौर पर देखा जाना चाहिए.
गैलेक्सी बनाना
मेरा प्लान, गैलेक्सी का एक ऐसा मॉडल जनरेट करना था जो स्टार के डेटा को कॉन्टेक्स्ट में रख सके. साथ ही, उम्मीद है कि इससे हमें मिल्की वे में अपनी जगह का शानदार व्यू मिल सके.

मिल्की वे गैलेक्सी बनाने के लिए, मैंने 1,00,000 पार्टिकल बनाए और उन्हें स्पाइरल में रखा. मैंने ऐसा गैलेक्सी की भुजाएं बनने के तरीके को ध्यान में रखकर किया. मुझे स्पाइरल आर्म बनने की खास बातों के बारे में ज़्यादा चिंता नहीं थी, क्योंकि यह गणितीय मॉडल के बजाय एक मॉडल होगा. हालांकि, मैंने स्पाइरल आर्म की संख्या को कम या ज़्यादा करके सही करने की कोशिश की है. साथ ही, उन्हें "सही दिशा" में घुमाने की कोशिश की है.
मिल्की वे मॉडल के बाद के वर्शन में, मैंने पार्टिकल के इस्तेमाल को कम कर दिया. इसके बजाय, मैंने पार्टिकल के साथ गैलेक्सी की प्लैनर इमेज का इस्तेमाल किया, ताकि यह ज़्यादा फ़ोटोग्राफ़िक दिखे. असल इमेज, स्पाइरल गैलेक्सी NGC 1232 की है. यह हमसे करीब 7 करोड़ प्रकाश वर्ष दूर है. इमेज में बदलाव करके इसे मिल्की वे जैसा दिखाया गया है.

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

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

सूरज को रेंडर करना मुश्किल था. मुझे रीयल-टाइम ग्राफ़िक्स की उन सभी तकनीकों का इस्तेमाल करना पड़ा जिनके बारे में मुझे पता था. सूर्य की सतह, प्लाज़्मा का गर्म झाग है. इसमें समय-समय पर बदलाव होता रहता है. इसे सौर सतह की इन्फ़्रारेड इमेज के बिटमैप टेक्सचर के ज़रिए सिम्युलेट किया गया था. सरफ़ेस शेडर, इस टेक्सचर के ग्रेस्केल के आधार पर रंग का पता लगाता है. साथ ही, अलग-अलग कलर रैंप में रंग का पता लगाता है. समय के साथ इस लुक-अप में बदलाव होने पर, यह लावा जैसा डिस्टॉर्शन पैदा करता है.
सूर्य के कोरोना के लिए भी इसी तकनीक का इस्तेमाल किया गया था. हालांकि, यह एक फ़्लैट स्प्राइट कार्ड होगा, जो https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js का इस्तेमाल करके, हमेशा कैमरे की ओर दिखता है.

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

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

स्पेस विज़ुअलाइज़ेशन में, मुझे लगता है कि लेंसफ़्लेयर का ज़्यादा इस्तेमाल किया जा सकता है. 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 फ़ंक्शन होता है, जिसे ओवरराइट किया जा सकता है. इसके ऑफ़सेट को सेट करने से, टेक्सचर उस ऐक्सिस के साथ "स्क्रोल" हो जाता है. साथ ही, spamming needsUpdate = 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,
/_..._/
});
मैं शायद require.js का इस्तेमाल कर सकता था. हालांकि, इसके लिए मुझे कुछ कोड को फिर से असेंबल करना पड़ता. यह समाधान, काफ़ी आसान है. हालांकि, मुझे लगता है कि इसे बेहतर बनाया जा सकता है. शायद इसे THREE.js एक्सटेंशन के तौर पर भी इस्तेमाल किया जा सकता है. अगर आपके पास इसे बेहतर बनाने के लिए कोई सुझाव या तरीका है, तो कृपया हमें बताएं!
THREE.js के ऊपर सीएसएस टेक्स्ट लेबल
हमारे पिछले प्रोजेक्ट, स्मॉल आर्म्स ग्लोब में, मैंने THREE.js सीन के ऊपर टेक्स्ट लेबल दिखाने की कोशिश की थी. मैं जिस तरीके का इस्तेमाल कर रहा था वह टेक्स्ट को दिखाने के लिए, मॉडल की सटीक पोज़िशन का हिसाब लगाता है. इसके बाद, THREE.Projector() का इस्तेमाल करके स्क्रीन की पोज़िशन तय करता है. आखिर में, सीएसएस "टॉप" और "लेफ़्ट" का इस्तेमाल करके, सीएसएस एलिमेंट को मनचाही पोज़िशन पर रखता है.
इस प्रोजेक्ट के शुरुआती वर्शन में, इसी तकनीक का इस्तेमाल किया गया था. हालांकि, मुझे लुइस क्रूज़ के बताए गए इस दूसरे तरीके को आज़माना है.
बुनियादी विचार: CSS3D के मैट्रिक्स ट्रांसफ़ॉर्म को THREE के कैमरा और सीन से मैच करें. इसके बाद, CSS एलिमेंट को 3D में "प्लेस" किया जा सकता है. ऐसा लगेगा कि वे THREE के सीन के ऊपर हैं. हालांकि, इसकी कुछ सीमाएं हैं. उदाहरण के लिए, आपके पास किसी THREE.js ऑब्जेक्ट के नीचे टेक्स्ट रखने का विकल्प नहीं होगा. यह अब भी "top" और "left" सीएसएस एट्रिब्यूट का इस्तेमाल करके लेआउट बनाने की तुलना में ज़्यादा तेज़ है.

इसका डेमो (और व्यू सोर्स में कोड) यहां देखा जा सकता है. हालांकि, हमें पता चला है कि 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 को सीन से मिली ओरिएंटेशन की जानकारी "मिटाने" के लिए मजबूर किया जाता है. इस तकनीक को "बिलबोर्डिंग" कहा जाता है. Gyroscope, इस तकनीक का इस्तेमाल करने के लिए सबसे सही टूल है.
सबसे अच्छी बात यह है कि सामान्य DOM और सीएसएस अब भी काम कर रहे हैं. जैसे, 3D टेक्स्ट लेबल पर माउस घुमाने पर, वह ड्रॉप शैडो के साथ चमकता है.

ज़ूम इन करने पर, मुझे पता चला कि टाइपोग्राफ़ी की स्केलिंग की वजह से, टेक्स्ट की पोज़िशनिंग में समस्याएं आ रही हैं. क्या ऐसा टेक्स्ट की कर्निंग और पैडिंग की वजह से हो रहा है? एक और समस्या यह थी कि ज़ूम करने पर टेक्स्ट पिक्सलेट हो जाता था. ऐसा इसलिए होता था, क्योंकि डीओएम रेंडरर, रेंडर किए गए टेक्स्ट को टेक्सचर वाले क्वाड के तौर पर ट्रीट करता है. इस तरीके का इस्तेमाल करते समय इस बात का ध्यान रखना चाहिए. मुझे लगता है कि मुझे बहुत बड़े फ़ॉन्ट साइज़ वाले टेक्स्ट का इस्तेमाल करना चाहिए था. शायद, आने वाले समय में इस पर और काम किया जा सकता है. इस प्रोजेक्ट में, मैंने "top/left" सीएसएस प्लेसमेंट टेक्स्ट लेबल का भी इस्तेमाल किया है. इनके बारे में पहले बताया जा चुका है. इनका इस्तेमाल, सौर मंडल में मौजूद ग्रहों के साथ दिखने वाले बहुत छोटे एलिमेंट के लिए किया गया है.
संगीत चलाना और उसे लूप करना
Mass Effect के 'गैलेक्टिक मैप' के दौरान बजने वाला संगीत, Bioware के संगीतकार सैम हलिक और जैक वॉल ने बनाया था. इसमें वह भावना थी जो मुझे वेबसाइट पर आने वाले व्यक्ति को महसूस करानी थी. हमें अपने प्रोजेक्ट में कुछ संगीत चाहिए था, क्योंकि हमें लगा कि यह माहौल का एक अहम हिस्सा है. इससे हमें वह एहसास पैदा करने में मदद मिली जो हम पैदा करना चाहते थे.
हमारे प्रोड्यूसर Valdean Klump ने सैम से संपर्क किया. सैम के पास Mass Effect के कई "कटिंग फ़्लोर" म्यूज़िक थे. उन्होंने हमें उनका इस्तेमाल करने की अनुमति दी. इस ट्रैक का टाइटल "In a Strange Land" है.
मैंने संगीत चलाने के लिए ऑडियो टैग का इस्तेमाल किया. हालांकि, Chrome में भी "loop" एट्रिब्यूट ठीक से काम नहीं करता था. कभी-कभी यह लूप नहीं होता था. आखिर में, इस ड्यूअल ऑडियो टैग हैक का इस्तेमाल, वीडियो के खत्म होने की जांच करने के लिए किया गया. साथ ही, वीडियो चलाने के लिए दूसरे टैग पर साइकल करने के लिए भी इसका इस्तेमाल किया गया. हालांकि, यह अब भी पूरी तरह से लूप नहीं हो रहा था, लेकिन मुझे लगता है कि मैं इससे ज़्यादा बेहतर नहीं कर सकता था.
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 के साथ इस्तेमाल करना अब भी एक चुनौती है. साथ ही, मुझे 100% यकीन नहीं है कि हम यहां जो कर रहे हैं वह सही तरीका है. यह अब भी हैक जैसा लगता है. ऐसा हो सकता है कि THREE के आने वाले वर्शन, अप ऐंड कमिंग सीएसएस रेंडरर के साथ, इन दोनों दुनिया को बेहतर तरीके से जोड़ने में मदद करें.
क्रेडिट
इस प्रोजेक्ट पर काम करने की अनुमति देने के लिए, ऐरन कोबलिन को धन्यवाद. जोनो ब्रैंडल को बेहतरीन यूज़र इंटरफ़ेस (यूआई) डिज़ाइन करने, उसे लागू करने, टाइप ट्रीटमेंट, और टूर लागू करने के लिए. Valdean Klump ने इस प्रोजेक्ट को नाम दिया और इसकी पूरी कॉपी तैयार की. सबाह अहमद को धन्यवाद. उन्होंने डेटा और इमेज सोर्स के इस्तेमाल के अधिकारों के लिए, मेट्रिक टन को साफ़ किया. क्लेम राइट को धन्यवाद, जिन्होंने पब्लिकेशन के लिए सही लोगों से संपर्क किया. तकनीकी विशेषज्ञता के लिए डग फ़्रिट्ज़. मुझे JS और CSS सिखाने के लिए, जॉर्ज ब्राउर को धन्यवाद. साथ ही, श्री ड्यूब को THREE.js के लिए धन्यवाद.