في هذا البرنامج التعليمي ، سأتحدث عن كيفية إعادة إنشاء تأثير رسومات الشعار المبتكرة الشائعة في الوحدة باستخدام التظليل. إذا كان أسلوبك يتطلب هذا النمط ، فسوف تتعلم من هذه المقالة كيفية تحقيقه دون تقديم مجموعة من الصور الإضافية.
على مدار الأعوام القليلة الماضية ، أصبح هذا النمط شائعًا بشكل متزايد ويستخدم بنشاط في ألعاب مثل
GoNNER و
Baba is You .
يغطي هذا البرنامج التعليمي كل ما تحتاجه ، بدءًا من أساسيات ترميز التظليل وحتى الرياضيات المستخدمة. في نهاية المقال ، يوجد رابط لتنزيل حزمة الوحدة الكاملة.
ألهمني نجاح
Doodle Studio 95 لإنشاء هذا البرنامج التعليمي
! .
مقدمة
في مدونتي ، أستكشف موضوعات معقدة للغاية ، من
رياضيات علم الحركة المعكوس إلى
تناثر رايلي الجوي . أود حقًا جعل مثل هذه المواضيع الصعبة مفهومة لجمهور واسع. لكن عدد الأشخاص المهتمين بهم والحصول على مستوى تقني كافٍ ليس كبيراً. لذلك ، يجب ألا تفاجأ بأن المقالات الأكثر شعبية هي أبسطها. ينطبق هذا أيضًا على
تغريدة حديثة لـ
Nick Caman والتي أظهر فيها كيفية إنشاء
تأثير رسومات الشعار المبتكرة في Unity.
بعد 1000 إعجابات و 4000 إعجابات ، أصبح من الواضح أن هناك طلبًا قويًا على برامج تعليمية أبسط يمكن دراستها حتى من قبل أشخاص ليس لديهم أي معرفة تقريبًا بإنشاء تظليل.
إذا كنت تبحث عن طريقة احترافية وفعالة لتحريك العفاريت ثنائية الأبعاد بدرجة عالية من التحكم الفني ، فإنني أوصي بشدة بـ
Doodle Studio 95! (انظر GIF أدناه).
هنا يمكنك إلقاء نظرة على بعض الألعاب التي تستخدم هذه الأداة.
خربش تأثير تشريح
لإعادة إنشاء تأثير رسومات الشعار المبتكرة ، نحتاج أولاً إلى فهم كيفية عمله والتقنيات المستخدمة فيه.
تأثير شادر. أولاً ، نريد أن يكون هذا التأثير بسيطًا قدر الإمكان ولا يتطلب نصوصًا إضافية. هذا ممكن بفضل استخدام
التظليلات التي تخبر Unity بكيفية تقديم النماذج ثلاثية الأبعاد (بما في ذلك النماذج المسطحة!) على الشاشة. إذا لم تكن معتادًا على عالم
ترميز التظليل ، فعليك دراسة مقالتي "
مقدمة لطيفة إلى تظليل" .
العفريت شادر. الوحدة تأتي مع العديد من أنواع التظليل. إذا كنت تستخدم أدوات Unity 2D المقدمة ، فمن الأرجح أنك تعمل مع
sprites . إذا كان الأمر كذلك ، فأنت بحاجة إلى
Sprite Shader - نوع خاص من التظليل المتوافق مع
SpriteRenderer
Unity. أو يمكنك البدء
بتظليل Unlit التقليدي.
تعويض فيرتكس. عند رسم العفاريت يدويًا ، لن يتزامن أي إطار مع الآخرين تمامًا. نريد أن نجعل العفريت "تمايل" بطريقة ما لمحاكاة هذا التأثير. تظليل لديك وسيلة فعالة للغاية للقيام بذلك ، وذلك باستخدام
إزاحة قمة الرأس . هذه تقنية تسمح لك بتغيير موضع رؤوس كائن ثلاثي الأبعاد. إذا قمنا بنقلها بشكل عشوائي ، فسوف نحصل على التأثير المطلوب.
وقت السفر. الرسوم المتحركة التي عادة ما تكون ذات معدل إطارات منخفض. إذا كنا نريد محاكاة ، على سبيل المثال ، خمسة إطارات في الثانية الواحدة ، فإننا نحتاج إلى تغيير موضع رؤوس العفاريت خمس مرات في الثانية. ومع ذلك ، من المحتمل أن تقوم Unity بتشغيل اللعبة بمعدل تحديث أعلى بكثير ؛ ربما مع 30 أو حتى 60 لقطة في الثانية الواحدة. بحيث لا يتغير sprite 60 مرة في الثانية ، فأنت بحاجة إلى العمل على مكون توقيت الحركة.
الخطوة 1: استكمال العفريت تظليل
إذا كنت ترغب في إنشاء تظليل جديد في الوحدة ، فسيكون الخيار محدودًا للغاية. أقرب تظليل يمكن أن نبدأ به سيكون
Unlit Shader ، على الرغم من أنه لا يناسب بالضرورة
أغراضنا .
إذا كنت تريد أن يكون تظليل doodle متوافق تمامًا مع
SpriteRenderer
Unity ، فإننا نحتاج إلى استكمال
Sprite Shader الحالي . لسوء الحظ ، لا يمكننا الوصول إليها مباشرة من الوحدة نفسها.
يمكنك الوصول إليها بالانتقال إلى صفحة
أرشيف تنزيل Unity وتنزيل حزمة
Build in shaders لإصدار Unity الذي تعمل معه. هذا ملف مضغوط يحتوي على الكود المصدري لجميع التظليلات التي تأتي مع بناء الوحدة الخاصة بك.
بعد التنزيل ، قم بفك ضغطه وابحث عن ملف
Sprites-Diffuse.shader
في المجلد
builtin_shaders-2018.1.6f1\DefaultResourcesExtra
. هذا هو الملف الذي سنستخدمه في البرنامج التعليمي.
Sprites-Diffuse ليس تظليل شبح قياسي!عند إنشاء نقش جديد ، تستخدم مادته القياسية Sprites-Diffuse.shader
Sprites-Default.shader
، وليس Sprites-Diffuse.shader
.
الفرق بين الاثنين هو أن الأول لا يستخدم الإضاءة ، والثاني يستجيب للإضاءة في المشهد. نظرًا لطبيعة تطبيق الوحدة ، فإن تحرير الإصدار أسهل بكثير من التحرير دون إضاءة.
في نهاية هذا البرنامج التعليمي ، يوجد رابط لتنزيل تظليل رسومات الشعار المبتكرة بدون إضاءة.
الخطوة 2: إزاحة القمم
داخل
Sprites-Diffuse.shader
هناك وظيفة تسمى
vert
، وهي
وظيفة قمة الرأس التي تحدثنا عنها أعلاه. اسمها غير مهم ، والشيء الرئيسي هو أنه يتزامن مع الاسم المحدد في قسم
vertex:
من
#pragma
:
#pragma surface surf Lambert vertex:vert nofog nolightmap nodynlightmap keepalpha noinstancing
باختصار ، يتم استدعاء وظيفة vertex لكل قمة من نموذج ثلاثي الأبعاد وتقرر كيفية تثبيتها على مساحة الشاشة ثنائية الأبعاد. في هذا البرنامج التعليمي ، نحن مهتمون فقط بكيفية تحريك كائن.
تحتوي المعلمة
appdata_full v
على حقل
vertex
الذي يحتوي على الموضع ثلاثي الأبعاد لكل قمة في
مساحة الكائن . عندما تتغير القيمة ، يتغير الرأس. وهذا هو ، على سبيل المثال ، الكود الموضح أدناه سينقل الكائن مع تظليله وحدة واحدة على طول المحور X.
void vert (inout appdata_full v, out Input o) { v.vertex = UnityFlipSprite(v.vertex, _Flip); v.vertex.x += 1; #if defined(PIXELSNAP_ON) v.vertex = UnityPixelSnap (v.vertex); #endif UNITY_INITIALIZE_OUTPUT(Input, o); o.color = v.color * _Color * _RendererColor; }
بشكل افتراضي ، لا تعمل الألعاب ثنائية الأبعاد التي تم إنشاؤها في Unity إلا مع محاور X و Y ، لذلك نحن بحاجة إلى تغيير
v.vertex.xy
لنقل العفريت على مستوى ثنائي الأبعاد.
ما هي مساحة الكائن؟يحتوي حقل vertex
في بنية appdata_full
على موضع قمة الرأس الحالية التي تتم معالجتها بواسطة التظليل في مساحة الكائن. هذا هو موضع قمة الرأس على افتراض أن الكائن موجود في وسط العالم (0،0،0) ، دون تغيير المقياس ودون دوران.
تعكس القمم المعبر عنها في الفضاء العالمي موقعها الحقيقي في مشهد الوحدة.
لماذا لا يتحرك الكائن بسرعة متر واحد لكل إطار؟إذا أضفنا 1+ إلى المكون x
من قيمة transform.position
في طريقة Update
للبرنامج النصي C # ، فسنرى كيف ينتقل الكائن إلى اليمين بسرعة 1 متر لكل إطار ، أي حوالي 216 كيلومتر في الساعة.
وذلك لأن التغييرات التي تقوم بها C # تُغيّر الموضع نفسه. في وظيفة قمة الرأس ، هذا لا يحدث. يغير التظليل فقط التمثيل البصري للنموذج ، لكنه لا يقوم بتحديث أو تعديل الرؤوس المخزنة للنموذج. هذا هو السبب في أن إضافة +1 إلى v.vertex.x
كائنًا بمتر واحد فقط.
تذكر أن استيراد العفريت كما ضيق!هذا التأثير يغير الجزء العلوي من العفريت. تقليديًا ، يتم استيراد العفاريت في الوحدة كوحدات رباعية (انظر الشكل على اليسار). هذا يعني أن لديهم أربعة قمم فقط. إذا كان الأمر كذلك ، عندئذٍ يمكن نقل هذه النقاط فقط ، مما يقلل من قوة تأثير رسومات الشعار المبتكرة.
للحصول على تشويه أقوى وأكثر واقعية ، تحتاج إلى استيراد العفاريت عن طريق اختيار
Tight لمعلمة
نوع الشبكة ، والتي تحولها إلى شكل محدب (انظر الشكل على اليمين).
هذا يزيد من عدد القمم. هذا ليس مرغوبًا فيه دائمًا ، ولكن هذا هو بالضبط ما نحتاجه الآن.
إزاحة عشوائية
يعمل تأثير رسومات الشعار المبتكرة بشكل عشوائي على تغيير موضع كل قمة. كان أخذ العينات
العشوائية في تظليل دائمًا مهمة صعبة. يحدث هذا بشكل أساسي بسبب بنية GPU الموزعة ، مما يعقد ويقلل من كفاءة إعادة إنشاء الخوارزمية المستخدمة في معظم المكتبات (بما في ذلك
Mathf.Random
).
استخدم منشور Nick Caman نسيجًا ضوئيًا ، عند أخذ العينات ، يعطي وهمًا عشوائيًا. في سياق مشروعك ، قد لا يكون هذا هو الأسلوب الأكثر فعالية ، لأنه في هذه الحالة يتضاعف عدد عمليات البحث عن الملمس التي أجراها التظليل.
لذلك ، في معظم التظليل ، يتم استخدام الوظائف المربكة والفوضوية ، والتي ، على الرغم من حتميتها ، يبدو لنا أنه ليس لدينا أي انتظام. ولأنه يجب توزيعها ، يجب إنشاء كل رقم عشوائي باستخدام نسخته الخاصة. هذا شيء عظيم بالنسبة لنا ، لأن موضع كل قمة يجب أن يكون فريدًا. يمكننا استخدام هذا لربط رقم عشوائي لكل قمة. سنناقش تنفيذ هذه الوظيفة العشوائية في وقت لاحق ؛ دعنا نسميها
random3
.
يمكننا استخدام
random3
لإنشاء إزاحة عشوائية لكل قمة. في المثال أدناه ، يتم تغيير الأرقام العشوائية باستخدام خاصية
_NoiseScale
، والتي تتيح لك التحكم في قوة الإزاحة.
void vert (inout appdata_full v, out Input o) { ... float2 noise = random3(v.vertex.xyz).xy * _NoiseScale; v.vertex.xy += noise; ... }
الآن نحن بحاجة إلى كتابة رمز
random3
.
العشوائية في تظليل
واحدة من أكثر الوظائف العشوائية الكاذبة والأيقونية المستخدمة في التظليل مأخوذة من مقال نشر عام 1998 من قبل دبليو راي تحت عنوان "
في توليد أرقام عشوائية ، بمساعدة y = [(a + x) sin (bx)] mod 1 ".
float rand(float2 co) { return fract(sin(dot(co.xy ,float2(12.9898,78.233))) * 43758.5453); }
هذه الوظيفة حتمية (أي أنها ليست عشوائية
حقًا ) ، لكنها تتصرف بشكل عشوائي بحيث تبدو عشوائية تمامًا. وتسمى هذه الوظائف
العشوائية الزائفة . من أجل تعليمي ، اخترت وظيفة أكثر تعقيدًا ، اخترعها
نيكيتا ميروبولسكي.توليد رقم عشوائي زائف في تظليل هو موضوع معقد للغاية. إذا كنت مهتمًا بمعرفة المزيد عنها ، فإن
The Book of Shaders لديها فصل جيد عنها. بالإضافة إلى ذلك ، قام
Patricio Gonzales Vivo ببناء مستودع كبير من الوظائف العشوائية الزائفة التي تسمى
GLSL noise ، والتي يمكن استخدامها في التظليل.
الخطوة 3: إضافة الوقت
بفضل الرمز الذي كتبناه ، يتم تغيير كل نقطة في كل إطار بنفس المقدار. حتى نحصل على العفريت مشوهة ، وليس لها تأثير خربش. لإصلاح ذلك ، تحتاج إلى إيجاد طريقة لتغيير التأثير مع مرور الوقت. واحدة من أسهل الطرق للقيام بذلك هي استخدام وضع قمة الرأس والوقت الحالي لإنشاء رقم عشوائي.
في حالتنا ، أضفت الوقت الحالي بالثواني
_Time.y
إلى موضع قمة الرأس.
float time = float3(_Time.y, 0, 0); float2 noise = random3(v.vertex.xyz + time).xy * _NoiseScale; v.vertex.xy += noise;
قد تتطلب الآثار الأكثر تعقيدًا طرقًا أكثر تطوراً لإضافة وقت للمعادلة. ولكن نظرًا لأننا مهتمون فقط بتأثير عشوائي متقطع ، فإن قيمتين أكثر من كافية.
وقت التبديل
المشكلة الرئيسية في إضافة
_Time.y
هي أنه يتسبب في تحريك العفريت في كل إطار. هذا أمر غير مرغوب فيه بالنسبة لنا ، لأن معظم الرسوم المتحركة المرسومة باليد لها معدل إطار منخفض. يجب ألا يكون المكون الزمني مستمرًا ، لكنه منفصل. هذا يعني أننا إذا أردنا عرض خمسة إطارات في الثانية ، فينبغي أن يتغير خمس مرات في الثانية فقط. وهذا هو ، يجب أن
يرتبط الوقت بخمس ثانية. يجب أن تكون القيم الصالحة الوحيدة
.
.
.
.
.
مع ، وهلم جرا ...
لقد غطيت بالفعل المفاجئة على مدونتي في
كيفية الانجذاب إلى الشبكة . في هذه المقالة ، اقترحت حلاً لمشكلة ربط موضع كائن على شبكة مكانية. إذا احتجنا إلى ربط الوقت بشبكة زمنية ، فإن الرياضيات ، وبالتالي الكود ، ستكون هي نفسها.
تأخذ الوظيفة الموضحة أدناه الرقم
x
وتربطه مع عدد صحيح من القيم هي مضاعفات
snap
.
inline float snap (float x, float snap) { return snap * round(x / snap); }
وهذا هو ، يصبح رمز لدينا مثل هذا:
float time = snap(_Time.y, _NoiseSnap); float2 noise = random3(v.vertex.xyz + float3(time, 0.0, 0.0) ).xy * _NoiseScale; v.vertex.xy += noise;
استنتاج
يمكن تنزيل حزمة Unity لهذا الغرض مجانًا
على Patreon .
موارد إضافية
خلال الأشهر القليلة الماضية ، ظهر عدد كبير من الألعاب بأسلوب رسومات الشعار المبتكرة. يبدو لي أن السبب في ذلك هو نجاح
Doodle Studio 95! - أداة للوحدة ، طورها
فرناندو رامالو . إذا كان هذا النمط مناسبًا للعبتك ، فإنني أوصي بشراء هذه الأداة الرائعة.