تعلم برنامج OpenGL. الدرس 6.4 - IBL. تعرض براق

OGL3
في الدرس السابق ، قمنا بإعداد نموذج PBR للعمل مع طريقة IBL - لهذا احتجنا إلى إعداد خريطة تشعيع مقدمًا تصف الجزء المنتشر من الإضاءة غير المباشرة. في هذا الدرس سوف ننتبه إلى الجزء الثاني من التعبير عن الانعكاسية - المرآة:

L o ( p ، o m e g a o ) = i n t l i m i t s O m e g a ( k d f r a c c p i + k s f r a c D F G 4 ( o m e g a o c d o t n )         ( omegai cdotn))Li(p، omegai)n cdot omegaid omegai

،،




قد تلاحظ أن المرآة كوك-تورينس مرآة (التعبير الفرعي بعامل ks ) ليست ثابتة وتعتمد على اتجاه الضوء الساقط ، وكذلك على اتجاه الملاحظة. إن حل هذا التكامل لجميع الاتجاهات المحتملة لحدوث الضوء ، جنبًا إلى جنب مع جميع الاتجاهات الممكنة للمراقبة في الوقت الفعلي ، غير ممكن ببساطة. لذلك ، اقترح الباحثون في Epic Games نهجًا يسمى تقريب المبلغ المقسم ، والذي يسمح لك بإعداد البيانات جزئيًا لمكون المرآة مقدمًا ، وفقًا لظروف معينة.

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

لفهم طريقة تقريب المجموع المقسوم ، دعونا نلقي نظرة مرة أخرى على التعبير عن الانعكاسية ، مع ترك فقط التعبير الفرعي لمكون المرآة الموجود فيه (تم النظر في الجزء المنتشر بشكل منفصل في الدرس السابق ):

Lo(p، omegao)= int limits Omega(ks fracDFG4( omegao cdotn)( omegai cdotn)Li(p، omegai)n cdot omegaid omegai= int limits Omegafr(p، omegai، omegao)Li(p، omegai)n cdot omegaid omegai

،،،،،


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

fr(p،wi،wo)= fracDFG4( omegao cdotn)( omegai cdotn)

،،


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

تحل طريقة المبلغ المقسم من Epic Games هذه المشكلة عن طريق تقسيم مشكلة الحساب الأولية إلى قسمين مستقلين ، يمكن دمج نتائجهما لاحقًا للحصول على القيمة النهائية المحسوبة. تستخرج طريقة المجموع المجزأ تكاملين من التعبير الأصلي لمكون المرآة:

Lo(p، omegao)= int limits OmegaLi(p، omegai)d omegai int limits Omegafr(p، omegai، omegao)n cdot omegaid omegai


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


يتم تحديد نواقل العينة وانتشارها بناءً على دالة التوزيع الطبيعي ( NDF ) لنموذج Cook-Torrens BRDF. تقبل هذه الوظيفة المتجه العادي واتجاه المراقبة كمعلمات إدخال. نظرًا لأن اتجاه الملاحظة غير معروف مقدمًا في وقت الحساب الأولي ، كان على مطوري Epic Games إجراء افتراض آخر: اتجاه النظرة (وبالتالي اتجاه الانعكاس المرآوي) مطابق دائمًا لاتجاه إخراج العينة  omegao . على شكل كود:

vec3 N = normalize(w_o); vec3 R = N; vec3 V = R; 

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


يحتوي الجزء الثاني من تعبير المجموع المقسم على BRDF للتعبير الأصلي لمكون المرآة. بافتراض أن سطوع الطاقة الواردة يمثله الطيف بالضوء الأبيض لجميع الاتجاهات (أي ، L(p،x)=1.0 ) ، من الممكن إجراء حساب مسبق لقيمة BRDF مع معلمات الإدخال التالية: خشونة المواد والزاوية بين العادي n واتجاه الضوء  omegai (أو n cdot omegai ) يتضمن نهج Epic Games تخزين نتائج حساب BRDF لكل مجموعة من الخشونة والزاوية بين الضوء الطبيعي واتجاه الضوء في شكل نسيج ثنائي الأبعاد يعرف باسم خريطة تكامل BRDF ، والذي يستخدم لاحقًا كجدول بحث ( LUT ) . يستخدم هذا النسيج المرجعي قنوات الإخراج الحمراء والخضراء لتخزين المقياس والإزاحة لحساب معامل فريسنل للسطح ، والذي يسمح لنا في نهاية المطاف بحل الجزء الثاني من التعبير للمجموع المنفصل:


يتم إنشاء هذا النسيج الإضافي على النحو التالي: إحداثيات النسيج الأفقية (تتراوح من [0. ، 1.]) تعتبر قيم معلمات الإدخال n cdot omegai وظائف BRDF ؛ تعتبر إحداثيات النسيج الرأسي كقيم خشونة الإدخال.

ونتيجة لذلك ، بوجود خريطة التكامل هذه وخريطة البيئة المعالجة مسبقًا ، يمكنك دمج العينات منها للحصول على القيمة النهائية للتعبير المتكامل لمكون المرآة:

 float lod = getMipLevelFromRoughness(roughness); vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod); vec2 envBRDF = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy; vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y) 

يجب أن تساعدك هذه المراجعة لطريقة المجموع المجزأ من Epic Games في منحك انطباعًا عن عملية تقريب جزء تعبير الانعكاس المسؤول عن مكون المرآة. الآن دعونا نحاول تجهيز بيانات البطاقة بأنفسنا.

التصفية المسبقة لخريطة بيئة HDR


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

سيتعين عليك أولاً إنشاء خريطة مكعبة جديدة تحتوي على نتيجة التصفية المسبقة. من أجل إنشاء العدد الضروري من مستويات mip ، فإننا ببساطة نسمي glGenerateMipmaps () - سيتم تخصيص الذاكرة اللازمة للنسيج الحالي:

 unsigned int prefilterMap; glGenTextures(1, &prefilterMap); glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterMap); for (unsigned int i = 0; i < 6; ++i) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 128, 128, 0, GL_RGB, GL_FLOAT, nullptr); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glGenerateMipmap(GL_TEXTURE_CUBE_MAP); 

يرجى ملاحظة: نظرًا لأن التحديد من prefilterMap سيعتمد على وجود مستويات mip ، فمن الضروري ضبط وضع مرشح الاختزال على GL_LINEAR_MIPMAP_LINEAR لتمكين التصفية ثلاثية الخطوط. يتم تخزين الصور المعالجة مسبقًا لصور المرآة في وجوه منفصلة للخريطة المكعبة بدقة على مستوى mip الأساسي 128 × 128 بكسل فقط. بالنسبة لمعظم المواد ، هذا يكفي تمامًا ، إذا كان المشهد الخاص بك يحتوي على عدد متزايد من الأسطح الملساء اللامعة (على سبيل المثال ، سيارة جديدة تمامًا) ، فقد تحتاج إلى زيادة هذه الدقة.

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


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

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

دمج مونتي كارلو وأخذ العينات من الأهمية


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

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

O= int limitsbaf(x)dx= frac1N sumN1i=0 fracf(x)pdf(x)


لحل التكامل ، نحصل على قيمة التكامل من أجل N النقاط العشوائية من العينة ضمن [a، b] ، يتم تلخيص النتائج وتقسيمها على العدد الإجمالي للنقاط المأخوذة لحساب المتوسط. البند pdf يصف دالة كثافة الاحتمال ، والتي توضح الاحتمال الذي تحدث به كل قيمة مختارة في العينة الأصلية. على سبيل المثال ، ستبدو وظيفة نمو المواطنين هذه كما يلي:


يمكن ملاحظة أنه عند استخدام نقاط أخذ العينات العشوائية ، لدينا فرصة أكبر بكثير لتحقيق قيمة نمو 170 سم من شخص لديه نمو 150 سم.

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

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

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

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


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

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

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

تسلسل عدم تطابق منخفض


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

باستخدام حيل حسابية صعبة على نحوٍ أحادي ، يمكنك تعيين تسلسل van der Corpute بكفاءة عالية في التظليل مباشرةً وبناءً عليه يقوم بإنشاء العنصر الأول من تسلسل هامرسلي من التحديد في N العناصر:

 float RadicalInverse_VdC(uint bits) { bits = (bits << 16u) | (bits >> 16u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } // ---------------------------------------------------------------------------- vec2 Hammersley(uint i, uint N) { return vec2(float(i)/float(N), RadicalInverse_VdC(i)); } 

إرجاع Hammersley () العنصر إيث من تسلسل عدم تطابق منخفض من عينات حجم متعددة N .
لا تدعم جميع برامج تشغيل OpenGL عمليات أحادي البت (WebGL و OpenGL ES 2.0 ، على سبيل المثال) ، لذلك في بعض البيئات ، قد يلزم تنفيذ بديل لاستخدامها:

 float VanDerCorpus(uint n, uint base) { float invBase = 1.0 / float(base); float denom = 1.0; float result = 0.0; for(uint i = 0u; i < 32u; ++i) { if(n > 0u) { denom = mod(float(n), 2.0); result += denom * invBase; invBase = invBase / 2.0; n = uint(float(n) / 2.0); } } return result; } // ---------------------------------------------------------------------------- vec2 HammersleyNoBitOps(uint i, uint N) { return vec2(float(i)/float(N), VanDerCorpus(i, 2u)); } 

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

نموذج أهمية في نموذج GGX


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

 const uint SAMPLE_COUNT = 4096u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); 

بالإضافة إلى ذلك ، من أجل التكوين الكامل لمتجه العينة ، سيكون من الضروري توجيهه بطريقة أو بأخرى في اتجاه فص المرآة المقابل لمستوى معين من الخشونة. يمكنك أخذ NDF (دالة التوزيع العادية) من درس نظري والجمع مع GGX NDF لطريقة تحديد متجه العينة في تأليف المجال Epic Games:

 vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { float a = roughness*roughness; float phi = 2.0 * PI * Xi.x; float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); float sinTheta = sqrt(1.0 - cosTheta*cosTheta); //       vec3 H; Hx = cos(phi) * sinTheta; Hy = sin(phi) * sinTheta; Hz = cosTheta; //        vec3 up = abs(Nz) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 tangent = normalize(cross(up, N)); vec3 bitangent = cross(N, tangent); vec3 sampleVec = tangent * Hx + bitangent * Hy + N * Hz; return normalize(sampleVec); } 

والنتيجة هي ناقل عينة ، موجه تقريبًا على طول المتجه الوسيط للسطوح الدقيقة ، لخشونة معينة وعنصر من تسلسل عدم تطابق Xi المنخفض. لاحظ أن Epic Games تستخدم مربع قيمة الخشونة لجودة بصرية أكبر ، استنادًا إلى عمل ديزني الأصلي على طريقة PBR.

بعد الانتهاء من تنفيذ تسلسل Hammersley ورمز توليد متجه العينة ، يمكننا إعطاء رمز التظليل المسبق للتصفية والالتفاف:

 #version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; uniform float roughness; const float PI = 3.14159265359; float RadicalInverse_VdC(uint bits); vec2 Hammersley(uint i, uint N); vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); void main() { vec3 N = normalize(localPos); vec3 R = N; vec3 V = R; const uint SAMPLE_COUNT = 1024u; float totalWeight = 0.0; vec3 prefilteredColor = vec3(0.0); for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { prefilteredColor += texture(environmentMap, L).rgb * NdotL; totalWeight += NdotL; } } prefilteredColor = prefilteredColor / totalWeight; FragColor = vec4(prefilteredColor, 1.0); } 


نقوم بتنفيذ prefiltering خريطة البيئة على أساس من خشونة محددة سلفا، ومستوى الذي يختلف عن كل مستوى خريطة برنامج التأمين الصحي الناتجة مكعب (0،0-1،0)، ونتيجة تصفية المخزنة في المتغير prefilteredColor . بعد ذلك ، يتم تقسيم المتغير على الوزن الإجمالي للعينة بأكملها ، كما أن العينات ذات المساهمة الأصغر في النتيجة النهائية (التي تحتوي على قيمة NdotL أقل ) تزيد أيضًا من الوزن الإجمالي أقل.

حفظ البيانات قبل التصفية في مستويات mip


يبقى كتابة التعليمات البرمجية التي توجه OpenGL مباشرة لتصفية خريطة البيئة بمستويات مختلفة من الخشونة ثم حفظ النتائج في سلسلة من مستويات mip للخريطة المكعبة المستهدفة. هنا ، فإن التعليمات البرمجية التي تم إعدادها بالفعل من الدرس حول حساب خريطة التشعيع مفيدة :

 prefilterShader.use(); prefilterShader.setInt("environmentMap", 0); prefilterShader.setMat4("projection", captureProjection); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); unsigned int maxMipLevels = 5; for (unsigned int mip = 0; mip < maxMipLevels; ++mip) { //        - unsigned int mipWidth = 128 * std::pow(0.5, mip); unsigned int mipHeight = 128 * std::pow(0.5, mip); glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); glViewport(0, 0, mipWidth, mipHeight); float roughness = (float)mip / (float)(maxMipLevels - 1); prefilterShader.setFloat("roughness", roughness); for (unsigned int i = 0; i < 6; ++i) { prefilterShader.setMat4("view", captureViews[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterMap, mip); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderCube(); } } glBindFramebuffer(GL_FRAMEBUFFER, 0); 

تشبه العملية الالتفاف لخريطة التشعيع ، ولكن هذه المرة يجب عليك تحديد حجم المخزن المؤقت للإطار في كل خطوة ، وتقليله إلى النصف ليتناسب مع مستويات mip. أيضًا ، يجب تحديد مستوى mip الذي سيتم تنفيذ التقديم إليه حاليًا كمعلمة للدالة glFramebufferTexture2D () .

يجب أن تكون نتيجة تنفيذ هذا الرمز خريطة مكعبة تحتوي على صور ضبابية بشكل متزايد للانعكاسات في كل مستوى mip لاحق. يمكنك استخدام هذه الخريطة المكعبة كمصدر بيانات لـ skybox وأخذ عينة من أي مستوى mip أقل من الصفر:

 vec3 envColor = textureLod(environmentMap, WorldPos, 1.2).rgb; 

ستكون نتيجة هذا الإجراء الصورة التالية:


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

التحف التحويلة قبل التصفية


بالنسبة لمعظم المهام ، يعمل النهج الموصوف بشكل جيد ، ولكن عاجلاً أم آجلاً ، سيتعين عليك مقابلة العديد من القطع الأثرية التي تولدها عملية التصفية المسبقة. فيما يلي أكثر الأساليب شيوعًا للتعامل معها.

مظهر طبقات الخريطة المكعبة


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


لحسن الحظ ، فإن OpenGL لديه القدرة على تنشيط هذا التصفية بعلامة بسيطة:

 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 

يكفي وضع علامة في مكان ما في رمز تهيئة التطبيق ويتم التخلص من هذه الأداة.

تظهر النقاط المضيئة


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


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

 float D = DistributionGGX(NdotH, roughness); float pdf = (D * NdotH / (4.0 * HdotV)) + 0.0001; //        float resolution = 512.0; float saTexel = 4.0 * PI / (6.0 * resolution * resolution); float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001); float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel); 

تذكر فقط تمكين التصفية الثلاثية لخريطة البيئة من أجل التحديد بنجاح من مستويات mip:

 glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 

أيضا ، لا تنس إنشاء مستويات mip مباشرة للنسيج باستخدام OpenGL ، ولكن فقط بعد تشكيل مستوى mip الرئيسي بالكامل:

 //  HDR      ... [...] //  - glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glGenerateMipmap(GL_TEXTURE_CUBE_MAP); 

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

الحساب الأولي لـ BRDF


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

L o ( p ، ω o ) = Ω L i ( p ، ω i ) d ω iΩ f r ( p ، ω i ، ω o ) n ω i d ω i


قمنا بحساب الجزء الأيسر من المجموع مبدئيًا وسجلنا النتائج لمستويات مختلفة من الخشونة في خريطة مكعبة منفصلة. سيتطلب الجانب الأيمن التفاف تعبير BDRF مع المعلمات التالية: anglen ω i ، خشونة السطح ومعامل فريسنلF 0 . عملية مشابهة لدمج BRDF معكوسة لبيئة بيضاء تمامًا أو مع سطوع طاقة ثابت L i = 1.0 . إن عقد BRDF لثلاثة متغيرات ليست مهمة تافهة ، ولكن في هذه الحالة يمكن اشتقاق F 0 من التعبير الذي يصف المرآة BRDF:

Ω fr(p،ωi،ωo)nωidωi=Ω fr(p،ωi،ωo) F ( ω o ، h )F ( ω o ، h ) nωidωi


هنا F هي دالة تصف حساب مجموعة فريسنل. عند تحريك المقسوم على تعبير BRDF ، يمكنك الانتقال إلى الترميز المكافئ التالي:

Ω f r ( p ، ω i ، ω o )F ( ω o ، h ) F(ωo،h)nωidωi


استبدال الإدخال الصحيح F على تقريب فريسنل شليك، نحصل على:

Ω f r ( p ، ω i ، ω o )F ( ω o ، h ) (F0+(1-F0)(1-ωoh)5)nωidωi


دلالة على التعبير ( 1 - ω oh ) 5 كيف / A ل ص ح ل تسهيل اتخاذ القرارات المتعلقةF 0 :

Ωfr(p,ωi,ωo)F(ωo,h)(F0+(1F0)α)nωidωi


Ωfr(p,ωi,ωo)F(ωo,h)(F0+1αF0α)nωidωi


Ωfr(p,ωi,ωo)F ( ω o ، h ) (F0(1-α)+α)nωidωi


الوظيفة التالية نقسم F إلى قسمين:

Ω f r ( p ، ω i ، ω o )F ( ω o ، h ) (F0(1-α))nωidωi+Ωfr(p،ωi،ωo)F ( ω o ، h ) (α)nωidωi


بهذه الطريقة سيكون F 0 ثابتًا تحت التكامل ، ويمكننا إخراجه من علامة التكامل. بعد ذلك ، سنكشفα في التعبير الأصلي والحصول على الإدخال النهائي لـ BRDF كمجموع منفصل:

F0Ωfr(p,ωi,ωo)(1(1ωoh)5)nωidωi+Ωfr(p,ωi,ωo)(1ωoh)5nωidωi


يمثل التكاملان الناتجان المقياس والإزاحة للقيمة F 0 ، على التوالي. لاحظ ذلكf ( p ، ω i ، ω o ) يحتوي على حدثو ، لأن هذه الأحداث تلغي بعضها البعض وتختفي من التعبير. باستخدام النهج المطور بالفعل ، يمكن تنفيذ التفاف BRDF مع بيانات الإدخال: الخشونة والزاوية بين المتجهات

ن و ث س .إرسال النتيجة في 2D نسيج - بطاقة complexation BRDF ( BRDF خريطة التكامل )، والتي سوف تكون بمثابة قيم الجدول مساعدة لاستخدامها في تظليل النهائي، التي ستشكل النتيجة النهائية للإضاءة المرآة غير المباشرة.

يعمل جهاز تظليل الالتواء BRDF على المستوى ، مباشرة باستخدام إحداثيات الملمس ثنائية الأبعاد كمعلمات إدخال لعملية الالتفاف ( NdotV و الخشونة ). يشبه الكود بشكل ملحوظ تجمّع ما قبل التصفية ، ولكن هنا تتم معالجة متجه العينة مع مراعاة الوظيفة الهندسية BRDF وتعبير فريسنيل-شليك التقريبي:

 vec2 IntegrateBRDF(float NdotV, float roughness) { vec3 V; Vx = sqrt(1.0 - NdotV*NdotV); Vy = 0.0; Vz = NdotV; float A = 0.0; float B = 0.0; vec3 N = vec3(0.0, 0.0, 1.0); const uint SAMPLE_COUNT = 1024u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(Lz, 0.0); float NdotH = max(Hz, 0.0); float VdotH = max(dot(V, H), 0.0); if(NdotL > 0.0) { float G = GeometrySmith(N, V, L, roughness); float G_Vis = (G * VdotH) / (NdotH * NdotV); float Fc = pow(1.0 - VdotH, 5.0); A += (1.0 - Fc) * G_Vis; B += Fc * G_Vis; } } A /= float(SAMPLE_COUNT); B /= float(SAMPLE_COUNT); return vec2(A, B); } // ---------------------------------------------------------------------------- void main() { vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); FragColor = integratedBRDF; } 

كما ترى ، يتم تنفيذ التفاف BRDF كترتيب حرفي تقريبًا للحسابات الرياضية المذكورة أعلاه. يتم أخذ معلمات الإدخال للخشونة والزاوية.θ ، يتم تكوين ناقل العينة بناءً على العينة حسب الأهمية ، ويتم معالجتها باستخدام دالة الهندسة وتعبير فريسنل المحول لـ BRDF. ونتيجة لذلك ، لكل عينة ، حجم التحجيم والتشريدF 0 ، التي يتم حساب متوسطها وإعادتها فيالنهاية علىشكلvec2. فيدرسنظري، ذكر أن المكون الهندسي لـ BRDF يختلف قليلاً في حالة حساب IBL ، حيث أن المعامل

يتم تحديد k بشكل مختلف:

k d i r e c t = ( α + 1 ) 28


k I B L = α 22


بما أن الالتواء BRDF هو جزء من حل التكامل في حالة حساب IBL ، فسوف نستخدم المعامل k I B L لحساب دالة الهندسة في نموذج Schlick-GGX:

 float GeometrySchlickGGX(float NdotV, float roughness) { float a = roughness; float k = (a * a) / 2.0; float nom = NdotV; float denom = NdotV * (1.0 - k) + k; return nom / denom; } // ---------------------------------------------------------------------------- float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } 

يرجى ملاحظة أن المعامل يتم حساب k بناءً على المعلمةa. علاوة على ذلك ، في هذه الحالة ،لايتمتربيعمعلمةالخشونةعند وصف المعلمةa، والتي تم إجراؤها في أماكن أخرى حيث تم تطبيق هذه المعلمة. لست متأكدًا من أين تكمن المشكلة: في عمل Epic Games أو في العمل الأولي من Disney ، ولكن تجدر الإشارة إلى أن هذا التعيين المباشر لقيمةالخشونةعلى وجه الدقةللمعلمةa هو الذييخلق خريطة تكامل BRDF متطابقة ، المقدمة في منشور Epic Games.علاوة على ذلك ، سيتم حفظ نتائج الالتفاف BRDF في شكل نسيج ثنائي الأبعاد بحجم 512x512:



 unsigned int brdfLUTTexture; glGenTextures(1, &brdfLUTTexture); //   ,     glBindTexture(GL_TEXTURE_2D, brdfLUTTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, 512, 512, 0, GL_RG, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

وفقًا لتوصية Epic Games ، يتم استخدام تنسيق نسيج عائم 16 بت هنا. تأكد من تعيين وضع التكرار على GL_CLAMP_TO_EDGE لتجنب أخذ عينات من الحافة.

بعد ذلك ، نستخدم نفس كائن المخزن المؤقت للإطار ونقوم بتنفيذ تظليل على سطح رباعية ملء الشاشة:

 glBindFramebuffer(GL_FRAMEBUFFER, captureFBO); glBindRenderbuffer(GL_RENDERBUFFER, captureRBO); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdfLUTTexture, 0); glViewport(0, 0, 512, 512); brdfShader.use(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); RenderQuad(); glBindFramebuffer(GL_FRAMEBUFFER, 0); 

ونتيجة لذلك ، نحصل على خريطة نسيجية تخزن نتيجة التفاف جزء من التعبير عن الكمية المقسمة المسؤولة عن BRDF:


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

حساب الانعكاس النهائي في نموذج IBL


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

 uniform samplerCube prefilterMap; uniform sampler2D brdfLUT; 

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

 void main() { [...] vec3 R = reflect(-V, N); const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; [...] } 

في مرحلة الالتفاف الأولية ، قمنا بإعداد 5 مستويات mip فقط (من صفر إلى رابع) ، يعمل MAX_REFLECTION_LOD الثابت على تحديد التحديد من مستويات mip التي تم إنشاؤها.

بعد ذلك ، نقوم بالاختيار من خريطة تكامل BRDF استنادًا إلى الخشونة والزاوية بين الوضع العادي واتجاه العرض:

 vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); 

تحتوي القيمة التي تم الحصول عليها من الخريطة على عوامل التحجيم والإزاحة للقيمة F 0 (هنا نأخذ القيمةF- معامل Fresnel).ثميتمدمجالقيمة المحولةFمع القيمة التي تم الحصول عليها من خريطة التصفية المسبقة للحصول على حل تقريبي للتعبير المتكامل الأصلي -براق.وبالتالي ، نحصل على حل للجزء من التعبير عن الانعكاسية ، وهو المسؤول عن الانعكاس المرآوي. للحصول على حل كامل لنموذج PBR IBL ، تحتاج إلى دمج هذه القيمة مع حل الجزء المنتشر من تعبير الانعكاس الذي تلقيناه فيالدرسالأخير:



 vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); vec3 ambient = (kD * diffuse + specular) * ao; 

ألاحظ أن القيمة المرآوية لا يتم ضربها في kS ، لأنها تحتوي بالفعل على معامل Fresnel.

لنقم بتشغيل تطبيق الاختبار الخاص بنا مع مجموعة مألوفة من المجالات ذات الخصائص المتغيرة للمعدنية والخشونة ونلقي نظرة على مظهرها في روعة PBR الكاملة:


يمكنك الذهاب إلى أبعد من ذلك وتنزيل مجموعة من القوام المطابقة لنموذج PBR والحصول على المجالات من المواد الحقيقية :


أو حتى تنزيل نموذج رائع جنبًا إلى جنب مع مواد PBR المعدة من أندرو ماكسيموف :


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


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

ما هي الخطوة التالية؟


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

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

نقطة أخرى مهمة: من أجل إعطاء فهم عميق لخط أنابيب PBR في هذه الدروس ، وصفت العملية الكاملة للتحضير لتقديم PBR ، بما في ذلك الحسابات الأولية للبطاقات المساعدة لـ IBL. ومع ذلك ، في ممارستك ، يمكنك أيضًا استخدام إحدى الأدوات المساعدة الرائعة التي تعد هذه البطاقات لك: على سبيل المثال cmftStudio أو IBLBaker .

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

(يمكن العثور على تنفيذ العينات ، وأكثر من ذلك بكثير ، في المحرك الخام لمؤلف الدروس هنا ، تقريبًا لكل. )

مواد إضافية


  1. Real Shading in Unreal Engine 4 : شرح لنهج Epic Games لتقريب التعبير عن مكون المرآة بمجموع مقسم. بناءً على هذه المقالة ، تم كتابة كود درس IBL PBR.
  2. التظليل المادي والإضاءة القائمة على الصور : مقالة ممتازة تصف عملية تضمين حساب مكون المرآة لـ IBL في تطبيق خط أنابيب تفاعلي PBR.
  3. الإضاءة القائمة على الصورة : مشاركة طويلة ومفصلة للغاية حول IBL المألوف والمسائل ذات الصلة ، بما في ذلك مشكلة استيفاء مسبار الضوء.
  4. Moving Frostbite to PBR : , PBR «AAA».
  5. Physically Based Rendering – Part Three : , IBL PBR JMonkeyEngine.
  6. Implementation Notes: Runtime Environment Map Filtering for Image Based Lighting : HDR , .

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


All Articles