تنفيذ تأثير الألوان المائية في الألعاب

صورة

المقدمة


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


محاكاة لون الماء في الصبغة.

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

ما هي الصبغة.؟


صبغة . - هذه لعبة ألغاز تسمح للاعب بإكمال المستويات ، ومزج ألوان الألوان المائية بحيث تتطابق مع ألوان الأوريجامي. تم إصدار اللعبة في خريف عام 2019 في Apple Arcade لأجهزة iOS و macOS و tvOS.


لون لقطة الشاشة.

المتطلبات


يمكن تقسيم التقنية الموضحة في مقالتي إلى ثلاث مراحل رئيسية يتم إجراؤها في كل إطار:

  1. قم بإنشاء نقاط جديدة بناءً على إدخال اللاعب وإضافتها إلى قائمة النقاط
  2. محاكاة الطلاء لجميع البقع في القائمة
  3. تقديم بقعة

سنتحدث أدناه بالتفصيل عن كيفية تنفيذ كل مرحلة من المراحل.

هدفنا الوصول إلى 60 إطارًا في الثانية ، أي أن هذه المراحل وجميع المنطق الموصوف أدناه يتم إجراؤه 60 مرة في الثانية.

الحصول على المدخلات


في كل إطار ، نقوم بتحويل إدخال اللاعب (اعتمادًا على النظام الأساسي ، يمكن أن يكون لمسة أو موضع الماوس أو المؤشر الافتراضي) إلى بنية بيانات متقطعة تحتوي على الموضع ومتجه الحركة واللون والضغط (2). أولاً ، نتحقق من طول انتقاد اللاعب على الشاشة ونقارنه بقيمة عتبة معينة. مع الضربات الشديدة ، نقوم بإنشاء بقعة واحدة لكل إطار في موضع الإدخال. في الحالة المعاكسة ، نملأ المسافة بين نقطتي البداية والنهاية للتمرير السريع للاعب ببقع جديدة تم إنشاؤها بكثافة محددة مسبقًا (وهذا يضمن كثافة طلاء ثابتة بغض النظر عن سرعة التمرير). يشير اللون إلى الطلاء الحالي المستخدم ، ويشير منحدر الحركة إلى اتجاه التمرير السريع. تتم إضافة البقع الجديدة التي تم إنشاؤها إلى مجموعة تسمى splatList، الذي يحتوي أيضًا على جميع البقع التي تم إنشاؤها مسبقًا. يتم استخدامه لمحاكاة وتقديم الطلاء في الخطوات التالية. تشير كل نقطة على حدة إلى "قطرة" من الطلاء التي يجب تقديمها - وهي لبنة البناء الرئيسية للرسم بالألوان المائية. سيكون الرسم بالألوان المائية النهائية نتيجة عرض عشرات / مئات البقع المتقاطعة. بالإضافة إلى ذلك ، يتم تعيين قيمة العمر (في الإطارات) إلى البقعة التي تم إنشاؤها حديثًا ، والتي تحدد مدة محاكاة البقعة.


مثال على الاستيفاء للبقع الطويلة. تشير الدوائر المجوفة إلى البقع التي تم إنشاؤها على فترات منتظمة.

اللوحة القماشية


مثل الطلاء الحقيقي ، نحن بحاجة إلى قماش. لتنفيذه ، أنشأنا مساحة محدودة في مساحة ثلاثية الأبعاد تشبه ورقة. يتم تسجيل إحداثيات إدخال اللاعب وجميع العمليات الأخرى ، مثل عرض شبكة ، في مساحة اللوحة القماشية. وبالمثل ، يعتمد حجم وحدات البكسل لأي مخزن مؤقت يستخدم لمحاكاة الرسم على حجم اللوحة. لا يرتبط مصطلح "canvas" كما هو مستخدم في هذه المقالة بأي حال من الأحوال بفئة Canvas من Unity UI.


يظهر المستطيل الأخضر منطقة القماش في اللعبة

بقعة


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


أمثلة على الشبكات المتداخلة.

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


مثال على ناقلات البقعة المخزنة ببيانات موضعية جديدة.

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


Wetmap ملء ، بكسل داخل شكل بقعة (الدائرة الخضراء) بمناسبة wetmap عازلة (الشبكة)، والرطب (الخضراء). العازلة الرطبة نفسها لديها دقة أعلى بكثير.

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


مثال على الطلاء بدون التأفق (يسار) ومعه (يمين).


أمثلة على التأقلم.

دورة المحاكاة


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

في كل إطار ، نتجول حول قائمة البقع ونغير مواضع جميع رؤوس البقع باستخدام المعادلة التالية:


حيث: m هو متجه الحركة الجديد ، a هو معامل التصحيح الثابت (0.33) ، b هو متجه منحدر الحركة = الاتجاه الطبيعي لانتقاد اللاعب ، مضروبًا في 0.3 ، cr هي القيمة العددية لخشونة القماش = Random.Range (1،1 + r) ، r هي معلمة الخشونة العالمية ، بالنسبة للطلاء القياسي ، قمنا بتعيينه على 0.4 ، v هو ناقل السرعة الذي تم إنشاؤه مقدمًا مع الشبكة الموضعية ، vm هو عامل السرعة ، وهي قيمة قياسية نستخدمها محليًا في بعض المواقف لتسريع التأفق ، x (t + 1) - موضع القمة الجديد المحتمل ، x (t) - موضع القمة الحالي ، brهو متجه الخشونة المتفرعة = (Random.Range (-r، r)، Random.Range (-r، r))، w (x) هي قيمة الترطيب في المخزن المؤقت للطبعة الرطبة.

نتيجة هذه المعادلات تسمى المشي العشوائي المتحيز ، فهي تحاكي سلوك الجسيمات في طلاء الألوان المائية الحقيقية. نحن نحاول تحريك كل قمة من البقعة إلى الخارج من مركزها ( ت ) ، مع إضافة العشوائية. ثم يتغير اتجاه الحركة قليلاً مع اتجاه السكتة الدماغية ( ب ) ويتم اختياره عشوائيًا مرة أخرى بواسطة مكون خشونة آخر ( br ). ثم تتم مقارنة موضع القمة الجديد هذا بخريطة مبللة . إذا كانت اللوحة في الموضع الجديد مبللة بالفعل (القيمة في المخزن المؤقت للطبقة الرطبةأكبر من 0) ، ثم نعطي القمة موضعًا جديدًا x (t + 1) ، وإلا فإننا لا نغير موضعها. ونتيجة لذلك ، سينتشر الطلاء فقط في تلك المناطق من القماش التي كانت مبللة بالفعل. في المرحلة الأخيرة ، نقوم بإعادة حساب منطقة البقعة ، والتي يتم استخدامها في دورة التقديم لتغيير التعتيم.


مثال مصغر لمحاكاة التأفق بين نقطتين نشطتين من الطلاء.

دورة التقديم - العازلة الرطبة


بعد إعادة سرد البقع ، يمكنك البدء في عرضها. في المخرج بعد مرحلة المضاهاة ، غالبًا ما تتحول شبكة البقع إلى تشوه (على سبيل المثال ، تحدث تقاطعات) ، وبالتالي ، من أجل تقديمها الصحيح دون تكاليف إضافية للتثليث المتكرر ، نستخدم حلًا مع مخزن استنسل ثنائي التمريرات. يتم استخدام واجهة رسم Unity Graphics لتقديم البقع ، ويتم تنفيذ دورة التقديم داخل طريقة Unity OnPostRender . يتم تقديم الشبكات الموضعية لتقديم نسيج ( wetBuffer ) باستخدام كاميرا منفصلة. في بداية الدورة ، يتم مسح wetBuffer وتعيينه كهدف عرض باستخدام Graphics.SetRenderTarget (wetBuffer) . التالي لكل بقعة نشطة من splatList نقوم بتنفيذ التسلسل الموضح في الرسم البياني التالي:


رسم تخطيطي لدورة العرض.

نبدأ بتنظيف المخزن المؤقت للاستنسل قبل كل بقعة بحيث لا تؤثر حالة المخزن المؤقت للاستنسل في البقعة السابقة على البقعة الجديدة. ثم نختار المادة المستخدمة لرسم البقعة. هذه المادة مسؤولة عن لون البقعة ، ونختارها بناءً على فهرس الألوان المخزن في splatData عندما رسم المشغل البقعة. ثم نقوم بتغيير عتامة اللون (قناة ألفا) بناءً على مساحة شبكة التركيز المحسوبة في الخطوة السابقة. يتم تنفيذ التقديم نفسه باستخدام مظلل استنسل ثنائي التمرير. في التمرير الأول (Material.SetPass (0)) نقوم بتمرير شبكة التركيز الأصلية لتسجيل الإحداثيات التي يتم فيها ملء الشبكة. مع هذا تمرير ColorMaskتم تعيين قيمة 0 ، لذلك لا يتم تقديم الشبكة نفسها. في الممر الثاني (Material.SetPass (1)) نستخدم الرباعي الموصوف حول الشبكة الموضعية. نتحقق من القيمة في المخزن المؤقت الاستنسل لكل بكسل من رباعي الأضلاع ؛ إذا كانت القيمة واحدة ، يتم تقديم البكسل ، وإلا يتم تخطيها. نتيجة لهذه العملية ، نقدم نفس شكل الشبكة الموضعية ، لكنها بالتأكيد لن تحتوي على قطع أثرية غير مرغوب فيها ، على سبيل المثال ، التقاطعات الذاتية.


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


مثال على ثلاث بقع متقاطعة تم تقديمها بالطريقة التقليدية ، مما أدى إلى ظهور القطع الأثرية (يسار) ، واستخدام تقنية المخزن المؤقت لاستنسل التمريرين مع التخلص من جميع القطع الأثرية (يمين).

بعد عرض جميع البقع في wetBuffer ، يتم عرضها في مشهد اللعبة. تستخدم لوحة الرسم لدينا تظليلًا مؤقتًا يجمع بين wetBuffer وخريطة ورقية منتشرة وخريطة عادية للورق.


تظليل قماش: فقط wetBuffer (يسار) ، إضافة نسيج الورق (الوسط) ، إضافة خريطة عادية (يمين).

تدعم اللعبة وضعًا للأشخاص الذين يعانون من عمى الألوان ، حيث يتم فرض أنماط منفصلة فوق الطلاء. لتحقيق ذلك ، قمنا بتغيير مادة البقع عن طريق إضافة نسيج النمط بالبلاط. تتبع الأنماط قواعد خلط ألوان اللعبة ، على سبيل المثال ، الأزرق (أشرطة) + أصفر (دوائر) تعطي الأخضر (دوائر في الأشرطة) عند التقاطع. لمزج الأنماط بسلاسة ، يجب تقديمها في نفس مساحة الأشعة فوق البنفسجية. نقوم بتعديل إحداثيات الأشعة فوق البنفسجية للربع الرباعي المستخدم في الممر الثاني من المخزن المؤقت الاستنسل ، وتقسيم مواضع x و y (المحددة في مساحة اللوحة القماشية) على عرض اللوحة وارتفاعها. نتيجة لذلك ، نحصل على قيم u ، v الصحيحة في الفضاء من 0 إلى 1.


مثال على أنماط عمى الألوان.

التحسين - العازلة البقع الجافة


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

DryBufferهو نسيج تجسيد إضافي يضاف إلى دورة التجسيد. كما ذكرنا سابقًا ، لكل بقعة عمر (في الإطارات) ، والذي ينقص مع كل إطار. بعد أن يصل عمر 0 ​​، تعتبر البقعة "جافة". لم تعد تتم محاكاة البقع الجافة ، ولا يتغير شكلها ، وبالتالي لا يلزم عرضها مرة أخرى في كل إطار.


DryBuffer في العمل ؛ تظهر البقع الرمادية البقع التي تم نسخها إلى dryBuffer.

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

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


مزيج من البقع الرطبة والجافة. يتراكم

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


بفضل التحسين مع dryBuffer ، لم يعد لدينا حدود على كمية الطلاء التي يمكن للاعب تطبيقها على اللوحة. يسمح

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

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


عرض أقصى 40 نقطة نشطة (أعلى) و 80 (أسفل) نشطة. تظهر البقع المجففة المنسوخة في dryBuffer باللون الرمادي. تشير القيمة إلى "كمية" الطلاء التي يمكن محاكاتها في نفس الوقت.

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

استنتاج


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


لقطة شاشة للعبة تعمل على Apple TV

(1) S. DiVerdi، A. Krishnaswamy، R. MÄch and D. Ito، "Painting with Polygons: A الداخلي Watercolor Engine"، in IEEE Transactions on Visualization and Computer Graphics، vol. 19 ، لا. 5 ، ص. 723-735 ، مايو 2013. doi: 10.1109 / TVCG.2012.295

(2) لا يؤخذ الضغط في الاعتبار إلا عند رسم قلم Apple على جهاز iPad.

All Articles