لدى العديد من الأشياء في ثلاثة. JS خاصية احتياجات ، ونادراً ما يتم كتابتها في المستندات (ولكن لا توجد العديد من المستندات في ثلاثة. إنهم لا يعرفون كيفية كتابة هذا في العديد من البرامج التعليمية عبر الإنترنت ، لأنه بالنسبة للبرامج التمهيدية البسيطة ، لا يمكن استخدام هذه الخاصية.
إذن ما هي هذه السمة المستخدمة؟ باختصار ، أخبر العارض أنه يجب عليّ تحديث ذاكرة التخزين المؤقت في هذا الإطار. على الرغم من أنه من السهل جدًا استخدامها كبضع علم ، لأنك بحاجة إلى معرفة سبب حاجتك إلى تحديث ذاكرة التخزين المؤقت وأي مخبئي يتم تحديثه ، لا يزال من الضروري فهمها بعناية.
لماذا الاحتياجاتبادئ ذي بدء ، دعونا نلقي نظرة على سبب حاجة ذاكرة التخزين المؤقت. إن وجود ذاكرة التخزين المؤقت بشكل عام هو تقليل عدد أوقات نقل البيانات ، وبالتالي تقليل الوقت الذي تقضيه في نقل البيانات. هنا ، صحيح أيضًا أنه ليس من السهل على كائن (شبكة) عرضه بنجاح على الشاشة في النهاية. يجب نقلها إلى ساحة المعركة ثلاث مرات.
الأول هو قراءة جميع بيانات Vertex وبيانات الملمس من القرص المحلي إلى الذاكرة من خلال البرنامج.
بعد ذلك ، بعد أن يقوم البرنامج بالمعالجة المناسبة في الذاكرة ، يحتاج إلى نقل بيانات Vertex وبيانات الملمس للكائنات التي تحتاج إلى رسمها إلى الشاشة إلى ذاكرة الفيديو.
أخيرًا ، عند تقديم كل إطار ، يتم مسح بيانات Vertex وبيانات الملمس في ذاكرة الفيديو في وحدة معالجة الرسومات للتجميع والرسم.
وفقًا لنموذج نقل البيانات الذي يشبه الهرم ، من الواضح أن الخطوة الأولى هي الأبطأ. إذا تم نقلها عبر الشبكة في بيئة مثل WebGL ، فسيكون ذلك أبطأ. والثاني هو الوقت من الذاكرة إلى ذاكرة الفيديو ، والتي ستكون اختبار بيانات بسيط لاحقًا.
ثم هناك تواتر استخدام هذه الخطوات الثلاث. بالنسبة للسيناريوهات الصغيرة ، فإن الخطوة الأولى هي لمرة واحدة ، أي في كل مرة يتم فيها تهيئة البرنامج ، سيتم تحميل جميع بيانات السيناريو في الذاكرة. بالنسبة للسيناريوهات الكبيرة ، قد يتم إجراء بعض التحميل غير المتزامن ، لكن هذا ليس هو المشكلة التي ندرسها حاليًا. يجب أن يكون تواتر الخطوة الثانية هو أهم شيء للحديث عن هذا الوقت. أولاً ، اكتب برنامجًا بسيطًا لاختبار الاستهلاك الناجم عن القيام بخطوة الإرسال هذه.
var canvas = document.createElement ('canvas') ؛
var _gl = canvas.getContext ('التجريبية webgl') ؛
VAR VERTICES = [] ؛
لـ (var i = 0 ؛ i <1000*3 ؛ i ++) {
vertices.push (i * math.random ()) ؛
}
var buffer = _gl.createBuffer () ؛
console.profile ('buffer_test') ؛
bindbuffer () ؛
console.profileend ('buffer_test') ؛
وظيفة bindbuffer () {
لـ (var i = 0 ؛ i <1000 ؛ i ++) {
_gl.bindbuffer (_gl.array_buffer ، buffer) ؛
_gl.bufferdata (_gl.array_buffer ، new float32array (رؤوس) ، _gl.static_draw) ؛
}
}
دعونا نشرح هذا البرنامج بإيجاز أولاً. القمم هي صفيف يحفظ القمم. هنا ، يتم إنشاء 1000 قمة بشكل عشوائي. نظرًا لأن كل قمة تحتوي على ثلاثة إحداثيات x و y و z ، هناك حاجة إلى مجموعة من 3000 حجم. يفتح الأمر _gl.createBuffer ذاكرة التخزين المؤقت لتخزين بيانات Vertex في ذاكرة الفيديو ، ثم يستخدم _gl.bufferdata لنقل بيانات الرأس التي تم إنشاؤها من الذاكرة إلى ذاكرة الفيديو. نحن هنا نفترض أن هناك 1000 كائن مع 1000 قمة في مشهد ما ، كل قمة هي 3 بايت 32 بت 4 بايت من البيانات العائمة. احسب بيانات ما يقرب من 1000 × 1000 × 12 = 11m. الملف الشخصي يستغرق حوالي 15 مللي ثانية. هنا قد نرى كيف أن 15 مللي ثانية هو فقط القليل من الوقت. ومع ذلك ، بالنسبة لبرنامج في الوقت الفعلي ، إذا كنت ترغب في التأكد من معدل إطار 30 إطارًا في الثانية ، فيجب التحكم في الوقت اللازم لكل إطار في حوالي 30 مللي ثانية. كيف يمكن أن يستغرق الأمر نصف الوقت لمجرد نقل البيانات؟ يجب أن تعلم أن الرأس الكبير يجب أن يكون عمليات الرسم في وحدة معالجة الرسومات والمعالجة المختلفة في وحدة المعالجة المركزية ، ويجب أن تكون بخيل مع كل خطوة من العملية في عملية التقديم بأكملها.
لذلك ، يجب تقليل عدد عمليات الإرسال في هذه الخطوة. في الواقع ، يمكن استخدامه لنقل جميع بيانات Vertex وبيانات الملمس من الذاكرة إلى ذاكرة الفيديو عند تحميلها. هذا ما يفعله Three.js الآن. يتم نقل بيانات Vertex للكائن الذي يجب رسمه إلى ذاكرة الفيديو لأول مرة ، وتخزين المخزن المؤقت للهندسة .__ WebGlverTexBuffer. بعد ذلك ، في كل مرة ترسم فيها ، ستحكم على خاصية هندسة VerticesNeedupDate. إذا كنت لا تحتاج إلى التحديث ، فاستخدم ذاكرة التخزين المؤقت الحالية مباشرة. إذا رأيت أن VerticesNeedupate صحيح ، فسيتم نقل بيانات Vertex في الهندسة إلى الهندسة .__ WebGlverTexBuffer. بشكل عام ، لا نحتاج إلى هذه الخطوة للأشياء الثابتة. ومع ذلك ، إذا واجهنا كائنات تتغير بشكل متكرر ، مثل استخدام رؤوس مثل أنظمة الجسيمات ، والشبكة التي تستخدم الرسوم المتحركة للهيكل العظمي ، فإن هذه الكائنات ستغير رؤوسها في كل إطار ، لذلك يحتاج كل إطار إلى تعيين خاصية VerticesNeedupDate إلى True لإخبار العارض بأنني بحاجة إلى إعادة إرسال البيانات!
في الواقع ، في برامج WebGL ، سيتم تغيير المزيد من مواقف قمة الرأس في قمة التظليل لإكمال تأثيرات الجسيمات والرسوم المتحركة للهيكل العظمي. على الرغم من أنه من الأسهل التوسع إذا تم وضعه على جانب وحدة المعالجة المركزية للحساب ، نظرًا لقيود قوة الحوسبة في JavaScript ، سيتم وضع المزيد من هذه العمليات الحسابية على جانب GPU. في هذه الحالة ، ليست هناك حاجة لإعادة إرسال بيانات قمة الرأس ، وبالتالي لا يتم استخدام الحالة أعلاه كثيرًا في البرنامج الفعلي ، ويتمثل الأمر أكثر حول تحديث الملمس وذاكرة التخزين المؤقت للمواد.
تصف الحالة أعلاه بشكل أساسي سيناريو يتم فيه إرسال بيانات قمة الرأس. بالإضافة إلى بيانات Vertex ، هناك أيضًا رأس كبير هو الملمس. يجب أن تشغل نسيج تنسيق R8G8B8A8 بحجم 1024*1024 حجم ذاكرة يصل إلى 4 أمتار ، لذلك انظر إلى المثال التالي.
var canvas = document.createElement ('canvas') ؛
var _gl = canvas.getContext ('التجريبية webgl') ؛
var texture = _gl.createTexture () ؛
var img = صورة جديدة ؛
img.onload = function () {
Console.profile ('testure test') ؛
bindTexture () ؛
console.profileend ('testure test') ؛
}
img.src = 'test_tex.jpg' ؛
وظيفة bindTexture () {
_gl.bindtexture (_gl.texture_2d ، نسيج) ؛
_gl.teximage2d (_gl.texture_2d ، 0 ، _gl.rgba ، _gl.rgba ، _gl.unsigned_byte ، img) ؛
}
ليست هناك حاجة لتكرار 1000 مرة المنحرفة هنا. يتطلب الأمر 30 مللي ثانية لإرسال نسيج 10241024 في وقت واحد ، وصورة 256256 حوالي 2 مللي ثانية. لذلك ، في ثلاثة. js ، يجب أن يتم نقل الملمس إلا مرة واحدة في البداية. بعد ذلك ، إذا لم يتم تعيين خاصية texture.needsupdate يدويًا على True ، فسيتم استخدام الملمس الذي تم نقله إلى ذاكرة الفيديو مباشرة.
ما الذي يجب تحديثه لخزات التخزين المؤقتيصف ما ورد أعلاه سبب احتياجات Three.js إلى إضافة سمة الاحتياجات هذه من خلال حالتين. بعد ذلك ، قم بإدراج العديد من السيناريوهات التي يجب معرفةها تحت الظروف التي تحتاجها لتحديث هذه التخزين المؤقت يدويًا.
تحميل غير متزامن من القوامهذه حفرة صغيرة ، لأن الصورة الأمامية يتم تحميلها بشكل غير متزامن. إذا قمت بكتابة نسيج. needsupdate = صحيح مباشرة بعد إنشاء IMG ، سيستخدم عارض Three.js _gl.teximage2d لنقل بيانات الملمس الفارغة إلى ذاكرة الفيديو في هذا الإطار ، ثم تعيين هذه العلامة على خطأ. ثم ، عند تحميل الصورة ، لن يتم تحديث بيانات ذاكرة الفيديو. لذلك ، يجب أن تنتظر تحميل الصورة بأكملها في حدث Onload قبل كتابة الملمس. needsupdate = True
ملمس الفيديوتشبه معظم القوام تمامًا أن يتم تحميل الصور ونقلها مباشرةً ، ولكن ليس لقوام الفيديو ، لأن الفيديو عبارة عن دفق صورة ، والصورة التي سيتم عرضها في كل إطار مختلفة ، لذلك تحتاج إلى تعيين الاحتياجات إلى صواب لكل إطار لتحديث بيانات الملمس في بطاقة الرسومات.
استخدم Render Bufferالعازلة العازلة هي كائن خاص نسبيا. بشكل عام ، سوف يتدفق البرنامج مباشرة على الشاشة بعد رسم المشهد بأكمله. ومع ذلك ، إذا كان هناك المزيد من المعالجة بعد المعالجة أو XXX المستند إلى الشاشة (مثل الحدوث المحيط القائم على الشاشة) ، فيجب رسم المشهد أولاً على المخزن المؤقت. هذا المخزن المؤقت هو في الواقع نسيج ، ولكن يتم إنشاؤه بواسطة الرسم السابق ، وليس تحميله من القرص. هناك كائن خاص ملمس WebGlrenderTarget في Three.js لتهيئة وحفظ RenderBuffer. يجب أيضًا ضبط هذا الملمس على صحيح في كل إطار.
احتياجات الموادتم وصف المادة في ثلاثة. js من خلال ثلاثة. في الواقع ، لا تحتوي المادة على الكثير من البيانات التي يجب نقلها ، ولكن لماذا تحتاج إلى تحقيق احتياجات؟ هنا سأتحدث عن التظليل. تتم ترجمة Shader على أنها تظليل ، والذي يوفر إمكانية برمجة رؤوس وبكسلات في وحدة معالجة الرسومات. هناك مصطلح مظلل في الطلاء لتمثيل الطريقة الخفيفة والظلام للطلاء. تظليل في وحدة معالجة الرسومات متشابهة. يتم حساب ضوء وظلام الضوء بواسطة البرنامج للتعبير عن مادة كائن ما. حسنًا ، نظرًا لأن Shader عبارة عن برنامج يعمل على وحدة معالجة الرسومات ، مثل جميع البرامج ، من الضروري إجراء عملية تجميع وربط. في WebGL ، يتم تجميع برنامج Shader في وقت التشغيل ، والذي يستغرق بالطبع وقتًا ، لذلك من الأفضل تجميعه وتشغيله حتى نهاية البرنامج. لذلك عندما يتم تهيئة المواد في Three.js ، يتم تجميع برنامج التظليل وربطه ويتم الحصول على كائن البرنامج بعد تخزين الرابط الترجمة. بشكل عام ، لا تحتاج المادة إلى إعادة ترجمة التظليل بأكمله بعد الآن. لضبط المادة ، تحتاج فقط إلى تعديل المعلمات الموحدة للظلال. ومع ذلك ، إذا استبدلت المادة بأكملها ، مثل استبدال Pong Shader الأصلي بتظليل Lambert ، فأنت بحاجة إلى تعيين material.needsupdate إلى true لإعادة الترجمة. ومع ذلك ، فإن هذا الموقف نادر ، والأكثر شيوعًا هو الوضع المذكور أدناه.
أضف وإزالة الأنواريجب أن يكون هذا أكثر شيوعًا في المشهد. ربما الكثير من الناس الذين بدأوا لتوه في استخدام ثلاثة. JS سوف يسقطون في هذه الحفرة. بعد إضافة ضوء إلى المشهد ديناميكيًا ، يجدون أن الضوء لا يعمل. ومع ذلك ، عند استخدام التظليل المدمج في Three.js ، على سبيل المثال ، Phong ، Lambert ، بالنظر إلى الكود المصدر في العارض ، ستجد أن Three.js يستخدم #Define في رمز التظليل المدمج لتعيين عدد الأضواء في المشهد. يتم الحصول على قيمة هذا #define عن طريق تظليل الربط السلسلة في كل مرة يتم تحديث المادة. الرمز كما يلي.
"#define max_dir_lights" + parameters.maxdirlights ،
"#define max_point_lights" + parameters.maxpointlights ،
"#define max_spot_lights" + parameters.maxspotlights ،
"#define max_hemi_lights" + parameters.maxhemilights ،
في الواقع ، يمكن أن تقلل طريقة الكتابة هذه بشكل فعال من استخدام سجلات GPU. إذا كان هناك ضوء واحد فقط ، فيمكنك إعلان فقط المتغير الموحد المطلوب لضوء واحد. ومع ذلك ، عندما يتغير عدد الأضواء ، خاصة عند الإضافة ، تحتاج إلى إعادة تجميع وتجميع وربط التظليل. في هذا الوقت ، تحتاج أيضًا إلى تعيين material.needsupdate من جميع المواد إلى True ؛
تغيير الملمسلا يعني تغيير الملمس هنا تحديث بيانات الملمس ، بل أن المادة الأصلية استخدمت الملمس ، ولكن لم يتم استخدامها لاحقًا ، أو لم تستخدم المادة الأصلية الملمس ، ثم أضافتها. إذا لم يتم تحديث المادة يدويًا ، فسيكون التأثير النهائي مختلفًا عما تفكر فيه. يشبه سبب هذه المشكلة الإضاءة المذكورة أعلاه ، وذلك أيضًا بسبب إضافة ماكرو إلى التظليل لتحديد ما إذا كان قد تم استخدام الملمس.
المعلمات. "#define use_map": "" ،
المعلمات. "#define use_envmap": "" ،
المعلمات. "#define use_lightmap": "" ،
المعلمات. "#define use_bumpmap": "" ،
المعلمات. normalmap؟ "#define use_normalmap": "" ،
المعلمات. "#define use_specularmap": "" ،
لذلك ، في كل مرة تقوم فيها خريطة أو envmap أو lightmap بتغيير القيمة الحقيقية ، تحتاج إلى تحديث المادة
التغييرات في بيانات قمة الرأس الأخرىفي الواقع ، فإن تغيير الملمس أعلاه سيخلق مشكلة. وذلك أساسا لأنه لا يوجد نسيج أثناء التهيئة. ومع ذلك ، في هذه البيئة تمت إضافتها ديناميكيًا ، لا يكفي تعيين المواد. needsupdate إلى True. كما يحتاج إلى تعيين هندسة. لماذا هناك مثل هذه المشكلة؟ إنه بسبب تحسين البرنامج من قبل ثلاثة. عند تهيئة الهندسة والمواد لأول مرة في العارض ، إذا تم الحكم على عدم وجود نسيج ، على الرغم من وجود كل بيانات UV في البيانات في الذاكرة ، فلن يقوم Three.js بنسخ هذه البيانات إلى ذاكرة الفيديو. يجب أن تكون النية الأصلية هي حفظ مساحة ذاكرة الفيديو القيمة. ومع ذلك ، بعد إضافة نسيج ، لن تقوم الهندسة بنقل بيانات الأشعة فوق البنفسجية هذه بذكاء لاستخدام الملمس. يجب علينا ضبط UVSneedsupdate يدويًا لإبلاغ ذلك بأن الوقت قد حان لتحديث الأشعة فوق البنفسجية ، هذا السؤال جعلني أشعر بالغش لفترة طويلة في البداية.
للحصول على سمات NeedUpdate لعدة أنواع من بيانات قمة الرأس ، يمكنك رؤية هذه المشكلة
https://github.com/mrdoob/three.js/wiki/updates
في النهايةيقوم تحسين Three.js بعمل جيد ، لكنه يجلب العديد من المزالق التي قد تتأثر بها تحسينات مختلفة. أفضل طريقة للقيام بذلك هي النظر إلى الكود المصدري ، أو الانتقال إلى Github لذكر القضايا