نسيج تلافيفي

القوام التحديث الذاتي


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


الشكل 1: الإلتواء المزدوج التخزين المؤقت

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

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

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

أمثلة تطبيق بسيط


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

  • إذا كان للخلية الحية أقل من اثنين من الجيران ، فإنها تصبح ميتة.
  • إذا كان للخلية الحية اثنان أو ثلاثة من الجيران الأحياء ، فإنها تظل حية.
  • إذا كان للخلية الحية أكثر من ثلاثة من الجيران الأحياء ، فإنها تصبح ميتة.
  • إذا كان للخلية الميتة ثلاثة جيران أحياء ، فإنها تصبح حية.

لتنفيذ هذه اللعبة كملمس تلافيفي ، أفسر النسيج على أنه شبكة اللعبة ، ويتم تقديم التظليل استنادًا إلى القواعد المذكورة أعلاه. البيكسل الشفاف عبارة عن خلية ميتة ، والبيكسل الأبيض المعتم هو خلية حية. يظهر تنفيذ تفاعلي أدناه. للوصول إلى GPU ، أستخدم myr.js ، والذي يتطلب WebGL 2 . يمكن لمعظم المتصفحات الحديثة (على سبيل المثال ، Chrome و Firefox) التعامل معها ، ولكن إذا لم يعمل العرض التوضيحي ، فمن المرجح أن المتصفح لا يدعمه. استخدم الماوس (أو الشاشة التي تعمل باللمس) [في المقالة الأصلية] لرسم خلايا حية على النسيج.


يظهر أدناه رمز تظليل الأجزاء (في GLSL ، لأنني أستخدم WebGL للتجسيد). أولاً ، أقوم بتنفيذ وظيفة get ، والتي تسمح لي بقراءة بكسل من إزاحة محددة من الحالية. متغير pixelSize عبارة عن ناقل ثنائي الأبعاد محدد مسبقًا يحتوي على إزاحة UV لكل بكسل ، وتستخدمه الدالة get لقراءة الخلية المجاورة. ثم تحدد الوظيفة main اللون الجديد للخلية بناءً على الحالة الحالية ( live ) وعدد الجيران الأحياء.

 uniform sampler2D source; uniform lowp vec2 pixelSize; in mediump vec2 uv; layout (location = 0) out lowp vec4 color; int get(int dx, int dy) { return int(texture(source, uv + pixelSize * vec2(dx, dy)).r); } void main() { int live = get(0, 0); int neighbors = get(-1, -1) + get(0, -1) + get(1, -1) + get(-1, 0) + get(1, 0) + get(-1, 1) + get(0, 1) + get(1, 1); if (live == 1 && neighbors < 2) color = vec4(0); else if (live == 1 && (neighbors == 2 || neighbors == 3)) color = vec4(1); else if (live == 1 && neighbors == 3) color = vec4(0); else if (live == 0 && neighbors == 3) color = vec4(1); else color = vec4(0); } 

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

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

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


خطوة واحدة إلى الأمام


قناةتطبيق
أحمرارتفاع الموجة
أخضرسرعة الموجة
أزرقغير مستخدم
ألفاغير مستخدم

الشكل 2: موجات بكسل.

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

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

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


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

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

نحن نستخدم جميع القنوات


قناةتطبيق
أحمرX تعويض
أخضرإزاحة Y
أزرقسرعة X
ألفاإزاحة Y

الشكل 3: بكسل العشب.

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

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

يمكن العثور على الكود المصدري للتطبيق هنا .


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

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

حالات الاستخدام والعيوب الأخرى


يمكنك الخروج بالعديد من التطبيقات الأخرى للتكنولوجيا ، على سبيل المثال:

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

عند استخدام هذه الطريقة ، هناك صعوبات وقيود:

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

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

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

استنتاج


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

Source: https://habr.com/ru/post/ar478772/


All Articles