و آخر مرة تحدثنا عن الرسوم البيانية ومسارات لوقف الرسوم المتحركة، واليوم سيكون حول المصفوفة. سنكتشف كيفية إنشاء تحويلات أساسية في CSS و SVG و WebGL ، وبناء عرض للعالم ثلاثي الأبعاد على الشاشة بأيدينا ، على طول الطريق لرسم موازٍ بأداة مثل Three.js ، وكذلك تجربة المرشحات للصور ومعرفة لأن مثل هذا السحر يكمن في جوهرها.
دعني أذكرك أنه في هذه السلسلة من المقالات ، نتعرف على أشياء مختلفة من مجال الرياضيات تخيف مصممي التخطيط ، ولكنها يمكن أن تكون مفيدة في حل مشاكل العمل. نحاول تجنب التنظير غير الضروري ، مفضلين الصور والتفسيرات على الأصابع ، مع التركيز على التطبيقات العملية في الواجهة الأمامية. في هذا الصدد ، قد لا تكون الصياغات في بعض الأماكن دقيقة تمامًا من وجهة نظر الرياضيات ، أو قد لا تكون كاملة تمامًا. الغرض من هذه المقالة هو إعطاء فكرة عامة عما يحدث ومن أين نبدأ إذا حدث شيء ما.
توجد نصوص لتوليد الصور بأسلوب سلسلة المقالات هذه على GitHub ، لذلك إذا كنت تريد أن ترى نفس الشيء لنفسك ، فأنت تعرف ما يجب فعله.
تعاريف قليلة
المصفوفة في الرياضيات هي مثل هذا التجريد ، يمكننا القول أنها نوع بيانات بمعنى ما ، وكتابتها في شكل جدول مستطيل. يمكن أن يكون عدد الأعمدة والصفوف أي شيء ، لكننا على الويب نتعامل دائمًا تقريبًا مع المصفوفات المربعة 2x2 و 3x3 و 4x4 و 5x5.
نحتاج أيضًا إلى تعريف مثل المتجه. أعتقد أنه من الهندسة المدرسية يمكنك تذكر التعريف المرتبط بكلمات "الطول" و "الاتجاه" ، ولكن بشكل عام في الرياضيات يمكن تسمية الكثير من الأشياء بالمتجه. على وجه الخصوص ، سوف نتحدث عن المتجه كمجموعة مرتبة من القيم. على سبيل المثال ، إحداثيات النموذج (س ، ص) أو (س ، ص ، ض) ، أو لون بالتنسيق (ص ، ز ، ب) أو (ح ، ق ، ل ، أ) ، إلخ. اعتمادًا على عدد العناصر المضمنة في مثل هذه المجموعة ، سنتحدث عن متجه بعد واحد أو آخر: إذا كان هناك عنصران ثنائي الأبعاد ، فإن ثلاثة عناصر ثلاثية الأبعاد ، إلخ. أيضًا ، في إطار الموضوعات قيد الدراسة ، قد يكون من الملائم أحيانًا التفكير في المتجه كمصفوفة بأحجام 1x2 ، 1x3 ، 1x4 ، إلخ. من الناحية الفنية ، يمكننا قصر أنفسنا على مصطلح "المصفوفة" فقط ، لكننا سنستمر في استخدام كلمة "ناقل" لفصل هذين المفهومين عن بعضهما البعض ،على الأقل بالمعنى المنطقي.
بالنسبة للمصفوفات وكذلك للمتجهات ، يتم تحديد عمليات مختلفة يمكن إجراؤها باستخدامها. على وجه الخصوص ، الضرب. نحن نضاعفهم باستمرار فيما بيننا. خوارزمية الضرب نفسها ليست معقدة للغاية ، على الرغم من أنها قد تبدو مربكة بعض الشيء:
function multiplyMatrices(a, b) {
const m = new Array(a.length);
for (let row = 0; row < a.length; row++) {
m[row] = new Array(b[0].length);
for (let column = 0; column < b[0].length; column++) {
m[row][column] = 0;
for (let i = 0; i < a[0].length; i++) {
m[row][column] += a[row][i] * b[i][column];
}
}
}
return m;
}
لكن بالنسبة لنا ، في الواقع ، ليس من المهم أن نتذكر باستمرار مبدأ عملها عند حل المشكلات اليومية. هنا نذكرها بالأحرى للاكتمال ، لتوفير سياق لمزيد من الأمثلة.
عند العمل مع كيانات معقدة في الرياضيات ، من المفيد جدًا التجريد. كما هو الحال هنا - غالبًا ما نتحدث عن الضرب ، لكننا لن ننتبه إلى نوع العمليات الحسابية بأي ترتيب تحدث هناك. نعلم أن عملية الضرب محددة - وهذا يكفي للوظيفة.
سنستخدم المصفوفات المربعة فقط في مجموعة محددة جدًا من المسائل ، لذا ستكفي مجموعة من القواعد البسيطة:
- يمكنك فقط ضرب المصفوفات من نفس البعد.
- نضرب المصفوفة في المصفوفة - نحصل على المصفوفة.
- يمكنك ضرب مصفوفة في متجه - نحصل على متجه.
- ترتيب الضرب مهم.
سنستخدم الضرب من اليسار إلى اليمين بشكل أساسي ، لأنه أكثر شيوعًا ومناسب للتفسيرات ، ولكن في بعض الكتب أو المكتبات قد تصادف تدوينًا من اليمين إلى اليسار ، وستنعكس جميع المصفوفات قطريًا. هذا لا يؤثر بأي شكل من الأشكال على جوهر التلاعب الذي يحدث ، لذلك لن نتطرق إلى هذا ، ولكن إذا قمت بنسخ ولصق شيء ما ، فاحرص على الانتباه.
أيضًا لمزيد من العمل ، سنحتاج إلى مفهوم مثل مصفوفة الهوية. هذه مصفوفة بها آحاد في القطر الرئيسي والأصفار في جميع الخلايا الأخرى. تم بناء خوارزمية الضرب بطريقة ضرب مصفوفة الهوية في مصفوفة أخرى - نحصل على نفس المصفوفة. أو ناقل ، إذا كنا نتحدث عن ناقل. بمعنى آخر ، تلعب مصفوفة الهوية دور واحد في الضرب المعتاد للأرقام. هذا شيء محايد "لا يؤثر على أي شيء" عند مضاعفته.
وآخر شيء نحتاجه هو المثال الموضح في الصورة أعلاه. هذا مثل حالة خاصة من ضرب مصفوفة في متجه ، عندما يكون الصف الأخير من المصفوفة "قطعة من مصفوفة الوحدة" ، والعنصر الأخير من المتجه يساوي أيضًا 1.
في هذا المثال ، نستخدم الحروف (س ، ص) ، وكما قد تكون خمنت ، ستركز المناقشة التالية على الإحداثيات ثنائية الأبعاد. لكن لماذا نضيف إحداثيًا ثالثًا ونتركه واحدًا؟ - أنت تسأل. الأمر كله يتعلق بالراحة ، أو حتى أفضل من التنوع. غالبًا ما نضيف إحداثيات +1 لتبسيط العمليات الحسابية ، والعمل مع 2D يتوافق مع مصفوفات 3x3 ، والعمل مع 3D - مع مصفوفات 4x4 ، والعمل مع 4D ، على سبيل المثال ، مع الألوان بالتنسيق (r ، g ، b ، a) مع المصفوفات 5 × 5. للوهلة الأولى ، تبدو هذه فكرة مجنونة ، لكن لاحقًا سنرى كيف أنها توحد جميع العمليات. إذا كنت تريد أن تفهم هذا الموضوع بمزيد من التفصيل ، يمكنك جوجل التعبير "الإحداثيات الموحدة".
لكن نظرية كافية ، دعنا ننتقل إلى الممارسة.
1. التحولات الأساسية في رسومات الحاسوب
لنأخذ التعبيرات من المثال أعلاه ونراها كما هي ، خارج سياق المصفوفات:
newX = a*x + b*y + c
newY = d*x + e*y + f
يمكنك التفكير في هذا على أنه المعادلات البارامترية التي رسمناها في المرة السابقة. ماذا يحدث إذا حددت هذه المعاملات أو تلك فيها؟ لنبدأ بالخيار التالي:
newX = 1*x + 0*y + 0 = x
newY = 0*x + 1*y + 0 = y
لا شيء يتغير هنا - الإحداثيات الجديدة (س ، ص) مطابقة للإحداثيات القديمة. إذا عوضنا بهذه المعامِلات في المصفوفة ودرسناها عن كثب ، فسنجد أننا نحصل على مصفوفة الوحدة.
ماذا يحدث إذا أخذنا معاملات أخرى؟ على سبيل المثال ، هذه هي:
newX = 1*x + 0*y + A = x + A
newY = 0*x + 1*y + 0 = y
سوف نحصل على تعويض على طول المحور X. ولكن ماذا يمكن أن يحدث هنا أيضًا؟ إذا لم يكن هذا واضحًا لك ، فمن الأفضل العودة إلى الجزء الأول ، حيث تحدثنا عن المخططات والمعاملات.
بتغيير هذه المعاملات الستة - أ ، ب ، ج ، د ، هـ ، و - وملاحظة التغييرات في x و y ، سنصل عاجلاً أم آجلاً إلى أربعة من مجموعاتها ، والتي تبدو مفيدة وملائمة للاستخدام العملي. دعنا نكتبها على الفور في شكل مصفوفات ، ونرجع إلى المثال الأصلي:
أسماء هذه المصفوفات تتحدث عن نفسها. عند ضرب هذه المصفوفات بالمتجهات بإحداثيات بعض النقاط ، والأشياء الموجودة في المشهد ، إلخ. نحصل على إحداثيات جديدة لهم. علاوة على ذلك ، نحن نعمل مع تحويلات بديهية - الحركة والقياس والتدوير والإمالة ، وتحدد المعاملات شدة هذا التحول أو ذاك على طول المحاور المقابلة.
غالبًا ما يكون من الملائم التفكير في المصفوفات على أنها تحويلات لشيء ما ، مثل الإحداثيات. هذه كلمة أخرى عن التجريدات.
يمكن تكديس التحويلات. فيما يتعلق بالمصفوفات ، سنستخدم عملية الضرب ، والتي قد تكون مربكة بعض الشيء ، لكن هذه لغة منطوقة. إذا احتجنا إلى إزاحة كائن ما إلى الجانب وزيادة حجمه ، فيمكننا أخذ مصفوفة للإزاحة ومصفوفة للقياس وضربها. ستكون النتيجة مصفوفة تعطي كلاً من الإزاحة والقياس في نفس الوقت. يبقى فقط لتحويل كل نقطة من وجوهنا بمساعدتها.
التحولات الأساسية في CSS
لكن هذه كلها كلمات. دعونا نرى كيف تبدو في الواجهة الأمامية الحقيقية. في CSS ، لدينا (فجأة) دالة مصفوفة. يبدو شيئًا كهذا في سياق الكود:
.example {
transform: matrix(1, 0, 0, 1, 0, 0);
}
يتم تغطية العديد من المبتدئين الذين يرون ذلك لأول مرة بالسؤال - لماذا توجد ستة معايير؟ هذا غريب. كان من الممكن أن يكون 4 أو 16 - ما زال لم يذهب إلى أين ، ولكن 6؟ مالذي يفعلونه؟
لكن في الواقع ، كل شيء بسيط. هذه المعلمات الستة هي نفس المعاملات التي قمنا من خلالها بتجميع المصفوفات للتحولات الأساسية. ولكن لسبب ما تم ترتيبها بترتيب مختلف: توجد
أيضًا في CSS دالة matrix3d من أجل تعيين تحويل ثلاثي الأبعاد باستخدام مصفوفة. هناك بالفعل 16 معلمة ، بالضبط لإنشاء مصفوفة 4x4 (لا تنسَ أننا نضيف بُعدًا +1).
يتم إنشاء المصفوفات الخاصة بالتحويلات ثلاثية الأبعاد الأساسية بنفس طريقة إنشاء المصفوفات ثنائية الأبعاد ، ولا يلزم إلا المزيد من المعاملات لترتيبها ليس في إحداثيات اثنين ، ولكن في ثلاثة إحداثيات. لكن المبادئ هي نفسها.
بطبيعة الحال ، سيكون من الغريب في كل مرة سياج المصفوفة ومراقبة الموضع الصحيح للمعاملات عند العمل مع التحولات البسيطة في CSS. عادة ما نحاول نحن المبرمجين أن نجعل حياتنا أسهل. لذلك لدينا الآن وظائف قصيرة في CSS لإنشاء تحويلات فردية - translateX ، و translateY ، و scaleX ، وما إلى ذلك. عادةً ما نستخدمها ، لكن من المهم أن نفهم أنها في الداخل تخلق نفس المصفوفات التي تحدثنا عنها ، ببساطة تخفي هذه العملية عنا خلف طبقة أخرى من التجريد.
توجد نفس تحويلات الترجمة والتدوير والقياس والانحراف ، بالإضافة إلى وظيفة المصفوفة الشاملة لتحديد التحويلات ، في SVG. تختلف الصيغة قليلاً ، لكن الجوهر هو نفسه. عند العمل باستخدام رسومات ثلاثية الأبعاد ، على سبيل المثال مع WebGL ، سنلجأ أيضًا إلى نفس التحولات. لكن المزيد حول ذلك لاحقًا ، من المهم الآن أن نفهم أنهم موجودون في كل مكان ، ويعملون في كل مكان وفقًا لنفس المبدأ.
المجاميع الجزئية
دعونا نلخص ما ورد أعلاه:
- يمكن استخدام المصفوفات كتحويلات للمتجهات ، خاصة لإحداثيات بعض الكائنات على الصفحة.
- نعمل دائمًا تقريبًا باستخدام مصفوفات مربعة ونضيف بُعدًا +1 لتبسيط العمليات الحسابية وتوحيدها.
- هناك 4 تحويلات أساسية - الترجمة والتدوير والقياس والانحراف. يتم استخدامها في كل مكان من CSS إلى WebGL وتعمل بطريقة مماثلة في كل مكان.
II. بناء المشهد 3D DIY
سيكون التطور المنطقي للموضوع حول تحويل الإحداثيات هو إنشاء مشهد ثلاثي الأبعاد وعرضه على الشاشة. بشكل أو بآخر ، توجد هذه المهمة عادةً في جميع دورات رسومات الكمبيوتر ، ولكنها ليست كذلك في الدورات التدريبية الأمامية. سنرى ، ربما مبسطًا بعض الشيء ، ولكن مع ذلك نسخة كاملة من كيفية صنع كاميرا بزوايا رؤية مختلفة ، وما هي العمليات اللازمة لحساب إحداثيات جميع الكائنات على الشاشة وإنشاء صورة ، وكذلك رسم أوجه التشابه مع Three.js - الأكثر شيوعًا أداة للعمل مع WebGL.
يجب أن يظهر هنا سؤال معقول - لماذا؟ لماذا تتعلم أن تفعل كل شيء بيديك إذا كان لديك أداة جاهزة؟ الجواب يكمن في مشاكل الأداء. ربما تكون قد زرت مواقع بها مسابقات مثل Awwwards و CSS Design Awards و FWA وما شابه ذلك. تذكر كيف أداء المواقع المشاركة في هذه المسابقات؟ نعم ، هناك تقريبًا كل شخص يتباطأ ، ويتأخر عند التحميل ويجعل الكمبيوتر المحمول همهمة مثل الطائرة! نعم ، بالطبع ، السبب الرئيسي عادةً هو التظليل المعقد أو التلاعب المفرط في DOM ، لكن السبب الثاني هو الكم الهائل من البرامج النصية. هذا له تأثير كارثي على تحميل مثل هذه المواقع. عادة ما يحدث كل شيء على هذا النحو: تحتاج إلى القيام بشيء ما على WebGL - خذ نوعًا من المحرك ثلاثي الأبعاد (+ 500 كيلوبايت) وبعض المكونات الإضافية له (+ 500 كيلوبايت) ؛تحتاج إلى جعل جسم ما يسقط أو يتطاير شيء - يأخذون محركًا فيزيائيًا (+ 1 ميجابايت ، أو حتى أكثر) ؛ تحتاج إلى تحديث بعض البيانات على الصفحة - حسنًا ، أضف بعض إطار عمل SPA مع عشرات الإضافات (+ 500KB) ، إلخ. وبهذه الطريقة ، تتم كتابة عدة ميغا بايت من البرامج النصية ، والتي لا تحتاج فقط إلى تنزيلها بواسطة العميل (وهذا بالإضافة إلى الصور الكبيرة) ، ولكن أيضًا سيقوم المتصفح بعمل شيء معهم بعد التنزيل - فهي لا تنتقل إليها فقط. علاوة على ذلك ، في 99٪ من الحالات ، حتى تعمل البرامج النصية ، لن يرى المستخدم كل الجمال الذي سيحتاج إلى إظهاره منذ البداية.ما يحتاجه العميل لتنزيله (وهذا بالإضافة إلى الصور الكبيرة) ، لذلك سيفعل المتصفح شيئًا معهم بعد التحميل - فهم لا يأتون إليه لسبب ما. علاوة على ذلك ، في 99٪ من الحالات ، حتى تعمل البرامج النصية ، لن يرى المستخدم كل الجمال الذي سيحتاج إلى إظهاره منذ البداية.ما يحتاجه العميل لتنزيله (وهذا بالإضافة إلى الصور الكبيرة) ، لذلك سيفعل المتصفح شيئًا معهم بعد التحميل - فهم لا يأتون إليه لسبب ما. علاوة على ذلك ، في 99٪ من الحالات ، حتى تعمل البرامج النصية ، لن يرى المستخدم كل الجمال الذي سيحتاج إلى إظهاره منذ البداية.
هناك اعتقاد شائع بأن كل 666 كيلو بايت من البرامج النصية في الإنتاج تزيد من وقت تحميل الصفحة بوقت كافٍ للمستخدم لإرسال مطور موقع إلى الدائرة التالية من الجحيم. يزن Three.js في التكوين الأدنى 628 كيلو بايت ...
علاوة على ذلك ، غالبًا لا تتطلب المهام ببساطة توصيل الأدوات المعقدة. على سبيل المثال ، لإظهار طائرتين بهما زخارف في WebGL وإضافة تظليلين بحيث تتباعد الصور في موجات ، لا تحتاج إلى Three.js بأكملها. ولسقوط جسم ما ، لا تحتاج إلى محرك فيزيائي كامل. نعم ، من المحتمل أن يؤدي ذلك إلى تسريع عملك ، خاصة إذا كنت معتادًا على ذلك ، لكنك ستدفع مقابل ذلك مع وقت المستخدمين. هنا يقرر الجميع لنفسه ما هو أكثر فائدة له.
تنسيق سلسلة التحول
في الواقع ، جوهر تحويلات التنسيق لإنشاء مشهد ثلاثي الأبعاد على شاشتنا بسيط للغاية ، لكننا سنستمر في تحليله في خطوات ، لأنه على الأرجح ، ستكون هذه العملية شيئًا جديدًا للعديد من مصممي التخطيط.
هذا كل شيء. لنفترض أن المصمم قد رسم نموذجًا ثلاثي الأبعاد. فليكن مكعبًا (في الأمثلة سنستخدم أبسط الإنشاءات حتى لا نعقد الرسم التوضيحي فجأة):
ما هو شكل هذا النموذج؟ في الواقع ، إنها مجموعة من النقاط في بعض أنظمة الإحداثيات ومجموعة من العلاقات بينها بحيث يمكنك تحديد النقاط التي يجب أن تقع المستويات بينها. ستقع مسألة المستويات في سياق WebGL على عاتق المتصفح نفسه ، والإحداثيات مهمة بالنسبة لنا. تحتاج إلى معرفة كيفية تحويلها بالضبط.
النموذج ، كما قلنا ، لديه نظام إحداثيات. لكن عادة ما نريد أن يكون لدينا الكثير من العارضات ، نريد أن نجعل المشهد معهم. سيكون للمشهد ، عالمنا ثلاثي الأبعاد ، نظام إحداثيات عالمي خاص به. إذا فسرنا إحداثيات النموذج على أنها إحداثيات عالمية ، فسيتم تحديد موقع نموذجنا كما لو كان "في مركز العالم". بعبارة أخرى ، لن يتغير شيء. لكننا نريد إضافة العديد من النماذج إلى أماكن مختلفة في عالمنا ، شيء مثل هذا:
ماذا أفعل؟ تحتاج إلى تحويل إحداثيات كل نموذج فردي إلى إحداثيات عالمية. يتم تحديد موضع النموذج في الفضاء من خلال التعويضات والدوران والقياس - لقد رأينا بالفعل هذه التحولات الأساسية. نحتاج الآن إلى بناء مصفوفة تحويل لكل نموذج ، والتي ستخزن في حد ذاتها هذه المعلومات فقط حول مكان النموذج بالنسبة للعالم ، وكيف يتم تدويره.
على سبيل المثال ، بالنسبة للمكعبات ، سيكون هناك تقريبًا المصفوفات التالية:
// .
// « » .
const modelMatrix1 = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
// , X.
const modelMatrix2 = [
[1, 0, 0, 1.5],
[0, 1, 0, 0 ],
[0, 0, 1, 0 ],
[0, 0, 0, 1 ]
];
// , X .
const modelMatrix3 = [
[1, 0, 0, -1.5],
[0, 1, 0, 0 ],
[0, 0, 1, 0 ],
[0, 0, 0, 1 ]
];
علاوة على ذلك ، سنتصرف على النحو التالي تقريبًا:
{
= [ ] *
}
وفقًا لذلك ، يحتاج كل نموذج إلى مصفوفة خاصة به.
وبالمثل ، يمكنك عمل سلسلة من بعض الكائنات. إذا كان على الطائر أن يرفرف بجناحيه ، فسيكون من المناسب ترجمة إحداثيات نقاط الجناح إلى إحداثيات الطائر ، ثم إلى إحداثيات العالم العالمي. سيكون من الأسهل بكثير تخمين مسار الجناح مباشرة في الإحداثيات العالمية. ولكن هذا هو الحال ، بالمناسبة.
بعد ذلك ، عليك أن تقرر الجانب الذي سننظر فيه إلى العالم. أحتاج كاميرا.
الكاميرا هي مثل هذا التجريد ، مثل تقليد الكاميرا المادية. لديها إحداثيات وبعض زوايا الميل التي تحدد موقعها في الإحداثيات العالمية. مهمتنا هي تحويل مجموعة من الإحداثيات العالمية الآن إلى نظام إحداثيات للكاميرا. المبدأ هو نفسه كما في المثال السابق:
{
= [ ] *
}
, , . !
دعنا ننظر إلى المشهد من المكان الذي توجد فيه الكاميرا الشرطية:
الآن ، بعد تحويل جميع النقاط إلى نظام إحداثيات الكاميرا ، يمكننا ببساطة تجاهل المحور Z ، وتفسير محوري X و Y على أنهما "أفقي" و "عمودي". إذا قمت برسم جميع نقاط النماذج على الشاشة ، فستحصل على صورة ، كما في المثال - لا يوجد منظور ومن الصعب فهم أي جزء من المشهد يقع بالفعل في الإطار. الكاميرا بحجم لا نهائي في كل الاتجاهات. يمكننا بطريقة ما تعديل كل شيء بحيث يناسب ما نحتاجه على الشاشة ، ولكن سيكون من الجيد أن يكون لدينا طريقة عالمية لتحديد أي جزء من المشهد يقع في مجال رؤية الكاميرا وأي جزء منها لن يقع.
باستخدام الكاميرات المادية ، يمكننا التحدث عن شيء مثل زاوية الرؤية. لماذا لا تضيفه هنا أيضًا؟
لهذا نحتاج إلى مصفوفة أخرى - مصفوفة الإسقاط. بشكل عام ، يمكن بناؤها بطرق مختلفة. اعتمادًا على ما يؤخذ كمعلمات أولية ، تحصل على نوع مختلف قليلاً من هذه المصفوفة بالذات ، لكن الجوهر سيكون هو نفسه. سنأخذ النسخة المبسطة قليلاً التالية:
// 90
const s = 1 / (Math.tan(90 * Math.PI / 360));
const n = 0.001;
const f = 10;
const projectionMatrix = [
[s, 0, 0, 0],
[0, s, 0, 0],
[0, 0, -(f)/(f-n), -f*n/(f-n)],
[0, 0, -1, 0]
];
تحتوي مصفوفة الإسقاط ، بطريقة أو بأخرى ، على ثلاث معلمات - هذه هي زاوية الرؤية ، وكذلك الحد الأدنى والحد الأقصى للمسافة للنقاط التي يسير العمل بها. يمكن التعبير عنها بطرق مختلفة ، وتستخدم بطرق مختلفة ، ولكن هذه المعلمات ستكون في هذه المصفوفة في أي حال.
أفهم أنه ليس من الواضح أبدًا سبب ظهور المصفوفة بهذا الشكل تمامًا ، ولكن لاشتقاقها مع التفسيرات ، فأنت بحاجة إلى صيغ من 2-3 صفحات. هذا يعيدنا مرة أخرى إلى فكرة أنه من المفيد التجريد - يمكننا العمل بنتيجة أكثر عمومية ، دون الخوض في التفاصيل الصغيرة حيث لا يكون من الضروري حل مشكلة معينة.
الآن ، قم بإجراء التحولات المألوفة بالفعل:
{
= [ ] *
}
سوف ندخل في مجال رؤيتنا بالضبط ما نتوقعه. زيادة الزاوية - نرى أكثر من أي شيء على الجانبين ، وننقص الزاوية - نرى فقط ما هو أقرب إلى الاتجاه الذي يتم توجيه الكاميرا إليه. ربح!
لكن في الواقع لا. لقد نسينا المنظور. هناك حاجة إلى صورة ميؤوس منها في أماكن قليلة ، لذلك تحتاج إلى إضافتها بطريقة ما. وهنا ، فجأة ، لسنا بحاجة إلى مصفوفات. تبدو المهمة صعبة للغاية ، ولكن يتم حلها من خلال التقسيم العادي للإحداثيات X و Y بواسطة W لكل نقطة:
* هنا قمنا بتحويل الكاميرا إلى الجانب وإضافة خطوط متوازية "على الأرض" لتوضيح مكان ظهور هذا المنظور.
من خلال اختيار المعاملات حسب ذوقك ، سنحصل على خيارات منظور مختلفة. بمعنى ما ، تحدد المعاملات هنا نوع العدسة ، ومدى "تسطيح" المساحة المحيطة بها.
الآن لدينا صورة كاملة. يمكنك أخذ إحداثيات X و Y لكل نقطة ورسمها على الشاشة بأي طريقة تريدها.
بشكل عام ، هذا كافٍ لبناء مشهد ، لكن في المشاريع الحقيقية قد تواجه أيضًا تحولًا إضافيًا متعلقًا بالقياس في النهاية. الفكرة هي أنه بعد الإسقاط نحصل على الإحداثيات (س ، ص) في حدود 1 ، الإحداثيات المعيارية ، ثم نقوم بضربها في حجم الشاشة أو قماش الرسم ، للحصول على الإحداثيات للعرض على الشاشة. تزيل هذه الخطوة الإضافية حجم اللوحة القماشية من جميع العمليات الحسابية ، وتتركها فقط في النهاية. هذا مناسب في بعض الأحيان.
هنا ربما يكون لديك صداع من كمية المعلومات ، لذلك دعونا نبطئ ونكرر كل التحولات في مكان واحد:
إذا جمعت هذه التحولات في واحدة ، تحصل على محرك صغير.
كيف تبدو في Three.js؟
الآن بعد أن فهمنا من أين أتى هذا المحرك الصغير ، دعنا نلقي نظرة على مثال على تظليل قمة الرأس الافتراضي في Three.js الذي "لا يفعل شيئًا":
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
أو بشكل كامل:
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}
هل يذكرك بأي شيء؟ نعم ، هذا هو هذا المحرك بالذات. ونعني بعبارة "لا تفعل شيئًا" أنها تقوم فقط بكل أعمال إعادة حساب الإحداثيات ، بناءً على المصفوفات التي تم تمريرها بعناية من Three.js. لكن لا أحد يهتم بعمل هذه المصفوفات بأيديهم ، أليس كذلك؟
أنواع الكاميرات في رسومات الحاسوب و Three.js
موضوع أنواع الكاميرات لا يتعلق بشكل مباشر بالمصفوفات ، لكن مع ذلك سنخصص له دقيقتين ، حيث أننا ما زلنا نتحدث عن Three.js ، وأحيانًا يكون لدى الناس فوضى في رؤوسهم حول هذا الموضوع.
الكاميرا هي فكرة مجردة. يساعدنا في التفكير في العالم ثلاثي الأبعاد بنفس الطريقة التي نفكر بها في العالم الحقيقي. كما قلنا ، للكاميرا موقع في الفضاء ، والاتجاه الذي تنظر فيه ، وزاوية الرؤية. يتم تحديد كل هذا باستخدام مصفوفتين ، وربما تقسيم إضافي للإحداثيات لإنشاء منظور.
في رسومات الكمبيوتر ، لدينا نوعان من الكاميرات - "ذات منظور" و "بدون منظور". هذان نوعان مختلفان تمامًا من الكاميرات من الناحية الفنية ، ويتطلبان إجراءات مختلفة للحصول على صورة. و هذا كل شيء. لا يوجد شيء آخر. كل شيء آخر هو مجموعاتها ، بعض التجريدات الأكثر تعقيدًا. على سبيل المثال ، تمتلك Three.js كاميرا ستريو - هذه ليست نوعًا من الكاميرات "الخاصة" من الناحية الفنية ، ولكنها مجرد صورة تجريدية - هناك كاميرتان متباعدتان قليلاً في الفضاء وتقعان بزاوية:
لكل نصف شاشة ، نأخذ الكاميرا الخاصة بنا ويتضح صورة ستيريو. و CubeCamera عبارة عن 6 كاميرات عادية موجودة على جوانب مختلفة من نقطة ، لا أكثر.
ماذا بعد؟
الخطوة التالية ، بعد الحصول على إحداثيات الكائنات ، هي تحديد الأشياء التي ستكون مرئية وأيها ستكون مخفية خلف الكائنات الأخرى. في سياق WebGL ، سيقوم المستعرض بذلك بنفسه. حسنًا ، ستكون هناك أيضًا مهام ذات صلة ، مثل فرض القوام عليها ، وحساب الإضاءة حسب الأعراف ، والظلال ، والمعالجة اللاحقة للصورة ، وما إلى ذلك. لكننا قمنا بالفعل بالجزء الأهم والأكثر صعوبة في الفهم. انه لشيء رائع. في الواقع ، لا تحتاج العديد من الأشياء التوليدية إلى هذه التركيبات والإضاءة ، لذلك قد تكون المعرفة المكتسبة الآن كافية للعمل معهم.
بالمناسبة ، فإن إلقاء ظل من كائن على مستوى ليس أكثر من إسقاط لهذا الكائن على هذا المستوى بالذات بزاوية معينة ، متبوعًا بمزج الألوان. تتشابه العملية بطبيعتها مع الكاميرا ، ولكنها تضيف أيضًا زاوية بين مستوى الإسقاط و "اتجاه الرؤية".
حول نسيج وتأثيرات الصور على WebGL ، بما في ذلك بدون مكتبات ، تحدثنا عنها في المقالات السابقة أكثر من مرة. يمكنك الرجوع إليهم إذا كنت مهتمًا بهذا الموضوع. وبالتالي ، من خلال الجمع بين كل هذه المعرفة معًا ، سنكون قادرين على بناء أشياء ثلاثية الأبعاد ملونة كاملة بأيدينا.
3D- . – - . , Three.js . , , , - , - . , .
حان الوقت الآن لتلخيص ما ورد أعلاه بحيث يكون هناك مساحة في رأسك لحالة الاستخدام التالية للمصفوفات.
وبالتالي:
- يمكنك بناء عالم ثلاثي الأبعاد وحساب إحداثيات الكائنات على الشاشة بيديك باستخدام قطار من المصفوفات.
- في العالم ثلاثي الأبعاد ، نعمل بطريقة تجريدية مثل "الكاميرا". لها موقع واتجاه وزاوية عرض. يتم تعيين كل هذا باستخدام نفس المصفوفات. وهناك نوعان من طرق عرض الكاميرا الأساسية - المنظور وغير المنظور.
- في سياق WebGL ، غالبًا ما يؤدي العرض اليدوي للصورة على الشاشة أو الحسابات الفيزيائية إلى إزالة التبعيات الثقيلة وتسريع تحميل الصفحة. ولكن من المهم تحقيق توازن بين البرامج النصية والأدوات الجاهزة والخيارات البديلة لحل المشكلات ، مع الانتباه ليس فقط لراحتك ، ولكن أيضًا لقضايا سرعة التنزيل والأداء النهائي ، بما في ذلك على الهواتف.
ثالثا. مرشحات للصور
أخيرًا ، سننظر في منطقة تطبيق المصفوفات كمرشحات للصور. إذا اعتبرنا لونًا بتنسيق RGBA متجهًا ، فيمكننا أن نفترض أنه يمكننا هنا تطبيق تحويل مشابه لما استخدمناه مع الإحداثيات:
وتطبيق هذا على الصورة وفقًا للمبدأ الواضح:
{
= [ ] *
}
إذا كانت مصفوفة الهوية تعمل كمصفوفة ، فلن يتغير شيء ، ونحن نعلم ذلك بالفعل. ماذا يحدث إذا قمت بتطبيق مرشحات مشابهة لتحولات الترجمة والقياس؟
OU. والنتيجة هي مرشحات السطوع والتباين. مثير للإعجاب.
عند تجربة مثل هذه المرشحات ، يجب أن تتذكر دائمًا ضبط القيم بحيث لا يتم تعريض الصورة بشكل مفرط. إذا كنت تضرب شيئًا ما في عدد كبير ، فغالبًا ما تحتاج إلى طرح أو قسمة شيء ما في مكان ما. كما هو موضح في المثال السابق.
ولكن كيف تصنع صورة بالأبيض والأسود من لون واحد؟ أول ما يتبادر إلى الذهن هو إضافة قيم قناة RGB ، والقسمة على 3 ، واستخدام القيمة الناتجة لجميع القنوات الثلاث. في تنسيق المصفوفة ، سيبدو مثل هذا:
وعلى الرغم من أننا حصلنا على صورة بالأبيض والأسود ، لا يزال من الممكن تحسينها. في الواقع ، تدرك أعيننا خفة الألوان المختلفة بطرق مختلفة. ومن أجل نقل هذا بطريقة ما أثناء إزالة التشبع ، نقوم بعمل معاملات مختلفة لكل قناة RGB في هذه المصفوفة.
سيقدم المثال أدناه القيم المقبولة عمومًا لهذه المعاملات ، ولكن لا أحد يكلف نفسه عناء اللعب بها. في المجموع ، يجب أن تعطي هذه المعاملات 1 ، ولكن اعتمادًا على نسبها ، سنحصل على صور بالأبيض والأسود مختلفة قليلاً. يمكن لهذا ، إلى حد ما ، محاكاة عرض ألوان مختلف عند العمل باستخدام كاميرات الأفلام.
وإذا قمنا أيضًا بضرب القطر الرئيسي قليلاً ، فسنحصل على مرشح تشبع عالمي:
إنه يعمل في كلا الاتجاهين - سواء في إزالة التشبع (يمكنك الوصول إلى صورة بالأبيض والأسود تمامًا) وفي التشبع. كل هذا يتوقف على المعامل المقابل.
بشكل عام ، يمكنك اللعب باستخدام المرشحات لفترة طويلة ، والحصول على مجموعة متنوعة من النتائج:
* يمكن عرض المصفوفات المستخدمة في هذا المثال على GitHubإذا احتجت إليها فجأة. لإدراجها في المقالة ، سيكون حجمها مفرطًا.
لكن دعونا لا نزال نولي القليل من الاهتمام لما ينطبق هذا بالفعل. من الواضح أن فكرة استبدال اللون لكل بكسل تقترح تظليلًا لمعالجة صورة ، أو لمعالجة بعض المشاهد ثلاثية الأبعاد ، ولكن ربما لا يزال في مكان ما في الواجهة الأمامية؟
المرشحات في CSS
في CSS ، لدينا خاصية التصفية. وهناك ، على وجه الخصوص ، هناك خيارات من هذا القبيل للمرشحات المتعلقة بالألوان:
- سطوع (صنعناه)
- التباين (تم)
- معكوس (مثل التباين ، فقط معاملات قطرية رئيسية بعلامة مختلفة)
- مشبع (فعل)
- التدرج الرمادي (كما لوحظ بالفعل ، هذه حالة خاصة من التشبع)
- البني الداكن (مفهوم غامض للغاية ، يتم الحصول على إصدارات مختلفة من البني الداكن من خلال اللعب بالمعاملات ، حيث نقوم بطريقة ما بتقليل وجود اللون الأزرق)
وتقبل هذه المرشحات المعاملات كمدخلات ، والتي يتم استبدالها بعد ذلك بشكل أو بآخر في المصفوفات التي صنعناها سابقًا. الآن نحن نعرف كيف يعمل هذا السحر من الداخل. والآن أصبح من الواضح كيف يتم دمج هذه المرشحات في أحشاء مترجم CSS ، لأن كل شيء هنا مبني وفقًا لنفس مبدأ الإحداثيات: ضرب المصفوفات - إضافة تأثيرات. صحيح ، لا توجد مصفوفة دالة مخصصة في هذه الخاصية في CSS. لكنها في SVG!
تصفية المصفوفات في SVG
داخل SVG ، لدينا feColorMatrix ، والذي يستخدم لإنشاء مرشحات للصور. وهنا لدينا بالفعل الحرية الكاملة - يمكننا عمل مصفوفة حسب ذوقنا. بناء الجملة شيء من هذا القبيل:
<filter id=’my-color-filter’>
<feColorMatrix in=’SourceGraphics’
type=’matrix’,
values=’1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1‘
/>
</filter>
يمكنك أيضًا تطبيق مرشحات SVG على عناصر DOM العادية داخل CSS ، وهناك وظيفة URL خاصة لهذا ... لكنني لم أخبرك بذلك!
في الواقع ، لا تزال مرشحات SVG داخل CSS غير مدعومة من قبل جميع المتصفحات (لا تشير بأصابع الاتهام إلى IE) ، ولكن هناك شائعات بأن Edge ينتقل أخيرًا إلى محرك الكروم ، وستفقد الإصدارات الأقدم الدعم في المستقبل المنظور ، لذا حان الوقت لهذه التقنية سيد ، يمكنك أن تفعل الكثير من الأشياء الشيقة به.
ماذا يحدث بعد ذلك؟
بالإضافة إلى تأثيرات الصور ، المبنية على مبدأ التحولات ، هناك العديد من الأشياء المبنية على إزاحة البكسل ، ومزج ألوانها وعمليات التلاعب الأخرى ، حيث يمكن أن تكون المصفوفة تنسيقًا جيدًا لتخزين البيانات التي يجب أن يتم من خلالها هذا التلاعب.
مصفوفة النواة
على وجه الخصوص ، في الواجهة الأمامية ، نلتقي بشيء مثل مصفوفة النواة ، والتأثيرات المرتبطة بها. النقطة بسيطة - هناك مصفوفة مربعة ، عادة 3 × 3 أو 5 × 5 ، على الرغم من أنه قد يكون هناك المزيد ، ويتم تخزين المعاملات فيها. في وسط المصفوفة - للبكسل "الحالي" ، حول المركز - للبكسل المجاورة. إذا كانت المصفوفة 5 × 5 ، فستظهر طبقة أخرى حول المركز - للبكسلات الموجودة واحدة من الطبقة الحالية. إذا كان 7x7 - ثم طبقة أخرى ، إلخ. بعبارة أخرى ، نعتبر المصفوفة مجالًا ثنائي الأبعاد ، حيث يمكنك ترتيب المعاملات وفقًا لتقديرك ، دون الرجوع إلى أي معادلات. وسيتم تفسيرها على النحو التالي:
{
=
,
}
القماش الفارغ ليس مناسبًا جدًا لمثل هذه المهام ، لكن الظلال متساوية جدًا. لكن من السهل تخمين أنه كلما كبرت المصفوفة ، زاد عدد وحدات البكسل المجاورة التي سنستخدمها. إذا كانت المصفوفة 3 × 3 ، فسنضيف 9 ألوان ، إذا كانت 5 × 5 - 25 ، وإذا كانت 7 × 7 - 49 ، وما إلى ذلك. المزيد من العمليات - مزيد من الحمل على المعالج أو بطاقة الفيديو. سيؤثر هذا حتمًا على أداء الصفحة ككل.
كلما أمكن ، استخدم مصفوفات صغيرة لتأثيرات مماثلة إذا كنت بحاجة إلى تراكبها في مكان ما في الوقت الفعلي.
داخل SVG ، لدينا علامة feConvolveMatrix خاصة ، تم إنشاؤها فقط لإنشاء مثل هذه التأثيرات:
<filter id=’my-image-filter’>
<feConvolveMatrix
kernelMatrix=’0 0 0
0 1 0
0 0 0’
/>
</filter>
لقد قمنا هنا بعمل مرشح بسيط للصورة لا يفعل شيئًا - اللون الجديد لكل بكسل سيكون مساويًا للون الحالي مضروبًا في 1 ، وسيتم ضرب قيم ألوان وحدات البكسل المجاورة بـ 0.
لاحظ أن المتصفحات المختلفة تعرض SVGs بشكل مختلف ، ويمكن أيضًا أن يطفو تجسيد اللون على نطاق واسع جدًا. أحيانًا يكون الاختلاف كارثيًا. لذلك اختبر دائمًا مرشحات SVG أو استخدم لوحة الرسم ، والتي يمكن توقعها بشكل أكبر في سياقنا.
إذا بدأنا في ترتيب الأرقام في طبقات ، من الأكبر إلى الأصغر ، نحصل على تمويه:
كلما كبرت المصفوفة ، كلما لامسنا عددًا أكبر من وحدات البكسل المجاورة ، زاد غسل الصورة. الشيء الرئيسي هنا هو عدم نسيان تطبيع القيم ، وإلا ستضيء الصورة.
الآن ، بمعرفة كيفية عمل التمويه ، يمكننا أن نفهم لماذا يؤدي استخدامه النشط على صفحة داخل CSS أو SVG إلى الفرامل - لكل بكسل يقوم المتصفح بإجراء مجموعة من العمليات الحسابية.
إذا بدأت في تجربة تغيير علامات المعاملات وترتيبها في أنماط مختلفة ، فستحصل على تأثيرات أكثر حدة ، واكتشاف الحواف ، وبعض التأثيرات الأخرى. حاول أن تلعب معهم بنفسك. قد يكون هذا مفيدًا.
وبالتالي ، يمكنك عمل تأثيرات مختلفة للصور ، أو حتى الفيديو ، في الوقت الفعلي ، وجعلها تعتمد على بعض إجراءات المستخدم. كل هذا يتوقف على خيالك.
المجاميع الجزئية
لنلخص ما قيل في هذا الجزء:
- يمكن استخدام المصفوفات ليس فقط للتحويلات المتعلقة بالإحداثيات ، ولكن أيضًا لإنشاء مرشحات الألوان. كل شيء يتم وفقًا لنفس المبدأ.
- يمكن استخدام المصفوفات كمخزن مناسب ثنائي الأبعاد لبعض البيانات ، بما في ذلك المعاملات المختلفة للتأثيرات المرئية.
خاتمة
إذا استخلصنا قليلاً من الخوارزميات المعقدة ، فستصبح المصفوفات أداة ميسورة التكلفة لحل المشكلات العملية. بمساعدتهم ، يمكنك حساب التحويلات الهندسية بيديك ، بما في ذلك ضمن إطار عمل CSS و SVG ، وإنشاء مشاهد ثلاثية الأبعاد ، وأيضًا عمل جميع أنواع المرشحات للصور أو ما بعد المعالجة للصور في إطار WebGL. عادة ما تتجاوز كل هذه الموضوعات الواجهة الكلاسيكية وترتبط بشكل أكبر برسومات الكمبيوتر بشكل عام ، ولكن حتى إذا لم تحل هذه المشكلات بشكل مباشر ، فإن معرفة مبادئ حلها سيسمح لك بفهم كيفية عمل بعض أدواتك بشكل أفضل. لن تكون أبدًا غير ضرورية.
آمل أن يكون هذا المقال قد ساعدك في فهم موضوع الاستخدام العملي للمصفوفات في الواجهة الأمامية ، أو على الأقل أعطاك أساسًا يمكنك البناء عليه في مزيد من التطوير. إذا كنت تعتقد أن بعض الموضوعات الأخرى المتعلقة بالرياضيات أو الفيزياء تستحق نفس المراجعة في سياق التخطيط ، فاكتب أفكارك في التعليقات ، وربما تناقشها إحدى المقالات التالية.