ارقص على أنغام WebVR

كنت متحمسًا عندما تواصل معي فريق "فن البيانات في Google" مع "مونيكر" بخصوص العمل معًا لاستكشاف الإمكانيات التي يوفّرها WebVR. لقد شاهدت العمل القادم من فريقهم على مر السنين وقد أثارت مشروعاتهم دائمًا ما يلفتني. أدّى تعاوننا إلى إنشاء تجربة Dance Tonite للرقص في الواقع الافتراضي المتغيرة باستمرار مع قناة LCD Soundsystem ومعجبيها. إليك كيف فعلنا ذلك.

المفهوم

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

لقد أخذنا هذا الأمر على محمل الجد. كل ما توصلنا إليه يجب أن يعمل مع جميع أنواع تكنولوجيا الواقع الافتراضي، بدءًا من سماعات رأس الواقع الافتراضي التي تعمل مع الهواتف المحمولة مثل Daydream View من Google وCardboard وGaler VR من Samsung إلى أنظمة يتم قياسها مثل HTC VIVE وOculus Rift التي تعكس تحركاتك الجسدية في محيطك الافتراضي. ربما الأهم من ذلك هو أننا شعرنا أنه من روح الويب أن نبتكر شيئًا يتناسب أيضًا مع كل من لا يملك جهاز واقع افتراضي.

1- تصوير الحركة الذاتي

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

شخص يسجّل نفسه على Dance Tonite تعرض الشاشة خلفهم ما يرونه في سماعة الرأس.

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

باستخدام WebVR، يمكن لمطوِّر البرامج الوصول إلى موضع رأس المستخدم واتجاهه عبر كائن VRPose. يتم تحديث هذه القيمة في كل إطار بواسطة جهاز VR بحيث يمكن للكود الخاص بك عرض إطارات جديدة من وجهة النظر الصحيحة. من خلال GamePad API مع WebVR، يمكننا أيضًا الوصول إلى موضع/اتجاه عناصر التحكّم الخاصة بالمستخدمين من خلال كائن GamepadPose. نقوم ببساطة بتخزين جميع قيم الموضع والاتجاه هذه في كل إطار، وبالتالي يتم إنشاء "تسجيل" لحركات المستخدم.

2. البساطة والأزياء

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

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

استوحينا التصميم المرئي من الغرف الملوّنة والأزياء الهندسية المعروضة في هذا التسجيل الذي صوّرته "مارغريت هاستينغز" في عام 1970 والتي تعرض لوحة "الباليه الثلاثي" للفنان "أوسكار شليمر".

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

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

3. دواسة دواسة للحركة

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

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

4. الغرف المتصلة

الغرف المتصلة

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

تحسينات الأداء: عدم إسقاط الإطارات

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

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

1- الهندسة المماثلة للمخزن المؤقت

وحيث إن مشروعنا بالكامل لا يستخدم سوى عدد قليل من الكائنات ثلاثية الأبعاد، فقد تمكنا من الحصول على تعزيز كبير في الأداء باستخدام Instanced Bufferity Geometry. وتسمح لك هذه الميزة بشكل أساسي بتحميل الكائن إلى وحدة معالجة الرسومات مرة واحدة ورسم أي عدد من "المثيلات" من ذلك الكائن في طلب رسم واحد. في Dance Tonite، لدينا 3 عناصر مختلفة فقط (مخروط وأسطوانة وغرفة بها ثقب)، ولكن لدينا مئات النسخ من هذه الأجسام على الأرجح. إنّ Instance Buffer Geometry (هندسة المخزن المؤقت) هي جزء من ThreeJS، لكننا استخدمنا شوكة "دوسان بوسنجاك" التجريبية وقيد التقدّم والتي تطبّق THREE.InstanceMesh، ما يجعل العمل مع "هندسة المخزن المؤقت المعزز" أسهل بكثير.

2. تجنُّب أداة تجميع النفايات

كما هو الحال مع العديد من لغات البرمجة النصية الأخرى، تقوم JavaScript تلقائيًا بتحرير الذاكرة عن طريق التعرف على الكائنات التي تم تخصيصها والتي لم تعد مُستخدَمة. تُسمى هذه العملية جمع البيانات غير الصالحة.

ولا يمكن للمطوّرين التحكم في وقت حدوث ذلك. قد يظهر جامع النفايات عند أبوابنا في أي وقت ويبدأ في إفراغها، ما يؤدي إلى إسقاط إطارات الصور أثناء استغراق وقتها الرائع.

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

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

const SERIALIZE_POSITION = new THREE.Vector3();
const SERIALIZE_ROTATION = new THREE.Quaternion();
const SERIALIZE_SCALE = new THREE.Vector3();
export const serializeMatrix = (matrix) => {
    matrix.decompose(SERIALIZE_POSITION, SERIALIZE_ROTATION, SERIALIZE_SCALE);
    return SERIALIZE_POSITION.toArray()
    .concat(SERIALIZE_ROTATION.toArray())
    .map(compressNumber);
};

3. سلسلة الحركة والتشغيل التدريجي

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

ولأنّ الزوّار على الويب غالبًا ما ينقرون على رابط بدون معرفة ما يمكن توقّعه، نحتاج إلى عرض المحتوى المرئي بسرعة وإلا سيغادرون الموقع في غضون ثوانٍ.

لهذا السبب، أردنا التأكد من أن مشروعنا يمكن أن يبدأ في أقرب وقت ممكن. في البداية، كنا نستخدم JSON كتنسيق لتحميل بيانات حركتنا. المشكلة هي أنه يتعين علينا تحميل ملف JSON الكامل قبل أن نتمكن من تحليله. ليس تقدميًا للغاية.

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

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

إليك ما يبدو عليه قسم من أحد تسجيلاتنا:

{"fps":15,"count":1,"loopIndex":"1","hideHead":false}
[-464,17111,-6568,-235,-315,-44,9992,-3509,7823,-7074, ... ]
[-583,17146,-6574,-215,-361,-38,9991,-3743,7821,-7092, ... ]
[-693,17158,-6580,-117,-341,64,9993,-3977,7874,-7171, ... ]
[-772,17134,-6591,-93,-273,205,9994,-4125,7889,-7319, ... ]
[-814,17135,-6620,-123,-248,408,9988,-4196,7882,-7376, ... ]
[-840,17125,-6644,-173,-227,530,9982,-4174,7815,-7356, ... ]
[-868,17120,-6670,-148,-183,564,9981,-4069,7732,-7366, ... ]
...

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

4. حركة الاستيفاء

بما أنّنا كنا نأمل عرض ما بين 30 و60 عرضًا في الوقت نفسه، احتجنا إلى خفض معدّل البيانات إلى مستوى أبعد من السابق. عالج فريق Data Arts المشكلة نفسها في مشروع Virtual Art Sessions، حيث شغّلوا تسجيلات لفنانين يرسمون أعمالهم بتقنية الواقع الافتراضي باستخدام إمالة الرشاة. وقد تم حلها من خلال عمل نسخ وسيطة من بيانات المستخدم بمعدلات إطارات منخفضة واستيفاء بين الإطارات أثناء تشغيلها. تفاجأنا عندما وجدنا أننا لم نستطِع كشف الفرق بين التسجيل المُضمَّن بسرعة 15 لقطة في الثانية مقابل التسجيل الأصلي الذي يبلغ 90 لقطة في الثانية.

لترى بنفسك، يمكنك إجبار Dance Tonite على تشغيل البيانات بمعدّلات مختلفة باستخدام سلسلة طلب البحث ?dataRate=. يمكنك استخدام ذلك لمقارنة الحركة المسجَّلة بسرعة 90 إطارًا في الثانية أو 45 إطارًا في الثانية أو 15 إطارًا في الثانية.

بالنسبة للموضع، يتم إجراء استقراء داخلي خطي بين الإطار الرئيسي السابق والإطار التالي، استنادًا إلى مدى قُربنا الزمني بين الإطارات الرئيسية (النسبة):

const { x: x1, y: y1, z: z1 } = getPosition(previous, performanceIndex, limbIndex);
const { x: x2, y: y2, z: z2 } = getPosition(next, performanceIndex, limbIndex);
interpolatedPosition = new THREE.Vector3();
interpolatedPosition.set(
    x1 + (x2 - x1) * ratio,
    y1 + (y2 - y1) * ratio,
    z1 + (z2 - z1) * ratio
    );

بالنسبة إلى الاتجاه، نجري استقراءًا خطيًا كرويًا (slerp) بين الإطارات الرئيسية. يتم حفظ الاتجاه على هيئة أرباع.

const quaternion = getQuaternion(previous, performanceIndex, limbIndex);
quaternion.slerp(
    getQuaternion(next, performanceIndex, limbIndex),
    ratio
    );

5. جارٍ مزامنة الحركات مع الموسيقى

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

ويؤدي ذلك إلى حدوث تقطع في تسجيلات الرقص الرائعة، والتي نريد تجنُّبها مهما كان ثمنها. لحل هذه المشكلة، طبّقنا الموقِّت الخاص بنا بلغة JavaScript. وبهذه الطريقة، يمكننا التأكّد من أنّ مقدار الوقت الذي يتغيّر بين الإطارات هو بالضبط مقدار الوقت الذي انقضى منذ الإطار الأخير. وعندما يتزامن المؤقت مع الموسيقى لمدة تزيد عن 10 مللي ثانية، تتم مزامنته مرة أخرى.

6. القش والضباب

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

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

لمواجهة ذلك، طرحنا الضباب. بعد مسافة معينة، يصبح كل شيء أسود ببطء. نظرًا لأننا لسنا بحاجة إلى حساب أو رسم بيانات غير مرئية، نستند إلى الأداء في الغرف غير المرئية، وهذا يتيح لنا توفير العمل لكل من وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات. كيف يمكنك تحديد المسافة المناسبة؟

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

// this is called every frame
// the FPS calculation is based on stats.js by @mrdoob
tick: (interval = 3000) => {
    frames++;
    const time = (performance || Date).now();
    if (prevTime == null) prevTime = time;
    if (time > prevTime + interval) {
    fps = Math.round((frames * 1000) / (time - prevTime));
    frames = 0;
    prevTime = time;
    const lastCullDistance = settings.cullDistance;

    // if the fps is lower than 52 reduce the cull distance
    if (fps <= 52) {
        settings.cullDistance = Math.max(
        settings.minCullDistance,
        settings.cullDistance - settings.roomDepth
        );
    }
    // if the FPS is higher than 56, increase the cull distance
    else if (fps > 56) {
        settings.cullDistance = Math.min(
        settings.maxCullDistance,
        settings.cullDistance + settings.roomDepth
        );
    }
    }

    // gradually increase the cull distance to the new setting
    cullDistance = cullDistance * 0.95 + settings.cullDistance * 0.05;

    // mask the edge of the cull distance with fog
    viewer.fog.near = cullDistance - settings.roomDepth;
    viewer.fog.far = cullDistance;
}

شيء للجميع: إنشاء واقع افتراضي على الويب

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

1- الكرة الصفراء

لذا، فإن مستخدمي الواقع الافتراضي على مستوى الغرفة سيقدمون الأداء، ولكن كيف سيكون لمستخدمي أجهزة الواقع الافتراضي على الهاتف المحمول (مثل Cardboard أو Daydream View أو Samsung الترس) تجربة المشروع؟ لهذا، قدمنا عنصرًا جديدًا للبيئتنا: الجرم الأصفر.

الكرة الصفراء
الكرة الصفراء

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

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

2. وجهة نظر أخرى

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

3. الظلال: التظاهر 'حتى تصنعها

وجدنا أن بعض المستخدمين لدينا يواجهون صعوبة في رؤية العمق في وجهة نظرنا المتساوية القياس. أنا على يقين بأن زاكسون كانت أيضًا من أوائل ألعاب الكمبيوتر في التاريخ التي تعرض ظلاً ديناميكيًا تحت أجسامها الطائرة.

تظليل

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

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

لإنشائها، استخدمنا هذا الزخرفة بتدرّج أبيض ناعم إلى أسود (بدون شفافية ألفا). نُعيِّن المادة على أنها شفافة ونستخدم المزج المطروح. يساعدهم ذلك على المزج بشكل جيد عندما يتداخلون:

function createShadow() {
    const texture = new THREE.TextureLoader().load(shadowTextureUrl);
    const material = new THREE.MeshLambertMaterial({
        map: texture,
        transparent: true,
        side: THREE.BackSide,
        depthWrite: false,
        blending: THREE.SubtractiveBlending,
    });
    const geometry = new THREE.PlaneBufferGeometry(0.5, 0.5, 1, 1);
    const plane = new THREE.Mesh(geometry, material);
    return plane;
    }

4. التواجد هناك

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

5. مشاركة التسجيلات

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

مشاركة التسجيلات

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

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

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

6. أرض صلبة: Google Cloud وFirebase

غالبًا ما تكون الجهة الخلفية لموقع "محتوى من إنشاء المستخدمين" معقّدة وهشة، ولكنّنا توصّلنا إلى نظام بسيط وقوي بفضل Google Cloud وFirebase. عندما يحمّل فنان رقصة جديدة إلى النظام، تتم مصادقة رقصتها مع إخفاء هوية المستخدم من خلال مصادقة Firebase. ويتم منحهم الإذن لتحميل التسجيل إلى مساحة مؤقتة باستخدام Cloud Storage لبرنامج Firebase. عند اكتمال التحميل، يستدعي جهاز العميل مشغّل HTTP لدوال السحابة الإلكترونية لبرنامج Firebase باستخدام رمز Firebase المميّز. يؤدي ذلك إلى تشغيل عملية الخادم التي تتحقق من الإرسال وإنشاء سجل قاعدة بيانات وينقل التسجيل إلى دليل عام على Google Cloud Storage.

أرضية صلبة

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

استخدمنا قاعدة بيانات Firebase في الوقت الفعلي ونقاط نهاية Cloud Function لإنشاء أداة إشراف/تنظيم بسيطة تتيح لنا مشاهدة كل إرسال جديد في الواقع الافتراضي ونشر قوائم تشغيل جديدة من أي جهاز.

7. مشغِّلو الخدمات

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

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

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
config.plugins.push(
    new SWPrecacheWebpackPlugin({
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'service-worker.js',
    minify: true,
    navigateFallback: 'index.html',
    staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
    runtimeCaching: [{
        urlPattern: /playlist\.json$/,
        handler: 'networkFirst',
    }, {
        urlPattern: /\/recordings\//,
        handler: 'cacheFirst',
        options: {
        cache: {
            maxEntries: 120,
            name: 'recordings',
        },
        },
    }],
    })
);

في الوقت الحالي، لا يعالج المكوّن الإضافي أصول الوسائط التي يتم تحميلها تدريجيًا مثل ملفات الموسيقى، لذا تعاملنا مع هذا الأمر من خلال ضبط العنوان Cache-Control في Cloud Storage على هذه الملفات على public, max-age=31536000 لكي يخزّن المتصفّح الملف مؤقتًا لمدة تصل إلى عام.

الخاتمة

نحن متحمّسون لرؤية ما سيضيفه الفنانون إلى هذه التجربة وسيستخدمونها كأداة للتعبير الإبداعي باستخدام الحركة. لقد أصدرنا كل الرموز البرمجية المفتوحة المصدر، والتي يمكنك العثور عليها على الرابط https://github.com/puckey/dance-tonite. في هذه الأيام الأولى للواقع الافتراضي وخاصةً WebVR، نتطلع إلى معرفة الاتجاهات الجديدة والإبداعية غير المتوقعة التي سيتخذها هذا الوسيط الجديد. ابدأ بالرقص.