لتقديم رسومات معقدة على صفحات الويب ، توجد مكتبة رسومات الويب ، والمختصرة باسم WebGL. تحدث مصمم الواجهة Dmitry Vasiliev عن برمجة GPU من وجهة نظر مصمم التخطيط ، وعن ماهية WebGL وكيف حلنا مشكلة تصور بيانات الطقس الكبيرة باستخدام هذه التكنولوجيا.
- أقوم بتطوير واجهات في مكتب يكاترينبرج في ياندكس. لقد بدأت في المجموعة الرياضية. كنا نقوم بتطوير مشاريع رياضية خاصة عندما كانت هناك بطولات عالمية في الهوكي وكرة القدم والأولمبياد وأولمبياد المعاقين وغيرها من الأحداث الرائعة. عملت أيضًا على تطوير نتائج بحث خاصة ، والتي كانت مخصصة لمسار Sochi الجديد.




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

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


بعد أن وضعنا التوقعات القياسية بالترتيب ، قررنا أن نجعل التنبؤات التي لم يكن لأحد. كانت هذه التوقعات تنبؤًا لحركة هطول الأمطار عبر المناطق.

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

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

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

الخدمات التي تظهر توقعات الرياح موجودة بالفعل. هذا هو اثنين من تلك التي تبرز أنيق.

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

بعد التشاور مع المصمم ، أدركنا أننا بحاجة إلى ملء حوالي 6٪ من الشاشة بجزيئات حتى يكون لها تأثير رائع.
لرسم هذا العدد من الجسيمات باستخدام النهج القياسي ، كان لدينا توقيت لا يقل عن 5 مللي ثانية.

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



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





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

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

نحن لا نفهم ما.

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

حسنا ، نحن نقرر أن نفعل ذلك بأنفسنا.

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

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

حسنًا ، قررنا استخدام WebGL 1 المجربة القديمة ، والتي تتمتع بدعم جيد إلى جانب Opera Mini ، الذي لا يحتاجه أحد.

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

لذلك ، عند العمل مع WebGL ، يجب عليك أيضًا الاستماع إلى فقدان السياق وتكون قادرًا على استعادته. لذلك ، أكدت أن الحرف الأول هو.

علاوة على ذلك ، فإن كل عمل JS يتلخص في جمع البرامج التي تعمل على وحدة معالجة الرسومات (GPU) ، وإرسال بطاقة رسومات لهم ، ووضع بعض المعلمات والقول "رسم".

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

ننتقل إلى ما يتم تنفيذه على بطاقة الفيديو نفسها - برنامج يتكون من مجموعتين من التعليمات المكتوبة بلغة GLSL تشبه C. وتسمى هذه التعليمات قمة شادر وتظليل شظية. يتم إنشاء برنامج من الزوجين.

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


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

لنقل هذا إلى JS ، فقط لف الكود المصدر للتظليل في المتغيرات.

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

قفز أعمق. في تظليل قمة الرأس ، يتم تنفيذ جميع العمليات الحسابية في الفضاء من -1 إلى 1 ، بغض النظر عن حجم نقطة الإخراج. على سبيل المثال ، يمكن أن تشغل المساحة من 1 إلى 1 الشاشة بأكملها 1920 × 1080. لرسم مثلث في وسط الشاشة ، تحتاج إلى رسم سطح يغطي الإحداثيات 0 ، 0.

يعمل تظليل الأجزاء في المساحة من 0 إلى 1 ، ويتم عرض الألوان بواسطة أربعة مكونات: R ، G ، B ، Alpha.
باستخدام CSS كمثال ، قد تصادفك تدوين ألوان مماثل عند استخدام النسب المئوية.

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

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

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

نصنع اثنين من تظليل ، قمة وشظية.

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

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

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

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

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

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

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

لتحميله على البطاقة ، نحتاج إلى قطعه. لتوصيل الصورة بالخريطة ، سنستخدم أداة Yandex.Map Layer القياسية ، والتي نحدد فيها العنوان الذي سيتم من خلاله قطع البلاط ، وإضافة هذه الطبقة إلى الخريطة.

نحصل على صورة حيث اللون الأخضر غير سار هو سرعة الرياح مشفرة.

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

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

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

من خلال رسم الجسيمات بالترتيب ومعرفة الرقم التسلسلي للجسيمات وحجم النسيج الذي تم تخزينهما به ، يمكنك حساب بكسل معين يتم فيه تشفير الموضع على الشاشة الحقيقية.


في التظليل نفسه ، يبدو مثل قراءة الفهرس المقدم والملمس مع الموضع الحالي للجزيئات وحجم الجانب. بعد ذلك ، نحدد إحداثيات x ، y لهذا الجسيم ، ونقرأ هذه القيمة ونفك تشفيرها. أي نوع من السحر هذا: rg / 255 + ba؟
لموقف الجزيئات نستخدم 20 قناة مزدوجة. تحتوي القناة الملونة على قيمة من 0 إلى 255 ، وبالنسبة لشاشة 1080 ، لا يمكننا وضع الجزيئات في أي موضع من الشاشة لفترة قصيرة ، لأنه يمكننا وضع الجسيمات بحد أقصى 255 بكسل. لذلك ، في إحدى القنوات ، نقوم بتخزين معرفة عدد المرات التي تجاوز فيها الجسيم 255 بكسل ، وفي القناة الثانية ، نقوم بتخزين القيمة الدقيقة لمقدار ما بعده.
بعد ذلك ، يجب على shader vertex تحويل هذه القيم إلى مساحة العمل الخاصة به ، أي من -1 إلى 1 ، وتعيين هذه النقطة على الشاشة.

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

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

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

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

نحن نحسب المسافة إلى بكسل المقدمة من المركز. إذا تجاوز نصف المساحة ، فنحن لا نعرضها.

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


يبدو الإجراء الخاص بتحديث الموضع نفسه كما يلي: قراءة الموضع الحالي ، وإضافته إلى متجه السرعة الحالي وإضافته ، وترميزه بلون جديد.

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

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

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

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

, , . , WebGL, . Khronos, , , .

— . , , , , , , , .

.


WebGL 2D canvas, . 64 . 2D canvas, 25 , WebGL 0,3 . .
, WebGL , , .
, , , - break points, - , . WebGL — .

, . , Firefox «», WebGL-, , , . , .

, , Spector.js. canvas WebGL- canvas, .

. , , , WebGL, , . .