بسم الله الرحمن الرحيم
سأتكلم من خلال درسي هذا عن جزء مهم في برمجة الجرافيكس , وهو ذاكرة التقنيع او مايعرف بـ Stencil
تقدم لنا ذاكرة التقنيع فوائد عديدة تمكننا من اضفاء جماليات على المشاهد يصعب انشاءها الا بالعمليات الرياضية المعقدة ,
سنأخذ على سبيل المثال انشاء المرايا وادخالها داخل المشهد ..
انشاء المرايا :
تكمن الفكرة بأنه يوجد جزء من المشهد يمثل المرآة تنعكس من خلاله اي جسم يمر من امامه ولهذا يجب ان نأخذ بعين الاعتبار بأن هذا الجسم المنعكس يجب ان لاينعكس الا اذا وقع امام هذه المرآة اما اذا وقع امام اي جسم اخر فأنه لاينعكس .
نستطيع ان نكون المرآة من خلال عمليات رياضيه معقدة تسمى برياضيات الانعكاس , ولكن سنستخدم هنا طريقة ابسط توفرها لنا ذاكرة التقنيع Stencil ......
انشاء المرايا بواسطة ذاكرة التقنيع Stencil
سنقسم الموضوع إلى ثلاثة اقسام :
اولا: انشاء المشهد مع المرايا ..
سنقوم بأنشاء اي مشهد , وعلى سبيل المثال قمت هنا بأنشاء غرفة تحتوي جدرانها على ارقام واحرف , وقمت بإنشاء صندوق خشبي ووضعته بمنتصف الغرفة , وفوقه ابريق شاي ..
بعد ذلك انشأنا مجسم كسوناه بخامة ثلجية لتمثل لنا جسم المرآة .
سنقوم بجعل هذه المرآة تعكس ابريق الشاي فقط اذا كان امام هذه المرآة اما اذا كان امام الجدار فإنه لاينعكس .
ثانيا : انشاء المرآة
اولا نقوم بملأ ذاكرة Stencil بالقيمة صفر من خلال المنهج Clear
Device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,0xffffffff,1.0f,0);
الوسيط السادس يحدد لنا القيمة التي سنملأ بها ذاكرة التقنيع .
ثانيا : ننشأ الدالة Mirror التي ستمثل لنا الانعكاس .
داخل هذه الدالة نقوم بتفعيل ذاكرة التقنيع Stencil
Device->SetRenderState(D3DRS_STENCILENABLE,true);
بعد ذلك نحدد القيمة المرجعية ref والتي هنا ستكون 1
ان مانقوم به هو ملأ الذاكرة باكملها بالقيمة 0 ثم تحديد المرآة وملأها بالقيمة 1 وهذا من شأنه يمنع تصيير مناطق معينة بالذاكرة .
Device->SetRenderState(D3DRS_STENCILREF,0x1);
بعدها نحدد قيمة القناع mask لحجب بتات من ref و value وقناع الكتابة الذي يحجب بتات اي قيمة نقوم بكتابتها داخل ذاكرة Stencil
Device->SetRenderState(D3DRS_STENCILMASK,0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
الان تبقى لنا مرحلة الاختبار او المقارنة وهي حالة من احدى الحالات التاليه :
D3DCMP_NEVER : وهي تحدد بأن الاختبار لن ينجح ابدا .
D3DCMP_LESS : ينجح عندما يكون الطرف الايسر من المقارنة اصغر من الطرف الايمن
D3DCMP_EQUAL: ينجح الاختبار عندما يتساوى طرفي المقارنة.
D3DCMP_LESSEQUAL : ينجح الاختبار في حالة طرف المقارنة الايسر اصغر من او يساوي طرف المقارنة الايمن .
D3DCMP_GREATER: ينجح الاختبار في حالة طرف المقارنة الايسر اكبر من طرف المقارنة الايمن .
D3DCMP_NOTEQUAL : ينجح الاختبار عندما يكون طرفا المقارنة غير متساويين .
D3DCMP_ALWAYS: ينجح الاختبار دائما .
هناك بقية من انواع الاختبارات لكن هذه اهمها .
سنختار هنا نجاح الاختبار دائما .
Device->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_ALWAYS);
بعدها نقوم بتحديث ذاكرة التقنيع من خلال :
1 - تحديد الطريقة التي يتم فيها تحديث اي حجرة من حجرات ذاكرة التقنيع في حال فشل التقنيع لأي بيكسل
2- تحديد الطريقة التي يتم فيها تحديث اي حجرة من حجرات ذاكرة التقنيع في حال فشل العمق Z-Buffer لأي بيكسل
3-تحديد الطريقة التي يتم فيها تحديث اي حجرة من حجرات ذاكرة التقنيع في حال نجاح اختبار التقنيع والعمق Z-Buffer لأي بيكسل :
ويمكن ان نعطي لكل حالة قيمة من مجموعة القيم التاليه :
D3DSTENCILOP_KEEP : تبقى حجرات الذاكرة كما هي وبنفس قيمها السابقة .
D3DSTENCILOP_REPLACE: سيتم استبدال حجرات الذاكرة بالقيمة المرجعيه ref
D3DSTENCILOP_ZERO : عندها ستصبح قيمة الحجرات صفرا.
هناك العديد من القيم المعرفة مسبقا والتي يمكن اعطائها للحالات السابقة ولكن هذه اهمها .
في مثالنا هذا سنبقي الحجرات كما هي عليه في حال فشل اختبار العمق والتقنيع ولكن سنغير القيمة إلى القيمة المرجعيه في حال نجاح الاختبار , ولو دققنا الملاحظة سنجد ان القيم ستتغير دائما وذلك لاننا قلنا بأن الاختبار سينجح دائما !!
Device->SetRenderState(D3DRS_STENCILFAIL,D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILZFAIL,D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_REPLACE);
الجزء التالي هو رسم المرآه فقط إلى ذاكرة التقنيع Stencil ومنع الكتابة على ذاكرة الاماميه Target او ذاكرة العمقZbuffer
Device->SetRenderState(D3DRS_ZWRITEENABLE,false);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ZERO);
Device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
نقوم بإعادة رسم المرآة ولكن هذه المرة إلى ذاكرة التقنيع ,
هنا سيحدث انه سيكون فقط الجسم الذي يمثل لنا المرآة يحمل القيمة المرجعية 1 اما بقية اجزاء الغرفة تحمل القيمة 0 والتي تم اعطائها عندما تم ملأ ذاكرة التقنيع بهذا من خلال المنهج Clear
قمنا بتعطيل الكتابة للذاكرة الامامية Target من خلال المزج Blending بتحديد المصدر صفر والهدف 1 , وناتج هذه المعادلة سيكون نفس قيمة الذاكرة الامامية Target
Device->DrawPrimitive(D3DPT_TRIANGLELIST,30,2);
الان نقوم بإعادة تفعيل ذاكرة العمق والذاكرة الامامية مرةاخرى
Device->SetRenderState(D3DRS_ZWRITEENABLE,true);
Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);
Device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);
الان حان موعد رسم الابريق المعكوس على المرآة , وهنا سيكون الاختبار هذه المرة فقط في حال التساوي ونلاحظ هنا بأنه لن يتساوى الا مع المرآة والتي سبق ان قمنا بملأها بالقيمة المرجعيه 1
اما في حال النجاح فأننا نجعل القيم كما هي D3DSTENCILOP_KEEP
Device->SetRenderState(D3DRS_STENCILFUNC,D3DCMP_EQUAL);
Device->SetRenderState(D3DRS_STENCILPASS,D3DSTENCILOP_KEEP);
نقوم بتجهيز الرسم
1- نحدد السطح الذي سيتم الانعكاس عليه , ونعكس مركبة z لاننا نرغب في هذا المثال بعكس المستوى xy
اما في حال رغبتنا بعكس المستوى yz فأننا نعكس المركبة x وهكذا ....إلخ
2-ننشأ مصفوفة الانعكاس D3DXMatrixReflect
D3DXPLANE plane(0,0,1,0);
D3DXMatrixReflect(&R,&plane);
D3DXMatrixTranslation(&T,Tr.x,Tr.y,Tr.z);
W=T*R;
Device->SetTransform(D3DTS_WORLD,&W);
رسم الابريق
نرسم الابريق في ذاكرة العمق ZBuffer مع مراعاة تعديل غربلة الاوجه إلى D3DCULL_CW لتصحيح الخطأ الذي ينتج عن الانعكاس
Device->Clear(0,0,D3DCLEAR_ZBUFFER,0,1,0);
Device->SetRenderState(D3DRS_CULLMODE,D3DCULL_CW);
وفي نهاية الدالة نقوم بإعادة الوضع كما كان ,نلغي تفعيل ذاكرة التقنيع Stencil ونلغي المزج ونعيد ضبط الغربلة إلى D3DCULL_CCW
Device->SetRenderState(D3DRS_STENCILENABLE,false);
Device->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,false);
وهنا ستكون النتيجة :
في المرة القادمة سنتعلم كيفية إنشاء ظلال للاجسام من خلال ذاكرة التقنيع Stencil ..
وشكرا