بعد أن رأينا في الجزء الأول فوائد استخدام المصفوفات لإنجاز مهام التحويل الأساسية (الإزاحة، الدوران، التحجيم) على المجسمات في الرسوميات ثلاثية الأبعاد، نكمل اليوم حديثنا عن أمور أكثر تفصيلاً. سننظر في تركيبة المصفوفة ونتعرف على أشكال المصفوفات الشائعة وكيف يتم تركيب التحويلات مع بعضها البعض في مصفوفة واحدة.
بعد قراءة هذه التدوينة، ستستطيع النظر إلى أية مصفوفة في أي برنامج رسوميات ومعرفة تأثيرها وما هي التحويلات التي تقوم بها عموماً. هذه المهارة مفيدة جداً لمبرمج الرسوميات والألعاب (كما تستطيع الاستفادة منها في إبهار أصدقائك على مائدة الطعام). كما وعدنا، لاستيعاب المعلومات الآتية نطلب منك فقط أن تعلم عمليتي الجمع والضرب وأن الإحداثيات الفراغية تتألف من ثلاث محاور.
في ركن مظلم مغبرّ من ذاكرتنا تستقر بعض دروس الرياضيات من أيام المدرسة المتوسطة (الإعدادية) والتي تتحدث عن المصفوفات والمحددات والعمليات عليها. إلا أن دراسة المصفوفات في تلك المرحلة لا تـُظهر كثيراً استخدامات المصفوفات في الهندسة الفراغية، وإنما تركز على استخدامها لحل المعادلات.
لن أكون متعصباً مملاً وأطالبك باستخراج كتاب الرياضيات للسنة الثانية المتوسطة من غيابت الجب لتراجع فصل المصفوفات، بل سأذكر ما يهمنا معرفته عنها الآن وفوراً.
المصفوفة مجموعة من الأرقام المصفوفة بجانب بعضها البعض لتشكل مستطيلاً من الأرقام، وتعرف المصفوفة بأبعادها (طولها وعرضها). مثلاً:
14
|
13
|
12
|
11
|
24
|
23
|
22
|
21
|
34
|
33
|
32
|
31
|
المصفوفة أعلاه هي مصفوفة 4×3. عند كتابة أبعاد المصفوفة، نضع عدد الصفوف على اليسار وعدد الأعمدة على اليمين. في الهندسة الفراغية والرسوميات ثلاثية الأبعاد نستخدم مصفوفات 4×4 في أغلب الحسابات، وأحياناً نستخدم مصفوفات 4×3 وَ 3×3 عند التعامل مع النواظم والمتجهات بدل الإحداثيات المكانية.
ملحوظة: الحسابات الرياضية في هذه المقالة مكتوبة بصيغة مكتبة الرسوميات دايركت ثري دي. تنطبق المبادئ المذكورة هنا على مكتبة أوبن جي إل أيضاً لكن مع تعديل بسيط وهو أن الصفوف تصبح أعمدة والأعمدة تصبح صفوفاً.
والآن لنرى كيف يتم استخدام المصفوفات في إزاحة وتحجيم وتدوير الأجسام. سنبدأ بالإزاحة لأنها الأسهل فهماً. فلنفرض أن لدينا مجسماً مكوناً من عدد كبير من رؤوس المثلثات، ولنقل أن إحداثيات أحد هذه الرؤوس هي [10,0,0]. تخيل معي موقع هذه النقطة في الفراغ، ستلاحظ أنها تقع في مكان ما على مستوى الأرض (بفرض أن المحور Y يرتفع للأعلى). الآن نريد إزاحة المجسم يميناً بمقدار خمس وحدات، ولأعلى ستّ وحدات وللعمق سبع وحدات. مما يعني أنه بعد الإزاحة يجب أن يصبح الرأس إياه في الإحداثيات [15,6,7]. والسؤال هو… ما هي المصفوفة التي تنجز لي هذه المهمة؟ الجواب هو:
0
|
0
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
1
|
7
|
6
|
5
|
كل ما نحتاجه هو ضرب إحداثيات الرأس بهذه المصفوفة، وستنتقل الإحداثيات إلى المكان الصحيح. سنتحدث عن تفاصيل عملية الضرب لاحقاً، أما الآن فدعونا نستمتع سوية بالنظ�� إلى المصفوفة أعلاه. كما تلاحظون، كافة عناصر المصفوفة هي الصفر، باستثناء القطر الذي عناصره تساوي واحد، والسطر الأخير به الأرقام 5 وَ 6 وَ 7. هذا الكلام له دلالة هامة، فالأذكياء منكم لا بد وأن قد لاحظوا أن مقادير الإزاحة التي أردناها موجودة في السطر الأخير بالترتيب XYZ. جميل جداً، وبقليل من التفكير أيضاً، نستطيع أن نتخيل شكل المصفوفة لو جعلنا مقدار الإزاحة صفراً على كافة المحاور:
0
|
0
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
0
|
هذه المصفوفة تسمى مصفوفة الوحدة، وهي عديمة التأثير. فلو ضربنا إحداثيات أي نقطة أو متجه بها فإن الإحداثيات لن تتأثر ولن تتغير. حسناً، فهمنا أن الإزاحة في السطر الأخير. ماذا عن التحجيم مثلاً؟ فلنفرض أننا نريد تقليص حجم المجسم بمقدار النصف على المحور X وبمقدار الثلث على المحور Y وبمقدار الربع على المحور Z. عندها نستخدم مصفوفة كهذه:
0
|
0
|
0
|
0.5
|
0
|
0
|
0.33
|
0
|
0
|
0.25
|
0
|
0
|
1
|
0
|
0
|
0
|
جميييل. الأذكياء منكم مرة أخرى سيلاحظون أن الخلية الأولى من العمود الأول تحوي مقدار التحجيم على المحور X، والخلية الثانية من العمود الثاني تحمل مقدار المحور Y والخلية الثالثة من العمود الثالث تحوي مقدار التحجيم على المحور Z. رائع. لنُزد العملية تعقيداً الآن. نريد أن نقوم بعملية تحجيم بنفس المقادير المذكورة أعلاه، ثم عملية إزاحة بالمقادير المذكورة في المصفوفة الأولى، وكل ذلك في عملية واحدة… فكيف يكون شكل المصفوفة التي تعبر عن هذه "الخلطبيطة"؟ الجواب أنها تكون هكذا:
0
|
0
|
0
|
0.5
|
0
|
0
|
0.33
|
0
|
0
|
0.25
|
0
|
0
|
1
|
7
|
6
|
5
|
ببساطة أخذنا خلايا الإزاحة من المصفوفة الأولى، وأخذنا خلايا التحجيم من المصفوفة الثانية ووضعناهما معاً في مصفوفة واحدة كما ترون، وهذه المصفوفة الآن قادرة على تطبيق عملية تحجيم فإزاحة بضربة واحدة! ليس هذا فقط، بل والأذكياء منكم (تباً لهم!) لاحظوا أيضاً أن كلاً من مقداري التحجيم والإزاحة على المحور X يقعان في العمود الأول، كما أن مقداري التحجيم والإزاحة على المحور Y يقعان في العمود الثاني، والمحور Z في العمود الثالث. هل هي مصادفة؟ كلا بالطبع. من الآن فصاعداً نستطيع أن نقول أن الأرقام في العمود الأول مخصصة للتأثير في المحور X والأرقام في العمود الثاني للمحور Y والأرقام في العمود الثالث للمحور Z.
لننظر أخيراً إلى مصفوفات الدوران. كما هو معروف، الدوران يتم حول محور معين. دعونا نلقي نظرة على مصفوفات الدوران حول المحاور الثلاثة الأساسية (XYZ). انظروا التالي:
0 |
0 |
0 |
1 |
0 |
0.71 |
0.71 |
0 |
0 |
0.71 |
0.71- |
0 |
1 |
0 |
0 |
0 |
|
0
|
0.71-
|
0
|
0.71
|
0
|
0
|
1
|
0
|
0
|
0.71
|
0
|
0.71
|
1
|
0
|
0
|
0
|
|
0
|
0
|
0.71
|
0.71
|
0
|
0
|
0.71
|
0.71-
|
0
|
1
|
0
|
0
|
1
|
0
|
0
|
0
|
|
المصفوفة اليمنى هي لتدوير المجسم بمقدار 45 ْ حول المحور X. المصفوفة الوسطى لتدوير المجسم بنفس المقدار لكن حول المحور Y. وأخيراً المصفوفة اليسرى للمحور Z. كما تلاحظون، هناك نسق واضح في الخلايا. ففي مصفوفة التدوير حول X، نلاحظ أن العمود الخاص بالمحور X لم تتغير عناصره. نفس الملاحظة حول مصوفة التدوير حول Y وعناصر عمود المحور Y، وكذلك Z.
لنخرج الآن من هذه الملاحظة بقاعدة: محور الدوران في مصفوفة دوران هو العمود الذي لم تتغير قيم عناصره.
طبعاً هذا الكلام ينطبق فقط على الدورانات حول المحاور الرئيسية XYZ. إن كان الدوران حول محور آخر فإن المصفوفة الناتجة ستكون أكثر تعقيداً، وسيصعب علينا استنتاج محور الدوران بمجرد لمحة خاطفة على عناصر المصفوفة.
وأخيراً، فلننظر إلى مصفوفة تجمع كافة العمليات المذكورة سابقاً. مصفوفة واحدة تقوم بالتحجيم، فالتدوير حول X ثم Y ثم Z وأخيراً تقوم بإزاحة المجسم. سنستعمل نفس المقادير من المصفوفات السابقة. المصفوفة الناتجة هي:
0
|
0.35-
|
0.25
|
0.25
|
0
|
0.16
|
0.28
|
0.05-
|
0
|
0.12
|
0.04-
|
0.21
|
1
|
7
|
6
|
5
|
حسناً. الآن حتى الأذكياء منكم لن يستطيعوا إيجاد نسق واضح للعناصر في الزاوية العليا اليسرى من المصفوفة. الشيء الوحيد الذي نستطيع ملاحظته هو أن العمود الأيمن دائماً ثابت لا يتغير، وأن السطر الأخير دائماً يحوي الإزاحة ولا يتأثر عند دمج المصفوفة مع دوران أو تحجيم.
أعتقد أنكم الآن أصبحتم قادرين على تمييز نوع المصفوفة من مجرد النظر إلى عناصرها ومقارنتها مع أحد الأنساق الواردة أعلاه. بقيت لدينا مصفوفتان شائعتان نودّ أن نتعرف عليهما أيضاً، لكن هذه قصة أخرى (على قول د. أحمد خالد توفيق). أما الآن فأترككم مع الكود الذي يقوم بحساب المصفوفة الشاملة التي نراها أعلاه. أنصحك أيها القارئ الكريم بتشغيل هذا الكود وتعديله وملاحظة النتائج. حاول أن تحصل على نفس المصفوفات التي ذكرناها في هذه التدوينة، وإن واجهت أية متاعب في ذلك فاطرح سؤالك في الشبكة العربية لمطوري الألعاب!
إلى اللقاء…
#include <stdio.h>
#include <d3dx9math.h>
#pragma comment(lib,"d3dx9")
void main(void)
{
D3DXMATRIX mat,matS,matRX,matRY,matRZ,matT;
D3DXMatrixScaling(&matS,0.5f,0.33f,0.25f); // مصفوفة التحجيم
D3DXMatrixRotationX(&matRX,D3DXToRadian(45.0f)); // مصفوفة الدوران حول محور السين
D3DXMatrixRotationY(&matRY,D3DXToRadian(45.0f)); // مصفوفة الدوران حول محور الصاد
D3DXMatrixRotationZ(&matRZ,D3DXToRadian(45.0f)); // مصفوفة الدوران حول محور العين
D3DXMatrixTranslation(&matT,5.0f,6.0f,7.0f); // مصفوفة الإزاحة
mat = matS * matRX * matRY * matRZ * matT; // المصفوفة الشاملة
// طباعة عناصر المصفوفة الشاملة على الشاشة
printf("%0.2f , %0.2f , %0.2f , %0.2f\n",mat._11,mat._12,mat._13,mat._14);
printf("%0.2f , %0.2f , %0.2f , %0.2f\n",mat._21,mat._22,mat._23,mat._24);
printf("%0.2f , %0.2f , %0.2f , %0.2f\n",mat._31,mat._32,mat._33,mat._34);
printf("%0.2f , %0.2f , %0.2f , %0.2f\n",mat._41,mat._42,mat._43,mat._44);
}