كنت متحمسًا عندما تواصل فريق فنون البيانات من Google مع مونيكر مع نفسي بشأن العمل معًا لاستكشاف الإمكانيات التي قدمها WebVR. لقدراقبت أعمال فريقه على مرّ السنين، ومشاريعه كانت تهمّني دائمًا. وقد أدّى هذا التعاون إلى إنشاء Dance Tonite، وهي تجربة رقص دائمة التغيير في الواقع الافتراضي مع LCD Soundsystem ومعجبيها. في ما يلي كيفية إجراء ذلك.
المفهوم
بدأنا بتطوير سلسلة من النماذج الأولية باستخدام WebVR، وهو معيار مفتوح يتيح إمكانية استخدام الواقع الافتراضي من خلال الانتقال إلى موقع إلكتروني باستخدام المتصفّح. والهدف هو تسهيل استخدام تجارب الواقع الافتراضي للجميع، بغض النظر عن الجهاز الذي تستخدمه.
لقد أخذنا هذه الملاحظات على محمل الجد. أيا كان ما توصلنا إليه يجب أن يعمل مع جميع أنواع الواقع الافتراضي، بدءًا من سماعات الرأس التي تعمل بتكنولوجيا الواقع الافتراضي التي تعمل مع الهواتف الجوالة مثل Daydream View من Google، وCardboard، وSamsung Wear VR، ووصولاً إلى الأنظمة المتوافقة مع نطاق الغرفة مثل HTC VIVE وOculus Rift، والتي تعكس تحركاتك الجسدية في بيئتك الافتراضية. والأهم من ذلك، شعرنا أنّه من المفيد أن نوفّر تجربة تناسب جميع المستخدمين، حتى أولئك الذين لا يملكون جهاز واقع افتراضي.
1. تقنية تصوير الحركة الذاتي
ولأنّنا أردنا إشراك المستخدمين بشكل إبداعي، بدأنا في البحث عن فرص المشاركة والتعبير عن الذات باستخدام الواقع الافتراضي. لقد أُعجبنا بمدى الدقة التي يمكنك بها التحرك والنظر حولك في الواقع الافتراضي، ومدى الدقّة التي يوفّرها. وقد أعطتنا هذه الملاحظة فكرة. بدلاً من أن ينظر المستخدمون إلى شيء ما أو ينشئونه، ما رأيك في تسجيل حركاتهم؟
لقد أنشأنا نموذجًا أوليًا سجّلنا فيه مواضع نظّارات الواقع الافتراضي ومقاييس التحكّم أثناء الرقص. لقد استبدلنا المواقف المسجّلة بأشكال مجردة، ونذهلنا بالنتائج. كانت النتائج بشرية للغاية وتتضمن الكثير من السمات الشخصية! وسرعان ما أدركنا أنه يمكننا استخدام WebVR لالتقاط صور للحركة بتكلفة زهيدة في المنزل.
باستخدام WebVR، يمكن للمطوّر الوصول إلى موضع رأس المستخدم واتجاهه من خلال عنصر VRPose . يتم تعديل هذه القيمة في كل لقطة بواسطة أجهزة الواقع الافتراضي لكي تتمكّن الرموز البرمجية من عرض لقطات جديدة من نقطة النظر الصحيحة. من خلال GamePad API مع WebVR، يمكننا أيضًا الوصول إلى موضع/اتجاه أدوات التحكّم الخاصة بالمستخدمين من خلال العنصر GamepadPose . نحفظ ببساطة كل قيم الموضع والاتجاه هذه في كل لقطة، وبالتالي ننشئ "تسجيلًا" لحركات المستخدم.
2. الأزياء البسيطة
باستخدام معدّات الواقع الافتراضي الحالية التي تعمل على مستوى الغرفة، يمكننا تتبُّع ثلاث نقاط في جسم المستخدم: رأسه ويديّه. في Dance Tonite، أردنا التركيز على الجانب الإنساني في حركة هذه النقاط الثلاث في الفضاء. لتحقيق ذلك، دفعنا العنصر الجمالي إلى أدنى حد ممكن من أجل التركيز على الحركة. لقد أعجبتنا فكرة تحفيز تفكير المستخدمين.
هذا الفيديو الذي يعرض عمل عالم النفس السويدي "غونار جوهانسون" هو أحد الأمثلة التي استنادنا إليها عند التفكير في تبسيط المحتوى قدر الإمكان. ويوضّح هذا الفيديو كيف يمكن التعرّف على النقاط البيضاء العائمة على الفور على أنّها أجسام عندما تظهر في الحركة.
من الناحية المرئية، استوحينا من الغرف الملونة والأزياء الهندسية في تسجيل "رقصة ترايدك" التي أعادتها "مارغريت هاستينغز" في عام 1970، وهي رقصة من تصميم "أوسكار شليمر".
بينما كان سبب اختيار "شلومر" للأزياء الهندسية المجردة هو الحدّ من حركات الراقصين إلى حركات الدمى والماريونيت، كان هدفنا مختلفًا في Dance Tonite.
وقد استند اختيارنا للأشكال إلى مقدار المعلومات التي تنقل من خلال الاستدارة. يبدو شكل الكرة متطابقًا بغض النظر عن كيفية تدويره، ولكن شكل الهرم يقترب ويشير إلى الاتجاه الذي ينظر إليه ويبدو مختلفًا من الأمام مقارنةً بالخلف.
3- دواسة حلقة للحركة
أردنا عرض مجموعات كبيرة من الأشخاص الذين يرقصون ويتحركون معًا. ولن يكون من الممكن إجراء ذلك مباشرةً، لأنّ أجهزة الواقع الافتراضي ليست متاحة بعد بكميات كبيرة. لكننا ما زلنا نرغب في وجود مجموعات من الأشخاص يتفاعلون مع بعضهم البعض من خلال الحركة. تذكّرنا بأداء "نورمان ماكلارن" المتكرّر في مقطع الفيديو "كانون" الذي تم إنشاؤه عام 1964.
يتميز أداء ماكلارين بسلسلة من الحركات المصمّمة بدرجة عالية والتي تبدأ في التفاعل مع بعضها بعد كل حلقة. وكما هو الحال إلى حد كبير مع دواسة دواسة في الموسيقى، حيث يلتقي الموسيقيون بأنفسهم عبر وضع أجزاء مختلفة من الموسيقى الحية، كنا مهتمين بمعرفة ما إذا كان بإمكاننا إنشاء بيئة تتيح للمستخدمين ارتجال الإصدارات غير الأكثر مرونة من العروض.
4. الغرف المتصلة ببعضها
مثل الكثير من الأغاني، يتم إنشاء مقاطع صوتية من LCD Soundsystem باستخدام مقاييس زمنية دقيقة. وتتضمن أغنيتها Tonite، والتي تم تضمينها في مشروعنا، قياسات تبلغ مدتها 8 ثوانٍ بالضبط. أردنا أن نجعل المستخدمين يقومون بأداء كل حلقة مدتها 8 ثوانٍ في المسار. على الرغم من أنّ إيقاع هذه القياسات لا يتغيّر، يتغيّر المحتوى الموسيقي لها. مع تقدّم الأغنية، تظهر لحظات بآلات وأصوات مختلفة يمكن للفنانين التفاعل معها بطرق مختلفة. يتم التعبير عن كلّ من هذه الإجراءات على أنّه غرفة، يمكن للمستخدمين من خلالها تقديم أداء يناسبها.
التحسينات لتحسين الأداء: عدم إسقاط اللقطات
إنّ إنشاء تجربة واقع افتراضي متعددة المنصات تعمل على قاعدة رموز واحدة مع الأداء الأمثل لكل جهاز أو نظام ليس بالأمر الهين.
من أكثر الأمور إزعاجًا التي قد تواجهها في تقنية الواقع الافتراضي أنّ معدل عرض الإطارات لا يتماشى مع حركتك. إذا حوّلت رأسك ولكن المحتوى المرئي الذي تراه عيناك لا يتطابق مع الحركة التي تشعر بها أذنك الداخلية، يؤدي ذلك إلى اضطراب المعدة بشكل فوري. لهذا السبب، كان علينا تجنُّب أي تأخُّر كبير في معدل عرض اللقطات. في ما يلي بعض التحسينات التي نفّذناها.
1. هندسة ذاكرة التخزين المؤقت المستندة إلى النماذج
بما أنّ مشروعنا بأكمله يستخدم عددًا قليلاً من الأجسام الثلاثية الأبعاد، تمكّنا من تحقيق
تحسُّن كبير في الأداء باستخدام ميزة "الأشكال الهندسية للوحات النموذجية". في الأساس، يتيح لك
تحميل العنصر إلى وحدة معالجة الرسومات مرة واحدة ورسم أي عدد تريده من "نماذج"
هذا العنصر في طلب رسم واحد. في لعبة Dance Tonite، لدينا 3
كائنات مختلفة فقط (مخروط وأسطوانة وغرفة بها ثقب)، ولكن قد يكون لدينا
مئات نُسخ من هذه الكائنات. إنّ "الأشكال الهندسية في وحدة تخزين المؤشرات" هي جزء من مكتبة
ThreeJS،
ولكننا استخدمنا الإصدار التجريبي الجاري تطويره من مكتبة
Dusan Bosnjak الذي ينفِّذ
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 عرضًا في الوقت نفسه، كان علينا خفض معدّل نقل البيانات أكثر مما سبق. عالج فريق "الفنون المستندة إلى البيانات" المشكلة نفسها في مشروع جلسات الفن الافتراضي، حيث يتم تشغيل تسجيلات الفنّانين وهم يرسمون في الواقع الافتراضي باستخدام تطبيق Tilt Brush. لقد قاموا بحلها من خلال إنشاء نسخ وسيطة من بيانات المستخدم بمعدلات إطارات منخفضة والدمج بين الإطارات أثناء تشغيلها مرة أخرى. لقد فوجئنا عند معرفة أنّه بالكاد يمكننا رصد الفرق بين التسجيل الذي تم تجميعه بمعدل 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 عرضًا إضافيًا تقريبًا مطلوبًا للنهاية الجديدة. ولكن تم خفض معدّلات عرض اللقطات على أجهزة جوّالة معيّنة إلى النصف.
لمواجهة ذلك، أضفنا الضباب. بعد مسافة معيّنة، يصبح كل شيء بطيئًا باللون الأسود. بما أنّنا لا نحتاج إلى احتساب أو رسم ما ليس مرئيًا، نزيل الأداء في الغرف غير المرئية، ما يسمح لنا بحفظ العمل لكل من وحدة المعالجة المركزية ووحدة معالجة الرسومات. ولكن كيف يمكنكم تحديد المسافة الصحيحة؟
يمكن لبعض الأجهزة التعامل مع أي محتوى بينما تكون إمكانيات بعضها الآخر محدودة. لقد اخترنا تطبيق مقياس متزايد. ومن خلال القياس المستمر لعدد اللقطات في الثانية، يمكننا ضبط مسافة الضباب وفقًا لذلك. طالما أنّ معدّل عرض اللقطات يعمل بسلاسة، نحاول تنفيذ المزيد من عمليات التقديم من خلال إزالة الضباب. إذا لم يكن معدل عرض الإطارات بسلاسة بما فيه الكفاية، يمكننا تقريب الضباب مما يسمح لنا بتخطي عروض الأداء في الظلام.
// 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 Gear) المشروع؟ لهذا، قدمنا عنصرًا جديدًا لبيئتنا: الجرم الأصفر.
عند مشاهدة المشروع باستخدام الواقع الافتراضي، ستشاهده من منظور الكرة الصفراء. أثناء انتقالك من غرفة إلى أخرى، يتفاعل الراقصون معك. إنها تشير إليك وترقص من حولك وتقوم بحركات مضحكة خلفك وتخرج بسرعة من طريقك حتى لا تصطدم بك. ويكون التركيز دائمًا على الكرة الصفراء.
ويعود سبب ذلك إلى أنّه أثناء تسجيل الأداء، يتحرك الجرم السماوي الأصفر من خلال مركز الغرفة بالتزامن مع الموسيقى ويكرّر ذلك. يمنح موقع الكرة المؤدي فكرة عن الوقت الذي مضى وبقي له في حلقة الالتفاف. ويوفّر لهم تركيزًا طبيعيًا ليقوموا ببناء أداء حوله.
2. وجهة نظر أخرى
لم نكن نريد استبعاد المستخدمين الذين لا يملكون أجهزة الواقع الافتراضي، خاصةً أنّهم يشكّلون جمهورنا الأكبر على الأرجح. وبدلاً من إنشاء تجربة واقع افتراضي زائفة، أردنا أن نمنح الأجهزة المستندة إلى الشاشة تجربتها الخاصة. لقد خطرت لنا فكرة عرض الأداء من الأعلى من منظور متساوي الأبعاد. يُعدّ هذا المنظور غنيًا بتاريخه في ألعاب الكمبيوتر. وقد تم استخدامه لأول مرة في لعبة Zaxxon، وهي لعبة إطلاق نار في الفضاء، في عام 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 for Firebase. عند اكتمال التحميل، يطلب جهاز العميل بدء مسار HTTP في وظائف السحابة الإلكترونية لبرنامج Firebase باستخدام رمز Firebase المميّز. يؤدي ذلك إلى تشغيل عملية في الخادم تتحقّق من عملية الإرسال وتنشئ سجلّ قاعدة بيانات وتنقل التسجيل إلى دليل عام على Google Cloud Storage.
يتم تخزين كل المحتوى العلني في سلسلة من الملفات المسطّحة في "حزمة" على Cloud Storage. وهذا يعني أنّه يمكن الوصول إلى بياناتنا بسرعة في جميع أنحاء العالم، ولا ينبغي أن نشعر بالقلق بشأن تأثير عدد الزيارات الكبير في مدى توفّر البيانات بأي شكل من الأشكال.
لقد استخدمنا قاعدة بيانات Firebase الآنية الاستجابة ونقاط نهاية وظائف Cloud لإنشاء أداة بسيطة للإشراف/التنسيق تتيح لنا مشاهدة كل محتوى جديد تم إرساله في الواقع الافتراضي ونشر قوائم تشغيل جديدة من أي جهاز.
7- مشغِّلو الخدمات
يُعدّ عاملو الخدمات ابتكارًا حديثًا إلى حدّ ما يساعد في إدارة عملية التخزين المؤقت لمواد عرض المواقع الإلكترونية. في حالتنا، تحمّل مهام الخدمة المحتوى بسرعة البرق لجذب مزيد من الزوّار المتكرّرين، كما تسمح للموقع الإلكتروني بالعمل بلا إنترنت. هذه ميزات مهمة لأنّ العديد من الزوّار سيستخدمون اتصالات جوّالة بجودة مختلفة.
كانت إضافة مهام الخدمة إلى المشروع سهلة بفضل مكوّن إضافي مفيد في Webpack يعالج معظم المهام الشاقة نيابةً عنك. في الإعداد أدناه، ننشئ عامل خدمة سيخزِّن تلقائيًا جميع ملفاتنا الثابتة. ستسحب الخدمة أحدث ملف قائمة تشغيل من الشبكة، إذا كان متاحًا، لأنّ قائمة التشغيل ستتم مراجعتها باستمرار. من المفترض أن يتم سحب جميع ملفات 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، نتطلع إلى رؤية المواد الإبداعية الجديدة والاتّجاهات غير المتوقّعة التي ستتخذها هذه الوسيلة الجديدة. الرقص