الصفحة الرئيسية/ التقنية / كاميرا اوبن جل : دورة OpenGL لغة c++ الدرس الثاني عشر

كاميرا اوبن جل : دورة OpenGL لغة c++ الدرس الثاني عشر


for Large تمت الكتابة بواسطة : محمد



كاميرا اوبن جل هي مجموعة من المصفوفات التي تتم عن طريق منظور إسقاط يساعد كثيرًا في تكوين مشهد العرض.[1]

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

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

حيث من خلاله يتم الكشف عن الكثير من المفاهيم مثل FPS الإطارات للثانية الواحدة وغيرها.[1]

عندما نتحدث عن الكاميرات نحن نتحدث عن نظام من الإحداثيات يتعلق بمنظور الكاميرا الواحدة فقط.[1]

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

وبالتالي لكي نتمكن من تحديد كاميرا اوبن جل فإننا نحتاج إلى الموقع في world space. [1]

 

 


 


مكونات كاميرا اوبن جل[1]

  1. الموقع (Camera position).
  2. اتجاه الكاميرا (Direction).

 


يعد الحصول على موقع الكاميرا أمر سهل للغاية فهو ليس إلا متجه في فضاء العالم 3D يشير بالفعل إلى الكاميرا.[1]

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

ومن خلال مكتبة GLM يمكن الإشارة إلى كاميرا اوبن جل عن طريق المتجه التالي:

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);

نضع بعين الإعتبار أن القيمة الثالثة من الإحداثيات تدل على محور Z في مرحلة world space.[1]

ما يعني أن الكاميرا تحتل الإحداثية 3 في منظور World.[1]

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

سوف نكتفي بتوجيه كاميرا اوبن جل إلى إحداثيات الأصل (0,0,0) ، لكن يجب أن نتذكر بأن طرح متجهين سوف ينتج لنا متجه جديد.

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

بينما يشير موقع كاميرا اوبن جل دائمًا إلى إحداثيات سالبة من العمق z. [1] 

من خلال التطبيق. بالتالي نعمل على تعيين وظيفتي متجهات لتحديد عرض الإتجاه.[1]

glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);

يدل المتجه الأول على هدف الكاميرا ما يعني أن عملية تصوير المشهد سوف تحدث على منطقة الأصل Origin.[1]

بينما يتم احتساب المسافة الواقعة بين الكاميرا ونقطة الأصل عن طريق طرح مكان الكاميرا من منطقة العرض.[1]

نحتاج إلى إرفاق متجهين آخرين لمحور اليمين وهي تتصف بطريقة مبهمة ، حيث يتم تحديد متجه يشير إلى الأعلى في منطقة world space.[1] بالتالي سوف نحصل على متجه يشير إلى إحداثيات x الموجبة.

glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));

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

glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);

 


خاصية Look At


من إحدى مزايا كاميرا اوبن جل هي خاصية Look At والتي تمكنك من إضافة مصفوفة من ثلاثة محاور ، وخاصة مع إجراء تحويل على أي متجه منها من خلال ضرب المصفوفة.[1]

وبالتالي تتكون مصفوفة Look At من ثلاثة متجهات حيث أن R هو المتجه الأيمن بينما يشير المتجه U إلى الأعلى و D هو الإتجاه.[1]

توفر مكتبة GLM جميع مكونات الكاميرات على أنها مصفوفات مجهزة من قبل.[1]

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

يمكن إضافة مصفوفة Look At عن طريق الكود التالي:

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));

 


تحريك الكاميرا


قبل تعيين إدخالات المستخدم نبدأ بعمل تدوير للكاميرا عند طريق بعض معادلات التحريك .[1] حيث نقوم بتعيين إحداثيات X و Z لكل إطار يمثل نقطة في الدائرة.

على سبيل المثال ، يمكن إعادة احتساب كل من قيم x و y فإننا بذلك نتخطى جميع النقاط في دائرة لينتج عنها التدوير حول المشهد.[1]

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

GLfloat radius = 10.0f;
GLfloat camX = sin(glfw GetTime()()) * radius;
GLfloat camZ = cos(glfw GetTime()()) * radius;

نقوم الآن بإجراء تعديل على مصفوفة العرض View التي تم بنائها سابقًا لتصبح على النحو التالي:

glm::mat4 view;
view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0),
glm::vec3(0.0, 1.0, 0.0));

 


التنقل بين النماذج


يعد التأرجح بين النماذج أمر جيد ، لكن يتطلب إعداد نظام الحركة ونظام التسارع.[1]

لكي نبدأ في هذا الإجراء يجب أن نقوم بتعريف نظام الكاميرا أعلى البرنامج على النحو التالي:

glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 camera Front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);

يلي الكود السابق دالة Look At والتي يمكن ارفاقها في الكود لتصبح على النحو التالي:

view = glm::lookAt(cameraPos, cameraPos + camera Front, cameraUp);

لقد قمنا بتعيين موقع الكاميرا في المتجه السابق camera pos بينما يشكل الاتجاه الموقع الحالي + المتجه الذي قمنا بتعيينه.


تفعيل وحدة إدخال الحركة


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

ومع ذلك توفر لنا مكتبة اوبن جل العديد من مزايا الحركة التي يسهل من خلالها السيطرة على مشهد العرض.[1]

بالتالي وأثناء الرجوع إلى دالة key callback فإن الإجراء سيكون على النحو التالي:

GLfloat cameraSpeed = 0.05f;
if(key == GLFW_KEY_W)
cameraPos += cameraSpeed * camera Front;
if(key == GLFW_KEY_S)
cameraPos -= cameraSpeed * camera Front;
if(key == GLFW_KEY_A)
cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) *
cameraSpeed;
if(key == GLFW_KEY_D)
cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) *
cameraSpeed;

تمثل الشروط السابقة الأحداث التي تتولى القيام بها وحدات الإدخال لحظة الضغط على WASD من أزرار الكيبورد.[1]  

 

يشير المصطلح WASD إلى أزرار لوحة المفاتيح W و S و A و D ، حيث أنها تشكل أحداث تتولى كاميرا اوبن جل التعامل معها أثناء عملية الحركة.

 

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

بينما عند الاتجاه والحركة يمينا ويسارا سيتم تطبيق Cross product لإضافة المتجه المناسب.[1]  


انسيابية الحركة


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

بالتالي فقد وفرت لنا مكتبة اوبن جل العديد من المزايا التي تساعد في تمكين حركة مشهد ناعمة.[1]

نقوم الآن بتفعيل متغير من نوع bool في أعلى كود المشروع.[1]

bool keys[1024];

وفي دالة key callback نقوم بإضافة السطور التالية:

if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;

نقوم الآن بإنشاء دالة جديدة باسم do movement ونضع أكواد مصفوفات الحركة بداخلها.

void movement()
{
    // Camera controls
    GLfloat cameraSpeed = 0.01f;
    if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * camera Front;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * camera Front;
    if (keys[GLFW_KEY_A])
        cameraPos -= glm::normalize(glm::cross(camera Front, cameraUp)) *
        cameraSpeed;
    if (keys[GLFW_KEY_D])
        cameraPos += glm::normalize(glm::cross(camera Front, cameraUp)) *
        cameraSpeed;
}

وفي حلقة while الخاصة بالمكتبة نقوم بالإتصال بالدالة ثم نحاول تشغيل التطبيق لنرى فرق النتائج.[1]

وقد تلاحظ بأن وحدة الإدخال تعمل على النحو المطلوب دون تقطّع أثناء عملية التنفيذ.[1]  


تفعيل التسارع


لقد قمنا باستخدام متغير ثابت لسرعة الحركة ويعتبر ذلك جيدًا ، لكن بعض المطورين لديهم أماكن مختلفة ونتائج غير متطابقة في تلك المهمة.[1]

عمومًا فإن تطبيقات الألعاب جميعها تحتفظ بقيمة موحدة تدعى delta time ، وهي متغير يعمل على حفظ الوقت التي تتطلبه عملية الاستدعاء في كل إطار للثانية الواحدة.[1]

يشار إلى delta time على أنها مخزن حقيقي لسرعة اللعبة ومشاهد العرض.

حيث أن احتساب آخر إطار من العرض يستغرق وقت أكثر من المعدل.[1]

مع ذلك يمكننا احتساب وقت دلتا عن طريق إضافة متغيرين في الأعلى من المشروع.[1]

GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;

في حلقة while الخاصة بمكتبة OpenGL نقوم بإضافة معادلة فرق الوقت التالية:

GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;

بالتالي نذهب الآن إلى دالة do movement التي قمنا بإنشائها مؤخرًا ثم نقوم بإضافة المتغير التالي:

GLfloat cameraSpeed = 5.0f * deltaTime;

بعد ذلك نقوم بضرب المتغير بكل من قيم الإتجاه للأمام W والخلف S تمامًا مثل الشكل التالي:

if (keys[GLFW_KEY_W])
        cameraPos += cameraSpeed * camera Front;
    if (keys[GLFW_KEY_S])
        cameraPos -= cameraSpeed * camera Front;

 


النظر بزوايا متعددة


عند استدعاء كاميرا اوبن جل فإن أهم ما يمكن الحصول عليه هو حركة استخدام عالية الكفاءة.

تشكل لوحة المفاتيح إحدى وحدات الإدخال لكن الإعتماد عليها بشكل رئيسي هو أمر ممل للغاية.[1]

على سبيل المثال ، لا توفر لوحة المفاتيح التفات كاميرا متعددة الزوايا ، بينما يمكن فعل ذلك بواسطة الفأرة.

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


زوايا أويلر


يمكن أن تساعدنا زوايا أويلر في عملية الحركة حيث أنها تتكون من ثلاثة قيم قادرة على تمثيل الرسم ثلاثي الأبعاد.[1]

على سبيل المثال يطلق على أسماء القيم pitch و yaw و roll.

يتم استخدام pitch عند التدوير من أعلى إلى الأسفل والعكس صحيح ، بينما يشار إلى yaw على التدوير من اليمين إلى اليسار والعكس صحيح .

وكذلك الأمر بالنسبة لوظيفة  roll حيث تستخدم للتدوير على نحو أسطواني. [1]

من الممكن الاستغناء عن وظيفة roll والقيام بكل من pitch و yaw في هذه الدورة ، بالتالي يمكن التحويل إلى 3D بطريقة سلسة خالية من التعقيد.

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

في حال ما أردنا معرفة مكان الوتر سوف يكون من السهل القيام باحتساب المثلثات.[1]

نقوم الآن بتعريف متغيرين في أعلى المشروع وليكن كما في الكود التالي:

GLfloat yaw = -90.0f;	
GLfloat pitch = 0.0f;

تشير فيمة yaw إلى -90 درجة لأن تطبيق أويلر في أوبن جل يستدعي جعلها سالبة.[1]

بينما في حال قمنا بتعيينها بالقيمة صفر سوف يتم عرض الجهة اليمنى بالوضع الافتراضي.  


تفعيل مدخلات الفأرة


يجب أن نعلم بأن كل من yaw و pitch تتعامل مع الفأرة أو بالأصل عصا joystick. حيث تتولى هذه المدخلات تحديد إتجاهات الحركة بشكل سريع لا تجيده لوحة المفاتيح ببعض الأحيان.

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

بالتالي يتم تحديث كل من yaw و pitch ليتم نقل مصفوفة الإحداثيات الجديدة إلى كاميرا اوبن جل.[1]

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

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

عند تفعيل المشروع الآن نلاحظ بالفعل عملية اختفاء مؤشر الفأرة في صندوق العرض.[1]

بل حتى أنها لا تكاد تخرج من صندوق Frustum في فترة تشغيل البرنامج.[1]

لكي يتم احتساب كل من قيمة pitch و yaw لا بد من إخبار مكتبة GLFW الاستماع إلى حركة الفأرة ، وذلك من خلال الإعلان عن دالة قبل main:

void mouse_callback(GLFWwindow* window, double xpos, double ypos);

يمكنك الاتصال بها عن طريق الوظيفة التالية:

glfwSetCursorPosCallback(window, mouse_callback);

 


متطلبات تفعيل أويلر للفأرة


  • احتساب قيمة الإزاحة من آخر إطار.
  • إضافة نسبة الإزاحة بالنسبة إلى كاميرا اوبن جل.
  • إضافة القيم الثابتة عن أقصى وأقل قيمة لكل من Yaw و Pitch.
  • احتساب إتجاه المتجهات.

بالتالي تتمثل الخطوة الأولى في احتساب الإزاحة للفأرة عن آخر إطار ظهر يعمل على الشاشة.

لا بد من تعيين مركز الفأرة على الشاشة وذلك بقسمة كل من طول وعرض الصندوق على القيمة 2.

سوف نقوم بتعيين ناتج القسمة في متغيرين من نوع GLfloat تماما كما في الكود التالي:

GLfloat lastX = 400, lastY = 300;

بعد تعيين مركز الفأرة على أنها قيمة افتراضية سوف يتسنى لنا القيام بنسخ الكود التالي داخل دالة mouse callback: 

GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos; 

    last X = xpos;
    last Y = ypos;

    GLfloat sensitivity = 0.05f;
    xoffset *= sensitivity;
    yoffset *= sensitivity;

  لاحظ أننا قمنا بإضافة sensitivity وهي المسؤولة عن حركة المشهد بانسيابية ناعمة.

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

الآن نقوم بالتأثير على كل من قيمة yaw و pitch عن طريق إضافة قيم الإزاحة الجديدة.

yaw += xoffset;
pitch += yoffset;

سوف نقوم بتحديد بعض الثوابت التي تعيق المستخدم من تحريك كاميرا اوبن جل بشكل مزعج. [1]

if(pitch > 89.0f)
pitch = 89.0f;
if(pitch < -89.0)
pitch = -89.0;

لاحظ في الكود السابق بأننا لم نقوم بتحديد حركة yaw لذلك نترك الخيار للمستخدم في الحركة.[1]

الخطوة الأخيرة من تفعيل مدخلات الفأرة هي احتساب المتجه الفعلي لكل من قيمتي pitch و yaw كما في الكود التالي:

glm::vec3 front;
    front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));
    front.y = sin(glm::radians(pitch));
    front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
    cameraFront = glm::normalize(front);

         

المراجع

  1. [1]^ كتاب ـــــــ offline learn OpenGL created by Joey de Vries.
  2. [2]^ مصادر المشروع من المراجع.

 

وقت النشر : 2023-02-04 21:21:27 ·

1    التصنيفات






2    مقالات من التقنية

يعتمد هذا الموقع على عرض الإعلانات في تحقيق الدخل ، نشكر تفهمكم الدائم ونتمنى لكم قضاء وقت رائع ... وشكراً :D .