تعلم كيفية الاستفادة من ذاكرة التقنيع Stencil الجزء الأول

الناقل : elmasry | الكاتب الأصلى : time1 | المصدر : www.arabteam2000-forum.com

بسم الله الرحمن الرحيم

سأتكلم من خلال درسي هذا عن جزء مهم في برمجة الجرافيكس , وهو ذاكرة التقنيع او مايعرف بـ Stencil

تقدم لنا ذاكرة التقنيع فوائد عديدة تمكننا من اضفاء جماليات على المشاهد يصعب انشاءها الا بالعمليات الرياضية المعقدة ,
سنأخذ على سبيل المثال انشاء المرايا وادخالها داخل المشهد ..


انشاء المرايا :

تكمن الفكرة بأنه يوجد جزء من المشهد يمثل المرآة تنعكس من خلاله اي جسم يمر من امامه ولهذا يجب ان نأخذ بعين الاعتبار بأن هذا الجسم المنعكس يجب ان لاينعكس الا اذا وقع امام هذه المرآة اما اذا وقع امام اي جسم اخر فأنه لاينعكس .

نستطيع ان نكون المرآة من خلال عمليات رياضيه معقدة تسمى برياضيات الانعكاس , ولكن سنستخدم هنا طريقة ابسط توفرها لنا ذاكرة التقنيع Stencil ......

انشاء المرايا بواسطة ذاكرة التقنيع Stencil

سنقسم الموضوع إلى ثلاثة اقسام :
اولا: انشاء المشهد مع المرايا ..

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

ارفق صورة : monthly_04_2008/post-82612-1208114050.jpg


بعد ذلك انشأنا مجسم كسوناه بخامة ثلجية لتمثل لنا جسم المرآة .

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

ثانيا : انشاء المرآة
اولا نقوم بملأ ذاكرة 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);


ارفق صورة : monthly_04_2008/post-82612-1208114321.jpg

الان نقوم بإعادة تفعيل ذاكرة العمق والذاكرة الامامية مرةاخرى
	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);


وهنا ستكون النتيجة :

ارفق صورة : monthly_04_2008/post-82612-1208114119.jpg




في المرة القادمة سنتعلم كيفية إنشاء ظلال للاجسام من خلال ذاكرة التقنيع Stencil ..


وشكرا