8.5 الرسم ثنائي الأبعاد | كتاب لينكس الشامل | >> |
يتم التعبير عن الرسوم على الشاشة التي هي نقاط مضيئة تسمى بيكسلات pixles مصطفة بجانب بعضها البعض بمصفوفة في الذاكرة الخاصة ببطاقة العرض (كرت الشاشة) تسمى framebuffer هذه المصفوفة هي عبارة عن أرقام كما في الشكل
يكون القياس في مكتبة openGL من الزاوية اليسرى السفلى لتكون نقطة الأصل (صفر،صفر) وليس العليا وكأن طرفا الشاشة هناك محاور وكأنه مستوى ديكارتي وليس مصفوفة إذ يمكنك رسم النقطة (نصف ، واحد ونصف) مثلا ولكن هذه ليست حقيقة مايجري داخل الحاسوب إذ لا يستطيع رسم سوى النقاط الصحيحة
تقوم بطاقة العرض بإعادة رسم ما في هذه المصفوفة
تلقائياً خلال فترات معينة تسمى refresh rate وغالباً ما تكون 60-70 fps أي
frame per second شاشة لكل الثانية وكما تعلم فإن المصفوفة لها حجم هو عدد الصفوف × عدد الأعمدة
ونفس الشيء هنا لدينا دقة العرض resolution وهي عدد النقاط في السطر الواحد ×
عدد الأسطر وكلما كان العدد أكبر كانت الصورة أوضح
وبحاجة إلى المزيد من الذاكرة والمزيد من العمليات الحسابية في التعامل معها.
إذا كان لدينا دقة بمقدار
640×480
نستطيع رسم النقاط من 0 إلى 639 وعلى الأسطر من 0 إلى 479
ونقول بأن لدينا مجال عرض viewport من صفر ، صفر وبعرض 640
وبارتفاع 480
المصفوفة التي تحدثنا عنها تحتوي أرقاماً هذه الأرقام تمثل ألواناً
وهناك أكثر من طريقة لتمثيل هذه الألوان منها طريقة الألوان
الحقيقية أو RGB وتعني أننا نعطي مقدار الأحمر ومقدار الأخضر
ومقدار الأزرق ويكون اللون ناتجح مزج الأضواء
بتلك الألوان وقد نحدد العمق اللوني color depth مثلا ب 24-بت
(أي 3 بايت)
فيكون لكل لون أساسي رمز من 8-بت أي من 0-255 ونعبر في البرمجة
عنها بالنظام الست-عشري فتكون من صفر إلى ff
مثلا الأبيض في html بالشكل التالي
#ffffff
أو في لغة C قد نستخدم int حيث أنه 32-بت نستخدم أول 24-بت
منها وذلك عن طريق الإزاحة مثلا الأبيض
0xff+
(0xff<<8)+
(0xff<<16)=
0xffffff
c1=((r1 & 255)<<8)+((g1 & 255)<<16)+(b1 & 255)
وهناك أطوار حقيقية غير ال 24-بت مثل 32-بت و 15-بت وهذا الأخير يكون لكل لون أساسي من أحمر وأخصر وأزرق 5-بت وهو غير قياسي ولا تدعمه معظم كررت الشاشة أما 16-بت فهو أكثر توفراً وهو المفضل لدي بشكل عام حيث نخصص للأحمر والأزرق 5-بت أما الأخضر 6-بت وسبب سرعته أنه يوفر 8-بت لكل نقطة مقارنة بطور 24-بت.
في مكتبة openGL نعبر عن اللون بثلاث أعداد نسبية float
قيمة كل واحد تقع بين الصفر والواحد
فالأبيض هو float myWhite[]={1.0,1.0,1.0};
طريقة مزج الألوان هنا هي RGB وهي طريقة جمعية أو ما تسمى مزج الضوء فإذا مزجنا الألوان الأساسية الأحمر والأخضر والأزرق بنسب متساوية نحصل على تدرجات الرمادي وبأعلى نسبة يكون الأبيض وهناك طريقة أخرى هي طريقة CYM أي أزرق سماوي و أصفر وزهري وهي مستخدمة في مزج الأصباغ والطباعة وهي طريقة طرحية subtractive أي تبدأ بورقة بيضاء تحتوي كل الطيف ثم تضع صبغة كل واحدة تمتص جزء من الضوء (تطرح منه) وعند مزج كل الأصباغ تحصل على الأسود.
وهذا يربك واضعي أسئلة الحزازير هل مزج الألون يعطي أسود أم أبيض؟ هذا طبعا يتوقف هل تتحدث عن ضوء أم عن أصباغ ، ولأن هذا النظام يستخدم في الطباعة وليس على الشاشة لن أتحدث عنه كثيراً.
r=255-(k+c); if (r<0) r=0; g=255-(k+m); if (g<0) g=0; b=255-(k+y); if (b<0) b=0;
k=255-max(r,g,b); c=255-k-r ; m=255-k-g ; y=255-k-b ;
والطريقة الأخرى هي طريقة جدول الألوان pallete وهي طريقة مستخدمة في عمق لوني 8-بت (أي 1 بايت لكل نقطة) فما دون حيث نخزن جدول ألوان في مكان منفصل يحتوي على كيفة تفسير الأرقام من 0-255 بألوان حقيقية وهي طريقة أكثر توفيرا في الذاكرة وسرعة حيث كل نقطة نمثلها بواحد بايت فقط وهي أقدم من الألوان الحقيقية
تدعم بطاقات العرض vga كثافة نقطية 640x480
مع 4-بت
و 320x200
مع 8-بت
لهذا فهذه الأخيرة كانت تستخدم بشكل واسع في الألعاب
لنفرض أنك تريد أن ترسم مستطيل في وسط الشاشة
وذلك بالكتابة مباشرة في الذاكرة وكما قلنا
تقوم بطاقة العرض برسم(تحديث) محتوياته على الشاشة بشكل متكرر في فترار معينة
ولكن لنفرض أنه كان قد أنهى تحديث النصف العلوي من الشاشة
وبدأ بتحديث النصف السفلي
في الوقت الذي رسمت فيه المستطيل في الذاكرة
عندها سيبدأ برسم نصف مستطيلك السفلي ويعود لرسم النصف الثاني في
الدورة التالية
فاذا كانت سرعة التحديث بطيئة ستلاحظ العين ظهور النصف السفلي
أولا ثم النصف العلوي ونقول بأن الصورة ترمش
فتخيل لو كان الوضع أعقد من رسم مستطيل أنك ترسم
ساعة مثلا بها عقرب يتحرك بأجزاء الثانية
فقط يظهر جزء من العقرب في مكان وجزء في مكان آخر
لهذا يوجد في بطاقات العرض اشارة تزامن SYNC
تحدث هذه الإشارة عند الإنتهاء من تحديث الشاشة كاملة
وبهذا نكون قد ضمنا أن المستطيل سيرسم بالترتيب الصحيح.
لتدرك كيف يمكن أن تتسبب هذه المشكلة في ظهور نتائج سيئة انظر هذه الصور
ولكن ماذا لو أردنا أن نرسم صورة يأخذ توليدها فترة طويلة وتولد على مراحل مثلا رسم مربع (بيت) ثم رسم مستطيل(باب البيت) ولا نريد أن تلحظ العين رسم هذا ثم هذا بل ترى النتيجة النهائية فقط أو مثلا في الصور لمتحركة نرسم ثم نمسح الشاشة ثم نرسم من جديد ولكن لا نريد للعين أن تلاحظ اختفاء الصورة وضهورها بل ترى حركة لهذا نستخدم double buffer والفكرة بسيطة نرسم على ذاكرة مستقلة غير التي تظهر على الشاشة على دفعات وعلى مهل وتأني (وفي هذا الوقت تكون الشاشة تعرض الصورة القديمة) ثم ننقل النتيجة النهائية إلى إلى ذاكرة الشاشة فتظهر وكأنها تحركت بسلاسة ورسمت في بسرعة آنية دون أن تمحى وتظهر أي دون أن ترمش
تستخدم هذه الذاكرة في تخزن مصفوفة ال frame buffer
وتخزين جدول ألوان واحد (يكون محجوز ولا يستخدم إذا كنا في طور الألوان الحقيقية
وهو لا يحسب جزء من ذاكرة الشاشة
)
فإذا قلنا أنك تملك بطاقة عرض 1MB (أي 1048576) فإن هذه البطاقة قد تتمكن من دعم
كثافة 800x600
ب 16-بت(2 بايت) لأن
800*600*2= 960000
أقل من 1 MB
بعمق 24-بت فإن ذاكرة العرض لن تكفي.
ولكن بطاقات الشاشة الحديثة تحتوي ذاكرة أكثر من تلك التي نحجزها للعرض (الظاهرة) فهناك جزء متبقي (غير ظاهر) تستغل فيه ذاكرة الشاشة دون أن ترسم محتوياته على الشاشة، وبحكم كون بطاقات عرض AGP ذو ذاكرة أسرع من ذاكرت الرام RAM العادية قد تستخدم بعض البرامج ذاكرته لتخزين بعض الصور والبيانات البينية
إذا لاحظت أننا في حال استعمال double buffer نقوم بعملية نقل من الذاكرة إلى ذاكرة بطاقة العرض عبر المعالج مما يأخذ وقت في عملية غير ضرورية فبطاقات العرص تحتوي على ما يسمى صفحات(وهي جزء من ذاكرة العرض غير الظاهرة) يمكننا أن نرسم على إحداها في الوقت الذي تظهر أخرى وعند الإنتهاء نقول له أن يظهر الصفحة الأخرى filp (نقلب الصفحة) ونبدأ بالرسم على الأولى وعند الإنتهاء نقلب مجدداً وهكذا وهنا لايوجد عملية نقل من إلى إنما هي عملية داخلية في بطاقة العرض لا تأخد من وقت المعالج شيء
عرفنا أن رسم النقطة يكون بوضع لونها في الذاكرة الخاصة بذلك
ولرسم خط نقوم برسم جميع النقاط ذات الإحداثيات الصيحيحة
(غير الكسرية)
الموجودة على الخط وأيضا النقاط التي تصل بينها .
وتستخدم طرق رياضية لتحديد
هذه النقاط بشكل متسلسل أي ايجاد النقطة إذا علمت النقطة التي قبلها
ودون اجراء عملية ضرب فتكون العملية سريعة
فنحن لا نستخدم القوانين العادية
y=ax+b
ولا
P=(P2-P1)t+P1
حيث t عدد بين الصفر والواحد
و P و P1 و P2 عبارة عن متجه (زوج مرتب)
وأعتقد أنه لا مجال لذكر الطريقة هنا
هذه العمليات لن تقوم أنت بكتابة برنامجها وإنما تقوم بها مكتبة الرسومات مثل OpenGL و Allegro وأنا أعرضها للثقافة
أما رسم المثلث (وما بداخله) فيكون برسم خط أفقي يصل بين كل نقطتين موجودتين على حدوده وعلى نفس الخط الإفقي أما رسم المضلع(وما بداخله) فيكون بعمل قائمة بالنقاط الموجودة على حدوده (أثناء رسمها) ووضعها في جدول مبوب حسب الإحداثي السيني الآن لكل احداثي سيني نرتب النقاط التي عليه ثم نصل بين كل زوج منهما. ولرسم دائرة نرسم أحد أرباعها ونكررها أربع مرات معكوسة وهي أقل سرعة من رسم المضلعات لذا حاول التقليل من رسم الدوائر قدر الإمكان في البرامج التفاعلية
الطريقة العادة في الرسم تجعل الخطوط تبدو مكسورة وغير مستقيمة عند التدقيق فيها أو تكبيرها بسبب طبيعة كون المصفوفة تحتوي عدد محدود وصحيح من النقاط لهذا تستخدم في بعض البرامج مثل gimp والمكتبات مثل OpenGL طريقة تسمى anti-alised لجعل الخطوط والرسومات تبدو ملساء smooth وذلك برسم تدرج حولها مثلا إذا رسمت خط أسود على خلفية بيضاء ستضع بعض النقاط الرمادية لتعطيك وهما بأنه الخط مستقيم
تستخدم منحنيات تسمى منحنيات Bezier في رسم خطوط الكتابة fonts أو في رسم الأشكال الدقيقة بطريقة تحاكي يد الإنسان وبشكل أساسي تصل هذه المنحنيات بين نقطتين ونأخذ نقطتين أخر كدليل hints ينحني الخط بإتجاههما وأيضا تستخدم هذه المنحنيات في التحريك بين وضعين في حركة انسيابية مع الزمن ويصلح هذا المنحنى لثلاث أبعاد أيضا وهذه معادلتها:
حيث t بين الصفر والواحد و P(t)
هي موقع الجسم عند t
و P1
و P4
هي نقطة البداية والنهاية
أما P2
و P3
فهي نقطتا الدليل
وإذا لم تكن تألف هذه الصيغ هذه التفاصيل
الطريقة الأولى تسمى Bitmap أو Raster Image حيث نخزن طولها وعرضها والعمق اللوني وجدول الألوان إن لزم الأمر ثم نخزن الصورة كما هي الذاكرة كسلسلة من الأرقام تمثل الأرقام الموضوعة في المصفوفة أو نقوم بضغطها فمثلا تتقنية jpeg تقوم بطريقة ضغط فاقدة للجودة أما gif و png فهي تقنيات ضغط غير فاقدة (أو أقل فقداً) إلا أن gif لا تصلح سوى لل 8-بت وهي تقنية بها براءة اختراع حصرية على طريقة التخزين والضغط أما png فهي مفتوحة وتصلح لأي عمق لوني أما أسهل طريقة في البرمجة هي xpm انظر إلى محتويات أي ملف xmp هذه مثلا صورة كتاب لاحظ أنك يمكن أن تضعها داخل كود C بلا مشاكل
/* XPM */ static char * openbook [] = { "16 16 4 1", " c None s None", ". c black", "X c #808080", "o c white", " ", " .. ", " .Xo. ... ", " .Xoo. ..oo. ", " .Xooo.Xooo... ", " .Xooo.oooo.X. ", " .Xooo.Xooo.X. ", " .Xooo.oooo.X. ", " .Xooo.Xooo.X. ", " .Xooo.oooo.X. ", " .Xoo.Xoo..X. ", " .Xo.o..ooX. ", " .X..XXXXX. ", " ..X....... ", " .. ", " " };
لا تعيد اختراع العجلة استخدم المكتبات التي تقوم بفتح الصور التي تريد بدلاً من كتابة ذلك بنفسك
وهناك الطريقة المتجهية vector أي أنك تخزن الإحداثيات والأبعاد وماشابه وعند تحميل الملف تقوم برسمها من هذه المعلومات، انظر إلى أي ملف SVG.
إذا رسمت مربع ثم نصّفت كل ضلع ووصلتهم معاً ستحصل على مربع جديد صغير مائل، فإذا كررت العملية ستحصل على زخرفة. هل سمعت عن مثلثات باسكال؛ ارسم مثلث متساوي الأضلاع ثم نصّف كل ضلع وصل نقاط المنتصف معاً لتحصل على مثلث صغير مقلوب في الوسط وثلاث مثلثات ضغيرة تحيط به، كرر نفس العملية مع المثلثات المحيطة للأوسط دونه ثم كررها وكررها. أسمينا معادلات على هذه الصيغة بالاستدعاء الذاتي recursion. بأن نستدعي الوظيفة بنواتج تنفيذ سابق. إذا تحدثنا عنها في سياق الرسم تسمى Fractals.
بنفس الطريقة نعمل المسار المعقد الموضح في أسفل يمين اللوحة السابقة، إذ أن البذرة فيه هي حرف U مقلوب والمولد بأن تقلب الشكل 90ْ مع عقارب الساعة ثم تضع فوقه الشكل الأصلي وعلى يمينه الشكل الأصلي وتحته الشكل مقلوب 90ْ عكس عقارب الساعة. يتميّز هذا المسار المعقد بأنه يجوب الرقعة كاملة ماراً بالمربعات القريبة من بعضها أولاً.
هل نظرت إلى صحيفة (أسود وأبيض) بواسطة مكبر؟ ستجد أن
كافة تدرجات الرمادي حصلنا عليها من وضع نقاط من الأسود والأبيض بترتيب
معين يبدو عشوائي للوهلة الأولى يسمى هذا الشفق chaos. مثل آخر
عند عمل تدرج بين عدة ألوان قد تلاحظ عند محدودية
العمق اللوني (16-بت أو 8-بت) وجود حدود واضحة بين
لونين متجاورين وأن التدريج ليس أملساً (ليس في gimp بل في برامج عادية).
لنفرض أنك تمتلك لونين فقط أحمر وأصفر (دون التدرجات) -اخترتهما بسبب
التباين الشديد بينهما - والمطلوب أن تحصل على لون برتقالي بنسبة
الربع والنصف والثلاثة أرباع بين الأصفر والأحمر.
أحد الطرق أن تقرّب ما هو مطلوب في موقع
معين (لنرمز للأحمر بالرقم0.0 والأصفر 1.0) ، مثلاً بواسطة
a=floor(v+e+0.5)
حيث a هي ناتج التقريب و v هي القيمة المطلوب تقريبها و e هي الخطأ التراكمي وهو صفر كبداية
ونضيف الفرق بين التقريب a والقيمة المطلوبة v إلى الخطأ
e+=v-a
وهكذا فإن الخطأ يتراكم حتى يصبح واحداً صحيحاً.
نسمي طريقة جمع الخطأ في التقريب إلى القيمة بحيث يتراكم شيئاً فشيئاً حتى يؤثر على القيمة بمعادلات الشفق chaos. في المثال السابق وعند السير بتسلسل تجمعت الألوان في خطوط عمودية وهذا مزعج وغير متناسق والأفضل أن تتوزع كما في الصورة على يمين المخططة. نحصل على التوزيع بسلوك مسار معقد يمر بالنقاط القريبة أولاً. نعم fractals ذات 4×4 السابق الذكر وستعمل على توزيع النقاط بحيث لا تتجمع بشكل مزعج.
بشكل عام لنفرض أن هناك صورة القيمة الفعلية هي v(x,y)
فإن الشفق يكون
ونتحرك فيه من النقطة (0،0) ثم نسير عبر المسار المعقد ونحدث قيمة الخطأ التراكمي e.
كما يمكن عمل ثلاث قيم وثلاث أخطاء واحدة لكل لون في طور الألوان الحقيقية RGB.
a=floor( v(x,y) +e+0.5)
<< السابق | كتاب لينكس الشامل | التالي >> |