الإضاءة الأساسية : دورة OpenGL لغة c++ الدرس الرابع عشر


الإضاءة الأساسية في مكتبة OpenGL واحدة من أهم برمجة الألوان في المشهد حيث أنها تعيد النظر في جودة الرسومات ودقتها الأقرب إلى عالم حقيقي.[1] بينما تتصف الإضاءة الأساسية في العالم الحقيقي بالتعقيد وذلك لاعتمادها على عوامل عديدة. لدينا قوى رئيسية في إدارة المصفوفات لكي يتم تقديم رسومات ذات جودة عالية. [1]تمتلك الإضاءة في اوبن جل مجموعة من النماذج التي توفر خصائص فريدة في عرض المشهد. لربما يستدعي بعضها القيام بمعادلات فيزيائية لكي تعطي أداء على نحو مطلوب.[1] يمكن تقسيم الإضاءة الأساسية في OpenGL إلى مجموعة من الأنواع الواقعة تحت الاسم Phong lighting model.[1]



أنواع الإضاءة الأساسية

  1. الإضاءة المحيطة (Ambient).
  2. المنتشرة (diffuse).
  3. البراقة (Specular).
  4. المدمجة (Phong).


يمكن أن تساعدك أنواع الإضاءة الأساسية في إخراج محرك رسومي قوي. وذلك من خلال الأنواع والفوائد الكبيرة التي تحصل عليها في عالم الفضاء الرسومي.[1]

على سبيل المثال , يشير أول نوع من الأضواء السابقة إلى وجود إشعاع خافت ومتوهج من مكان بعيد يظهر ألوان وأماكن بعض الأجسام في العرض مثل ضوء القمر عندما ينعكس على الجبال والمباني أو أضواء أخرى قوية.[1]

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

 


الإضاءة المحيطة



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

تمثل خوارزمية global illumination أحد اشكالها في الإضاءة الرئيسية. وتتصف بمدى التعقيد الذي يحصل عند تطبيقها على نحو مطلوب.[1]

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

إضافة الإضاءة المحيطة سهلة وبسيطة فهي تأخذ قيمة الضوء مضروبة بمعامل الضوء كما في الكود التالي:

void main()
{
float ambientStrength = 0.3f;
vec3 ambient = ambientStrength * light Color;
vec3 result = ambient * object Color;
color = vec4(result, 1.0f);
}

لقد قمنا بتطبيق المعادلات السابقة في ملف lighting.frag الذي قمنا بإعداده في الدرس السابق.[1] بالتالي عند تفعيل الكود ستلاحظ بأن معادلة الضوء المحيط تعمل تماما مثل الصورة التالية:

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

 

يمكنك التحكم بمدى سطوع الضوء عن طريق ضبط قيمة ambient Strength التي قمنا بتعيينها في ملف GLSL.

 


الإضاءة المنتشرة



قد لا تقدم معادلة الضوء المحيط نتائج جيدة على النماذج لكن إضاءة الانتشار سوف تعطي تأثير كبير على الكائنات.[1] على سبيل المثال فهي تعطي وضوح أكثر على النماذج الأقرب لأشعة الضوء المصدرية (Lamp shader).[1]

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

بينما في حال الوقوع بشكل مائل سيقل مدى تأثير الضوء على السطح.[1]

يتم استخدام متجه باسم normal vector لقياس مدى شدة الإسقاط العمودي على الأسطح.[1] ومع ذلك فإننا نستخرج الزاوية الناتجة بين هذين المتجهين ليتم الوصول إلى قيمة dot product.[1]

ويتم الوصول إلى احتساب القيم عن طريق متجه Normal وهي التي تمثل وقوع المتجه عموديًا على سطح النموذج.[1] بينما يتم الوصول إلى شعاع الضوء من خلال احتساب المسافة بين موقع الضوء (light source) وموقع السطح المنعكس(surface).[1]

 


متجهات (Normal vector)



يتكون Normal vector من متجه واحدي عمودي الإسقاط على السطح وهو يمثل فقط نقطة واحدة من الفضاء.[1] ونستطيع استرجاع قيمته من خلال قيم وإحداثيات السطح الذي استقبل الإشعاع.[1] بالتالي يمكننا احتساب هذه القيم بطريقة cross product. لكن لأن المكعب هو ليس من النماذج المعقدة نستطيع اضافة كل من عناصر المعادلة إلى إحداثياته.[1]

ندعوك إلى استبدال كافة إحداثيات المكعب. قد تجد الرابط أسفل المراجع.[2] نقوم الآن بإجراء تعديل بسيط على ملف lighting.vs لكي يتم تضمين متجه normal في الظلال.

layout (location = 1) in vec3 normal;

لأننا قمنا بإضافة متجه normal يعمل على قراءة القيم من الإحداثيات. يجب إجراء تعديل بسيط على وظيفة glVertexAttribPointer وذلك لأننا أيضا أجرينا تغيير على إحداثيات المكعب لتصبح كل واحدة مكونة من 6 قيم.

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (
GLvoid*)0);

يتم الآن إضافة متغير باسم Normal في ملف lighting.vs ومن ثم نتصل به في دالة main.

out vec3 Normal;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
Normal = normal;
}

 

قم بالحفاظ على القيم السابقة الموجودة في ملف lighting.vs لكي لا تفقد الطريقة الصحيحة في عمل المشروع.

 

يتم الآن الإعلان عن استقبال متغير Normal في ملف lighting.frag.

in vec3 Normal;

 


احتساب لون الإنتشار



تستخدم الإضاءة الأساسية طرق محددة في احتساب معامل الانتشار. وبما أننا نمتلك متجهات normal لكل إحداثية فإننا نريد احتساب مكان الضوء.[1] يمثل مكان الضوء متغير ثابت ويمكن الإعلان عنه في ملف lighting.frag.[1]

uniform vec3 lightPos;

وفي العودة إلى كود المشروع يتعين علينا القيم بتحديث يونيفورم الضوء , سوف نقوم بذلك في حلقة while الخاصة بمكتبة openGL.[1]

GLint lightPosLoc = glGetUniformLocation(lightingShader.Program, "lightPos"
);
glUniform3f(lightPosLoc, light Pos.x, lightPos.y, lightPos.z);

بعد ذلك نقوم باحتساب كافة قيم الضوء في منطقة world space الخاصة بـ lighting.vs.[1] ويتم ذلك من خلال ضرب إحداثيات vertex بـ model matrix. لنبدأ بالإعلان عن متغير FragPos في ملف lighting.vs.[1]

out vec3 FragPos;

وفي دالة void main الخاصة بملف lighting.vs نقوم بتنفيذ المعادلة التالية:

FragPos = vec3(model * vec4(position, 1.0f));

ثم نذهب إلى ملف lighting.frag ونقوم بتعريف متغير تم استقباله من ملف vertex shader. [1]

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

يتم احتساب ذلك عن طريق المصفوفات ويمكن استخراج الفرق بين متجهين عن طريق طرح أحدهما من الآخر كما في تظهر المعادلة في ملف lighting.vs.[1]

vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);

 

تتطلب الإضاءة الأساسية أيضا احتساب الانتشار الفعلي للضوء في ملف lighting.frag. وذلك عن طريق استخراج قيمة dot product لكل من norm و lightDir.[1]

float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * light Color;

الآن سوف يتم تطبيق كل من ambient و diffuse عن طريق دمج الألوان ببعضها البعض.[1] ويظهر ذلك عن طريق تطبيق المعادلة التالية:

vec3 result = (ambient + diffuse) * object Color;
color = vec4(result, 1.0f);
الإضاءة الأساسية
صورة يظهر من خلالها تفعيل الإضاءة الأساسية Diffuse lighting في مكتبة OpenGL.

يمكنك الحصول على كود lighting.frag[3] و مصادر كود المشروع عن طريق المصادر أدناه. [4]

 


الإضاءة البراقة (Specular)



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

للحصول على إحداثيات world space من جهة صندوق العرض frustum نحتاج إلى إحداثيات كائن الكاميرا ولكي يتحقق ذلك يتطلب منا إضافة يونيفورم خاص يتم تعريفه في ملف lighting.frag.

 

uniform vec3 viewPos;

ثم بعد ذلك نحاول الاتصال به في كود المشروع عن طريق الوظيفة التالية:

GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");
glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.
Position.z);

لدينا جميع المتغيرات التي تساعدنا في احتساب الإضاءة الأساسية للمتجه specular وفي بداية الأمر نقوم بتعريف كثافة الضوء البراق في ملف lighting.frag عن طريق المتغير التالي:

float specularStrength = 0.5f;

الآن نقوم باحتساب اتجاه العرض والانعكاس المطابق لكل من المحاور.

vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);

float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * light Color;

وفي نهاية المعادلة نقوم بإضافة مزيج الألوان في متغير result جنبا إلى جنب مع الألوان السابقة:

vec3 result = (ambient + diffuse + specular) * object Color;
    color = vec4(result, 1.0f);
تفعيل إضاءة specular.
صورة يظهر من خلالها تفعيل إضاءة specular على مكعب تم إعداده بمكتبة OpenGL.

 

 

المراجع
  1. [1]^ كتاب ـــــــ offline learn OpenGL created by Joey de Vries.
  2. [2]^ إحداثيات مكعب الرسم.
  3. [3]^ ملف lighting.frag.
  4. [4]^ كود Diffuse lighting.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *