العرض الحجمي في WebGL


الشكل 1. مثال على العروض الحجرية التي يؤديها عارض WebGL الموضحة في المنشور. يسار: محاكاة لتوزيع الاحتمالات المكانية للإلكترونات في جزيء البروتين عالي الإمكانات. اليمين: رسم بياني لشجرة بونساي. تؤخذ كلتا مجموعتي البيانات من مستودع بيانات Open SciVis .

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

1. مقدمة



الشكل 2: التقديم الحجمي الفيزيائي ، مع مراعاة امتصاص وانبعاث الضوء من حيث الحجم ، وكذلك آثار الانتثار.

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

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

C(r)= int0LC(s) mu(s)e int0s mu(t)dtds


أثناء مرور الحزمة عبر مستوى الصوت ، ندمج اللون المنبعث C(s)والامتصاص  mu(s)في كل نقطة sعلى طول الشعاع. يتلاشى الضوء المنبعث عند كل نقطة ويعود إلى العين امتصاص الحجم حتى هذه النقطة ، والذي يتم حسابه بواسطة e int0s mu(t)dt.

في الحالة العامة ، لا يمكن حساب هذا التكامل بشكل تحليلي ؛ لذلك ، يجب استخدام تقريب رقمي. نقوم بتنفيذ تقريب لا يتجزأ من خلال أخذ العديد من العينات Nعلى طول الشعاع في الفاصل s=[0،L]كل منها يقع على مسافة  Deltasبصرف النظر عن بعضها البعض (الشكل 3) ، وتلخيص كل هذه العينات. يصبح مصطلح التخميد عند كل نقطة أخذ عينات نتاج امتصاص التراكم المتسلسل في العينات السابقة.

C(r)= sumi=0NC(i Deltas) mu(i Deltas) Deltas prodj=0i1e mu(j Deltas) Deltas


لتبسيط هذا المبلغ إلى أبعد من ذلك ، فإننا نقدر مصطلح التخميد ( e mu(j Deltas) Deltas) بالقرب من تايلور. أيضا للراحة نقدم ألفا  alpha(i Deltas)= mu(i Deltas) Deltas. هذا يعطينا معادلة تركيب ألفا التي يتم إجراؤها من الأمام إلى الخلف:

C(r)= sumi=0NC(i Deltas) alpha(i Deltas) prodj=0i1(1 alpha(j دلتاق))دولا



الشكل 3: حساب تكامل تقديم امتصاص الانبعاثات في الحجم.

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

 hatCi= hatCi1+(1 alphai1) hatC(i Deltas)


 alphai= alphai1+(1 alphai1) alpha(i Deltas)


تحتوي هذه المعادلات النهائية على عتامة مضروبة مسبقًا للخلط المناسب ،  hatC(i Deltas)=C(i Deltas) alpha(i Deltas).

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


الشكل 4: Raymarching عبر شبكة تخزين.

2. تنفيذ GPU على WebGL2


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


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

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


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

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

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

يظهر تظليل قمة الرأس الذي نستخدمه أعلاه ، ويتم عرض الوجوه الخلفية المنقطة المرسومة في اتجاه شعاع الرؤية في الشكل 7.

#version 300 es layout(location=0) in vec3 pos; uniform mat4 proj_view; uniform vec3 eye_pos; uniform vec3 volume_scale; out vec3 vray_dir; flat out vec3 transformed_eye; void main(void) { // Translate the cube to center it at the origin. vec3 volume_translation = vec3(0.5) - volume_scale * 0.5; gl_Position = proj_view * vec4(pos * volume_scale + volume_translation, 1); // Compute eye position and ray directions in the unit cube space transformed_eye = (eye_pos - volume_translation) / volume_scale; vray_dir = pos - transformed_eye; }; 


الشكل 7: الوجوه المعكوسة لمتوازي الحجم المتوازي المرسوم في اتجاه الحزمة.

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

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

آخر بيانات الإدخال لتظليل الأجزاء هي أبعاد الحجم التي نستخدمها لحساب حجم خطوة الحزمة (  Deltas) لأخذ عينات من كل فوكسل على طول الحزمة مرة واحدة على الأقل. منذ المعادلة شعاع التقليدية لديه الشكل r(t)= veco+t vecd، من أجل الامتثال ، سنقوم بتغيير المصطلحات في التعليمات البرمجية ، والإشارة إلى  Deltasكيف  textttdt. وبالمثل ، الفاصل الزمني s=[0،L]على طول شعاع ، التي تغطيها حجم ، فإننا نشير إلى [ texttttmin، texttttmax].

لأداء قياس مستوى الصوت في شظية شظية ، سنفعل ما يلي:

  1. نحن تطبيع اتجاه شعاع الرؤية المستلمة كمدخل من تظليل قمة الرأس ؛
  2. عبور خط البصر مع حدود مستوى الصوت لتحديد الفاصل الزمني [ texttttmin، texttttmax]لأداء raymarching بهدف جعل حجم ؛
  3. نحسب طول هذه الخطوة  textttdtبحيث يتم أخذ عينات من كل فوكسل مرة واحدة على الأقل ؛
  4. بدءا من نقطة الدخول إلى r( texttttmin)، دعنا نذهب من خلال شعاع من خلال مستوى الصوت حتى نصل إلى نقطة النهاية في r( texttttmax)
    1. في كل نقطة نأخذ عينات من حجم الصوت ونستخدم وظيفة النقل لتعيين اللون والتعتيم ؛
    2. سنقوم بتجميع اللون والتعتيم على طول الحزمة باستخدام معادلة التركيب من الأمام إلى الخلف.

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

يظهر التظليل الكامل للجزء الخاص بجهاز عرض الحجم أدناه. تمت إضافة التعليقات إليها ، مع تحديد كل مرحلة من مراحل العملية.


الشكل 8: تصوّر بونساي الجاهز الناتج من وجهة النظر نفسها كما في الشكل 7.

هذا كل شئ!

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

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


All Articles