GPU منضم. الجزء الثاني غابة لا نهاية لها



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

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

لذلك ، المهمة: تحتاج إلى رسم الكثير من الغابات. اللعبة لدينا إستراتيجية في الوقت الفعلي (RTS) بمستويات كبيرة (30 × 30 كم) ، وهذا يحدد المتطلبات الأساسية لنظام التقديم:

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

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

أداء


دعونا نرى لبداية ما إذا كان كل شيء سيء للغاية مع كائنات speedtree العادية:



هنا حوالي 2000 شجرة على المسرح. كل شيء يتماشى مع التقديم ، حيث يتم دمج الأشجار في مجموعات ، لكن مع وحدة المعالجة المركزية ، كل شيء سيء. يتم تبريد نصف الوقت الذي يتم فيه عرض الكاميرا. ونحن بحاجة إلى مئات الآلاف. نحن بالتأكيد نرفض GameObjects ، لكننا نحتاج الآن إلى الكشف عن هيكل نموذج SpeedTree ، آلية تبديل LODs والقيام بكل شيء باستخدام المقابض.

تتكون شجرة SpeedTree من LODs (عادة 4) ، آخرها عبارة عن لوحة ، وكل الباقي عبارة عن هندسة بدرجات متفاوتة من التفاصيل. كل واحد منهم يتكون من عدة صابش ، بمواده الخاصة:


هذه ليست خصوصية SpeedTree. أي هيكل يمكن أن يكون مثل هذا الهيكل. يتم تطبيق LOD التبديل في وضعين المتاحة:

  1. الصليب تتلاشى:

  2. شجرة السرعة:


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

SpeedTree (LOD_FADE_PERCENTAGE) مصنوع خصيصًا للأشجار. بالإضافة إلى الإحداثيات الرئيسية ، يتم تسجيل الإحداثيات الإضافية لموضع رؤوس المبتدئين فيما يتعلق بمستوى LOD الحالي في هندسة الأوراق والأغصان والجذع. درجة الانتقال من مستوى إلى آخر هي قيمة الوزن للاستيفاء الخطي لهذين الموقفين. يتم الانتقال إلى / من لوحة الإعلانات باستخدام طريقة CrossFade.

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

مولد الغابات


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



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

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

إعدام ولود التبديل


معرفة موقع ونوع الشجرة لا يكفي لجعل عرض فعال. من الضروري تحديد كل إطار من الكائنات المرئية وأي LOD (مع مراعاة منطق الانتقال) لإرساله إلى التجسيد.

سوف تظليل حساب خاص أيضا القيام بذلك. لكل كائن ، يتم تنفيذ Frustum Culling لأول مرة:


إذا كان الكائن مرئيًا ، فسيتم تنفيذ منطق تبديل LOD. وفقًا للحجم على الشاشة ، نحدد مستوى LOD المطلوب. إذا تم ضبط وضع CrossFade على قائمة LOD الخاصة بالمجموعة ، فإننا نزيد وقت الانتقال للتردد. إذا كانت SpeedTree Percentage ، فسننظر في قيمة الانتقال المقيسة بين LODs.

تحتوي واجهات برمجة التطبيقات الرسومية الحديثة على وظائف رائعة تتيح تمرير معلومات إرسال الرسم إلى استدعاء السحب في المخزن المؤقت لحساب (على سبيل المثال ، ID3D11DeviceContext :: DrawIndexedInstancedIndirect لـ D3D11). هذا يعني أنه يمكنك ملء هذا المخزن المؤقت لحساب GPU كذلك. وبالتالي اتضح إنشاء نظام مستقل تمامًا عن وحدة المعالجة المركزية (حسنًا ، ما عليك سوى الاتصال بـ Graphics.DrawMeshInstancedIndirect). في حالتنا ، من الضروري فقط تسجيل عدد حالات كل sabmesh. باقي المعلومات (عدد الفهارس في الشبكة والإزاحة) ثابت.

يتم تقسيم المخزن المؤقت لحساب ، مع وسيطات لاستدعاء السحب ، إلى أقسام ، كل منها مسؤولة عن استدعاء تقديم التقديم. في التظليل الحسابي للشبكة المراد رسمها في الإطار الحالي ، قم بزيادة قيمة InstanceCount المقابلة.

إليك كيف يبدو في العرض:


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

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

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

CBUFFER_START(SpeedTreeWind) float4 _ST_WindVector; float4 _ST_WindGlobal; float4 _ST_WindBranch; float4 _ST_WindBranchTwitch; float4 _ST_WindBranchWhip; float4 _ST_WindBranchAnchor; float4 _ST_WindBranchAdherences; float4 _ST_WindTurbulences; float4 _ST_WindLeaf1Ripple; float4 _ST_WindLeaf1Tumble; float4 _ST_WindLeaf1Twitch; float4 _ST_WindLeaf2Ripple; float4 _ST_WindLeaf2Tumble; float4 _ST_WindLeaf2Twitch; float4 _ST_WindFrondRipple; float4 _ST_WindAnimation; CBUFFER_END 

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

لذلك ، ترفرف الأشجار في مهب الريح ، لكن المنظر العلوي للوحات الإعلانية يكتنفها الكآبة:


مع وجود خيار التظليل BILLBOARD_FACE_CAMERA_POS:


نحتاج إلى لوحات إعلانية أفقية (من أعلى إلى أسفل). هذه هي ميزة SpeedTree القياسية منذ وقت King Pea ، ولكن إذا حكمنا من خلال المنتديات ، فهي لا تزال غير مطبقة في Unity. منشور من منتدى SpeedTree الرسمي: "لم يستخدم تكامل Unity لوحة الإعلانات الأفقية أبدًا." سوف نربط أيدينا. الهندسة نفسها سهلة الصنع. كيف تعرف إحداثيات الأشعة فوق البنفسجية لعفريت في الأطلس لها؟


نحصل على SDK SpeedTreeRT القديمة ، ونجد الهيكل في الوثائق:

 struct SBillboard { bool m_bIsActive; const float* m_pTexCoords; const float* m_pCoords; float m_fAlphaTestValue; }; 

"يشير M_pTexCoords إلى مجموعة من إحداثيات نسيج 4 (s، t) التي تحدد الصور المستخدمة في لوحة الإعلانات. يحتوي m_pTexCoords على 8 إدخالات. "، كما يقول بلغة أجنبية. حسنًا ، سوف نبحث عن سلسلة من 4 قيم للفاصلة العائمة في ملف spm ثنائي ، كل واحدة منها تقع في النطاق [0..1]. من خلال أسلوب البحث العلمي ، نكتشف أن التسلسل المرغوب هو أمام كتلة من 12 تعويم مع وجود علامات مطابقة للنمط:

 float signs[] = { -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1 }; 

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

 Azalea_Desktop.spm: 0, 1, 0.333333, 1, 0.333333, 0.666667, 0, 0.666667, Azalea_Desktop_Flowers_1.spm: 0, 1, 0.333333, 1, 0.333333, 0.666667, 0, 0.666667, Azalea_Desktop_Flowers_2.spm: 0, 1, 0.333333, 1, 0.333333, 0.666667, 0, 0.666667, Leaf_Map_Maker_Desktop_1_Modeler_Use_Only.spm: Pattern not found! Leaf_Map_Maker_Desktop_2_Modeler_Use_Only.spm: Pattern not found! BarrelCactus_Cluster_Desktop_1.spm: 0, 0.592376, 0.407624, 0.592376, 0.407624, 0.184752, 0, 0.184752, BarrelCactus_Cluster_Desktop_2.spm: 0, 1, 0.499988, 1, 0.499988, 0.500012, 0, 0.500012, BarrelCactus_Desktop_1.spm: 0, 0.2208, 0.220748, 0.2208, 0.220748, 5.29885e-05, 0, 5.29885e-05, BarrelCactus_Desktop_2.spm: 0, 1, 0.301392, 1, 0.301392, 0.698608, 0, 0.698608, 

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

الآن مثل هذا:


لا يزال غير جدا. باستخدام عتبة اختبار ألفا ، سنعمل على إخفاء لوحة الإعلانات العمودية في التسجيلات من الزاوية إلى الكاميرا:



النتائج

يعرض منشئ ملفات التعريف ديناميكية (عدد الأشياء التي يتم تقديمها) وإحصائيات ثابتة (عدد الكائنات ومعلماتها في المشهد):


حسنًا ، الفيديو الجميل النهائي (النصف الثاني يوضح تبديل مستويات الجودة):


ما لدينا في النهاية:

  • النظام هو وحدة المعالجة المركزية مستقلة تماما.
  • إنه يعمل بسرعة.
  • يستخدم أصول SpeedTree الجاهزة ، والتي يمكنك شراؤها على الإنترنت.
  • بالطبع ، لقد صنعت صديقاتها مع أي LODGroup ، وليس فقط SpeedTree. الكثير من الحصى ممكنة الآن.

من بين أوجه القصور التي يمكن ملاحظتها عدم وجود إعدام انسداد وما زالت اللوحات الإعلانية غير معبرة للغاية.

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


All Articles