كسب 100,000 نجمة

مرحبًا، اسمي مايكل تشانغ وأعمل مع فريق فنون البيانات في Google. مؤخرًا، أكملنا 100,000 نجمة، وهي تجربة في Chrome تُظهر النجوم القريبة. تم إنشاء المشروع باستخدام THREE.js وCSS3D. في دراسة الحالة هذه، سأحدد عملية الاكتشاف، وأشارك بعض تقنيات البرمجة، وأختتم ببعض الأفكار للتحسين في المستقبل.

ستكون المواضيع التي تتم مناقشتها هنا واسعة إلى حدٍ ما وتتطلب بعض المعرفة بشأن THREE.js، ولكن نأمل أن تتمكن من الاستفادة من ذلك من خلال التقارير التحليلية الفنية بعد حدوث الكوارث. لا تتردد في الانتقال إلى المنطقة محل الاهتمام باستخدام زر جدول المحتويات على اليمين. أولاً، سأعرض جزء العرض من المشروع، متبوعًا بإدارة أداة التظليل، وأخيرًا كيفية استخدام تصنيفات نصوص CSS إلى جانب WebGL.

100,000 نجمة، تجربة في Chrome أعدّها فريق "فن البيانات"
تستخدم "100,000 نجمة" THREE.js لتصور النجوم القريبة في مجرة درب التبانة

استكشاف الفضاء

بعد وقت قصير من انتهائنا من Small Arms Globe، بدأتُ بتجربة عرض جسيمات THREE.js من خلال عمق الحقل. لاحظت أنه يمكنني تغيير "مقياس" المشهد المفسَّر من خلال تعديل مقدار التأثير المطبق. عندما يكون عمق التأثير الميداني كبيرًا للغاية، أصبحت الأجسام البعيدة ضبابية للغاية تشبه الطريقة التي يعمل بها التصوير بالإمالة ذات الإزاحة على إعطاء المرء وهمًا في النظر إلى مشهد مجهري. وعلى العكس من ذلك، فإن خفض التأثير جعله يبدو كما لو كنت تحدق في الفضاء السحيق.

بدأتُ البحث عن البيانات التي يمكنني استخدامها لحقن مواضع الجسيمات، وهو مسار يقودني إلى قاعدة بيانات HYG على موقع astronexus.com وهو عبارة عن تجميع لمصادر البيانات الثلاثة (Hipparcos وYle Bright Star Catalog وGliese/Jahreiss Catalog) المصحوبة بالإحداثيات الكارتيزية المحسوبة مسبقًا. فهيا بنا نبدأ!

رسم بيانات النجوم.
تكمن الخطوة الأولى في رسم كل نجمة في القائمة كجزيء واحد.
النجوم التي تمت تسميتها.
بعض النجوم في الكتالوج تحمل أسماء علم، وهي مذكورة هنا.

فقد استغرق الأمر حوالي ساعة لاختراق شيء وضع بيانات النجوم في مساحة ثلاثية الأبعاد. يوجد 119617 نجمة بالضبط في مجموعة البيانات، لذا فإن تمثيل كل نجمة بجسيم لا يمثل مشكلة في وحدة معالجة الرسومات الحديثة. هناك أيضًا 87 نجمة محددة بشكل فردي، لذلك أنشأت علامة CSS على سطح الصفحة باستخدام الأسلوب نفسه الذي وصفته في Small Arms Globe.

وخلال هذا الوقت، انتهيت للتو من سلسلة Mass Effect. في هذه اللعبة، تتم دعوة اللاعب لاستكشاف المجرّة واستكشاف كواكب مختلفة والتعرُّف على تاريخها الخيالي الذي يبدو وكأنّه من ويكيبيديا، مثل أنواع الكائنات التي ازدهرت على هذا الكوكب وتاريخها الجيولوجي وما إلى ذلك.

من خلال معرفة ثروة البيانات الفعلية الموجودة هناك عن النجوم، يمكن للمرء أن يقدّم معلومات حقيقية عن المجرة بالطريقة نفسها. يتمثل الهدف النهائي لهذا المشروع في إحياء هذه البيانات، والسماح للمشاهد باستكشاف المجرة à la Mass Effect، والتعرف على النجوم وتوزيعها، ونأمل أن يلهمنا شعورًا بالرهبة والتساؤل عن الفضاء. أخيرًا!

من المفترَض أن أستهلك بقية دراسة الحالة هذه بالقول إنّني أنا لست خبيرًا فلكيًا بأي شكل من الأشكال، وأنّ هذا العمل من أبحاث للهواة يدعمه بعض النصائح من خبراء خارجيين. وينبغي بالتأكيد تفسير هذا المشروع على أنه تفسير فنان للفضاء.

إنشاء مجرة

وكانت خطتي هي إنشاء نموذج للمجرة بشكل إجرائي يمكن من خلاله وضع بيانات النجوم في سياقها، ونأمل أن يقدم هذا النموذج رؤية رائعة لمكاننا في مجرة درب التبانة.

نموذج أولي مبكر للمجرة.
نموذج أولي مبكر لنظام جسيمات درب التبانة

ولإنشاء مجرة درب التبانة، أنتجت 100000 جسيم ووضعتها في حلزوني عن طريق محاكاة طريقة تكوين أذرع المجرة. لم أكن قلقًا للغاية بشأن تفاصيل تشكيل الذراع الحلزوني لأن هذا سيكون نموذجًا تمثيليًا وليس نموذجًا رياضيًا. ومع ذلك، فقد حاولتُ الحصول على عدد الأذرع الحلزونية بشكل صحيح أكثر أو أقل، مع الدوران في "الاتجاه الصحيح".

وفي الإصدارات اللاحقة من نموذج "درب التبانة"، قللت من التأكيد على استخدام الجزيئات لصالح صورة مسطحة للمجرّة إلى جانب الجزيئات، على أمل إضفاء مظهر فوتوغرافي عليها. الصورة الفعلية للمجرّة الحلزونية NGC 1232 على بُعد 70 مليون سنة ضوئية تقريبًا منّا، وقد تم التلاعب بالصورة لتبدو مثل مجرة درب التبانة.

التعرف على حجم المجرة.
كل وحدة من وحدات GL هي سنة ضوئية. يبلغ عرض الكرة في هذه الحالة 110,000 سنة ضوئية، وتشمل نظام الجسيمات.

قررتُ مبكرًا تمثيل وحدة GL واحدة، في الأساس، بكسل في ثلاثي الأبعاد، كسنة ضوئية واحدة - وهو اتفاقية موحد لكل شيء يتم تصوره، ولسوء الحظ أعطاني مشكلات دقة خطيرة لاحقًا.

أما الاصطلاح الآخر، فقد قررتُ تدوير المشهد بأكمله بدلاً من تحريك الكاميرا، وهو ما فعلته في بعض المشاريع الأخرى. تتمثل إحدى الميزات في وضع كل شيء على "أسطوانات دوّارة" بحيث يؤدي سحب الماوس إلى اليسار واليمين لتدوير الكائن المعني، لكن التكبير هو مجرد تغيير في موضع Camera.position.z.

إنّ مجال الرؤية (FOV) للكاميرا هو أيضًا ديناميكي. كلما انسحب أحدهم للخارج، تتسع مجال الرؤية، لأخذ المزيد والمزيد من المجرة. العكس صحيح عند التحرك للداخل نحو نجم، يضيق مجال الرؤية. يتيح ذلك للكاميرا مشاهدة الأشياء المتناهية (مقارنةً بالمجرّة) من خلال جعل مجال رؤية الكاميرا مصغّرًا إلى عدسة مكبرة تشبه الإلهاء بدون الحاجة إلى التعامل مع مشكلات القطع القريبة من المستوى.

الطرق المختلفة لعرض المجرّة.
(أعلاه) مجرة الجسيمات المبكرة. (أدناه) الجسيمات المصحوبة بمستوى صورة.

ومن هنا تمكنت من "وضع" الشمس في عدد من الوحدات بعيدًا عن مركز المجرة. وتمكّنت أيضًا من تصوير الحجم النسبي للنظام الشمسي من خلال تحديد نصف قطر منحدر كايبر (لقد اخترت في النهاية تصوير سحابة Oort) بدلاً من ذلك. من خلال نموذج النظام الشمسي هذا، يمكنني أيضًا تصور مدار مبسط للأرض، ونصف القطر الفعلي للشمس في المقارنة.

النظام الشمسي.
الشمس التي تدور حولها كواكب وكرة تمثّل حزام كايبر.

كان من الصعب تصوير الشمس. اضطررت إلى الغش في أكبر عدد ممكن من تقنيات الرسومات في الوقت الفعلي. سطح الشمس عبارة عن رغوة ساخنة من البلازما ويحتاج إلى النبض والتغير مع مرور الوقت. تمت محاكاة ذلك من خلال زخرفة نقطية لصورة بالأشعة تحت الحمراء لسطح الشمس. يقوم عامل تظليل السطح بالبحث عن اللون بناءً على التدرج الرمادي لهذا الزخرفة وإجراء بحث بمنحدر لون منفصل. وعندما يتغير البحث بمرور الوقت، ينتج عنه هذا التشوّه الشبيه بالحمم البركانية.

وتم استخدام أسلوب مشابه لكون الشمس، غير أنّه سيكون بطاقة الرموز المتحركة المسطحة التي تواجه الكاميرا دائمًا باستخدام https://github.com/mrdoob/three.js/blob/master/src/extras/core/Gyroscope.js.

جارٍ عرض Sol.
الإصدار المبكر من الشمس:

أُنشئت التوهجات الشمسية باستخدام أدوات تظليل الأجزاء والرأس التي تم تطبيقها على طائفة، وهي تدور حول حافة سطح الشمس مباشرةً. يحتوي مظلل الرأس على وظيفة تشويش تجعله ينسج بطريقة تشبه النقطة.

كانت هنا حيث بدأت أواجه بعض المشكلات المتعلقة بخوارزمية z بسبب دقة GL. تم تحديد جميع متغيرات الدقة مسبقًا في THREE.js، لذلك لم أتمكن من رفع الدقة بشكل واقعي بدون قدر كبير من العمل. لم تكن مشاكل الدقة سيئة عند نقطة الأصل. ومع ذلك، بمجرد أن بدأت في تصميم أنظمة نجمية أخرى، أصبحت هذه مشكلة.

نموذج نجمي.
تم تعميم الرمز الخاص بعرض الشمس في وقت لاحق لعرض نجوم أخرى.

وكانت هناك بعض الحيل التي استخدمتها للتخفيف من صعوبات "ي. ي". دالة Material.polygonoffset الثالثة هي خاصية تسمح بعرض المضلّعات في موقع مختلف مرئي (على حدّ تقديري). وقد تم استخدام هذه الخوارزمية لإجبار سطح الشمس على الظهور دائمًا على سطح الشمس. وأسفل هذا العنصر، تم عرض "هالة" الشمس لإعطاء أشعة ضوئية حادة تبتعد عن الكرة الأرضية.

كانت هناك مشكلة أخرى تتعلق بالدقة، وهي أن نماذج النجوم ستبدأ في التذبذب مع تكبير المشهد. لحل هذه المشكلة، كان لابد من إزالة الصفر من دوران المشهد وتدوير نموذج النجوم وخريطة البيئة بشكل منفصل لأوهامك بأنك تدور حول النجمة.

جارٍ إنشاء إضاءة خافتة

مع القوة الكبيرة تأتي مسئولية كبيرة.
مع القوة الكبيرة تأتي مسؤولية كبيرة.

تصورات الفضاء هي المكان الذي أشعر فيه أنه يمكنني الابتعاد عن الإفراط في استخدام توهج العدسة. تقدّم THREE.LensFlare هذا الغرض، ولكن كل ما عليّ فعله هو صياغة بعض الأشكال السداسية المشوهة وشَرطة JJ Abrams. يوضح المقتطف أدناه كيفية إنشائها في المشهد.

// 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;

}
}

طريقة سهلة لتمرير الزخرفة

فيلم مستوحى من Homeworld
طائرة الديكارتية للمساعدة في الاتجاه المكاني في الفضاء.

بالنسبة لـ "مستوى الاتجاه المكاني"، تم إنشاء THREE.CylinderGeometry() عملاق وتوسيط حول الشمس. لإنشاء "موجة من الضوء" المتحرّكة للخارج، قمتُ بتعديل إزاحة الزخرفة بمرور الوقت على النحو التالي:

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

map هي الهيئة التي تنتمي إلى المادة، والتي تحصل على دالة onUpdate يمكنك الكتابة فوقها. يؤدي تعيين إزاحتها إلى "تمرير" الهيئة على هذا المحور، ويحتاج إرسال الرسائل غير المرغوب فيها إلىUpdate = 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') );

}
}

تأخذ الدالة uploadShaders() قائمة بأسماء ملفات أداة التظليل (توقعًا .fsh للجزء و .vsh لأدوات تظليل الرأس)، وتحاول تحميل بياناتها، ثم تستبدل القائمة بالكائنات فقط. النتيجة النهائية هي في الزي الرسمي THREE.js يمكنك تمرير أدوات التظليل إليها على النحو التالي:

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

ربما كان بإمكاني استخدام request.js على الرغم من أن ذلك كان سيحتاج إلى بعض إعادة تجميع الرموز لهذا الغرض فقط. على الرغم من سهولة هذا الحل، إلا أنه يمكن تحسينه بناءً على ما أعتقد، حتى كإضافة THREE.js. يُرجى إعلامنا إذا كانت لديك اقتراحات أو طرق لتحسين أداء هذا الخيار.

تصنيفات نصوص CSS في أعلى THREE.js

في مشروعنا الأخير، Small Arms Globe، تلاعبت بجعل تسميات النص تظهر أعلى مشهد THREE.js. تحسب الطريقة التي كنت أستخدمها موضع النموذج المطلق للمكان الذي أريد أن يظهر فيه النص، ثم تحل موضع الشاشة باستخدام THREE.Projector() ، وأخيرًا تستخدم CSS "أعلى" و "يسار" لوضع عناصر CSS في الموضع المطلوب.

استخدمت التحسينات المبكرة في هذا المشروع الأسلوب نفسه، لكنني كنت أتوق لتجربة هذه الطريقة الأخرى التي وصفها "لويس كروز".

الفكرة الأساسية: مطابقة مصفوفة التحويل في CSS3D مع كاميرا ومشهد THREE، ويمكنك "وضع" عناصر CSS في وضع ثلاثي الأبعاد كما لو كانت أعلى المشهد الثالث. على الرغم من ذلك، هناك قيود على ذلك، على سبيل المثال، لن تتمكن من وضع النص أسفل كائن THREE.js. ولا يزال ذلك أسرع بكثير من محاولة تنفيذ التنسيق باستخدام سمتَي CSS "أعلى" و "يسار".

التصنيفات النصية
استخدام عمليات التحويل 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 "فقدان" اتجاهه الموروث من المشهد. تُسمى هذه التقنية "وضع الإعلانات"، ويعد الجيروسكوب مثاليًا للقيام بذلك.

من اللطيف أن تظل كل عناصر DOM وCSS العادية قيد التشغيل، مثل إمكانية تمرير الماوس فوق تصنيف نص ثلاثي الأبعاد وجعله يضيء بتظليل القطرات.

التصنيفات النصية
تساعد التصنيفات النصية دائمًا في مواجهة الكاميرا من خلال إرفاقها بالرمز THREE.Gyroscope().

عند التكبير، وجدت أن توسيع أسلوب الخط كان يتسبب في مشكلات في تحديد الموضع. هل من المحتمل أن يكون السبب هو تأخُّر النص الممتد والمساحة المتروكة معه؟ وكانت هناك مشكلة أخرى تتمثل في تقطيع النص عند تكبيره، حيث يتعامل عارض DOM مع النص المعروض كرباعي مزخرف، وهو أمر يجب الانتباه إليه عند استخدام هذه الطريقة. عند النظر بأثر رجعي، كان بإمكاني استخدام نص ضخم بحجم الخط، وربما هذا شيء للاستكشاف المستقبلي. في هذا المشروع، استخدمت التسميات النصية لمواضع صفحات CSS "أعلى/يسار"، والتي تم وصفها سابقًا، للعناصر الصغيرة جدًا التي تصاحب الكواكب في النظام الشمسي.

تشغيل الموسيقى وتكرارها

ألحان مقطوعة موسيقية تم تشغيلها في أغنية Galactic Map التي صممها Mass Effect من إعداد مؤلفي Bioware Sam Hurick وJack Wall، واتسمت هذه الموسيقى بنوعية العاطفة التي تمنيت للزائر أن يختبرها. أردنا الحصول على بعض الموسيقى في مشروعنا لأننا شعرنا أنّها جزء مهم من الأجواء، ما ساعد في إثارة الشعور بالخوف والتعجب الذي كنا نحاول تحقيقه.

تواصل منتِجنا "فالدين كلومب" مع "سام" الذي قدّم مجموعة من الموسيقى المذهلة من قناة 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 لفترة، أشعر أنني وصلت إلى مرحلة كانت فيها بياناتي تختلط فيها بشكل كبير مع التعليمات البرمجية. على سبيل المثال، عند تحديد المواد والمواد الزخارف والتعليمات الهندسية المضمنة في الصفحة، كنتُ أساسًا "تصميم ثلاثي الأبعاد باستخدام التعليمات البرمجية". كان هذا سيئًا حقًا، وهو مجال يمكن أن تحسّن فيه المهام المستقبلية باستخدام THREE.js بشكل كبير، على سبيل المثال، تحديد البيانات المادية في ملف منفصل، ويفضل أن يكون ذلك قابلاً للعرض وقابلاً للتعديل في بعض السياق، ويمكن إعادتها إلى المشروع الرئيسي.

قضى زميلنا راي ماكلور بعض الوقت أيضًا في إنشاء بعض "الضوضاء في الفضاء" التوليدية الرائعة، والتي كان لا بد من خفضها بسبب عدم استقرار واجهة برمجة تطبيقات الصوت على الويب، وتؤدي إلى تعطل Chrome من حين لآخر. ومن المؤسف أنه قد شجّعنا على التفكير بشكل أفضل في سوق العمل في المستقبل. بناءً على ذلك، أبلغنا أنّه قد تم تصحيح واجهة برمجة التطبيقات Web Audio API، ومن المحتمل أن تكون هذه الواجهة تعمل الآن بشكلٍ سليم، وهو أمر يجب توقُّعه في المستقبل.

لا تزال العناصر الطباعية المقترنة بـ WebGL تمثل تحديًا، ولست متأكدًا بنسبة 100% من ما نفعله هنا هو الطريقة الصحيحة. لا يزال الأمر يبدو وكأنه تحدي حقيقي. ربما يمكن استخدام الإصدارات المستقبلية من THREE مع عارض CSS القادم للانضمام إلى العالمَين بشكل أفضل.

المساهمون

شكرًا لآرون كوبلن للسماح لي بالذهاب إلى المدينة من خلال هذا المشروع. جونو براندل لتصميم واجهة المستخدم الممتازة + التنفيذ، ومعالجة الكتابة، وتنفيذ الجولة. فالديان كلومب لإعطاء اسم للمشروع وجميع النسخة. صباح أحمد محو كمية هائلة من حقوق استخدام البيانات ومصادر الصور. كليم رايت للوصول إلى الأشخاص المناسبين لنشره. "دوغ فريتز" للتميز التقني. لجورج بروير لتعليمي لغة JavaScript وCSS. وبالطبع، فإن السيد دوب لـ THREE.js.

المراجع