[دروس] إنشاء الإنعكاس و الضلال درس في الأوبن جي أل

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

بســـــــــــــــــــــــــــــــــــــــــــــم الله الرًّحمن الرًّحيـــــــــــــــــــــــــــــــــــــــــــــــم
سيكون الدرس إن شاء الله حول الضلال و الإنعكاسات بإستعمال ذاكرة الإستنسل
وناتج الدرس سيكون بهذا الشكل, هوعبارة عن صندوق و صفيحة
أشبه بخشبة ملمعة, مما تخلق إنعكاس و ضل
Posted Image
لنبدء بالإنعكاس فهو أقل سهوله عن قبيله الضل, الكائنات التي تظهر في المسطح ليست سوى نفس الكائنات المرسومة في حقيقة العالم الثلاثي الأبعاد, وقد تم إضافة قليل من الشفافية في المسطح بأعلى الشكل, تخيل أنه لو لم تضاف الشفافية لكان السطح العاكس أشبة بالمراة و حينها ليس من التعقل إضافة الضلال .
لو قلت لك أنني سأرسم الشكل ثم أعيد رسمه ثانية بالشكل المقلوب, ستقول لي أنها فكرة عادية ولكنها الحقيقة , وستقول لي أن البرنامج سيكون مثقلا , لذا و لهذا السبب أوجد الخبراء وسيلة لتسريع عميلة الإنعكاس و التضليل فكانت ذاكرة الإستنسل.
في الحقيقة , لم تخلق مكتبات OpenGL دوال لإنشاء الضل و الإنعكاس مثلا:glShaddow أو glReflection
بل هناك طريقة تمر بعدة مراحل يتم فيها إنشاء تأثيرات الضلال و الإنعكاس .
يرجى التقيد بالمراحل فهي حساسة لحالة حدوث الأخطاء:
1 : حفظ المصفوفة التي ستأخذ بيانات الشكل المعكوس في المكدس stack وهذا بإستخدام الدالة glPushMatrix
2 : بإستعمال دالة glScalef نتمكن من قلب الشكل رأسا على عقب مثلا: (glScalef(1.0, -1.0, 1.0 هذايسمح بقلب المجسمات على المحور ع التي ستمثل المجسمات المعكوسة.
3 :إعداد مصادر مواقع الضوء, فهي أيظا تعكس.
4 :رسم الكائن وهو بالذات الكائن الذي سينعكس.
5 :إخراج المصفوفة التي أخذت بيانات الكائن المعكوس من المكدس بإستخدام glPopMatrix
6 : إعداد المصدر الأصلي لمواقع الضوء.
7 :أخيرا رسم المجسم الذي سيمثل الجسم العاكس وفي المثال أعلاه يمثل بمستطيل (الخشبة الملمعة).

glPushMatrix();
     gluLookAt (CUBE_R*NB_CUBE_X/2, 22, 30 ,CUBE_R*NB_CUBE_X/2, 21.9, 29 0, 1, 0);
     float zz=float(2*CUBE_R * (3 + 2*sin(tt/3000)));
     glPushMatrix();
         glScalef(1.0, -1.0, 1.0);
         for (int x = 0 ; x < NB_CUBE_X ; x++)
              for (int y = 0 ; y < NB_CUBE_Y ; y++)
                   for (int z = 0 ; z < NB_CUBE_Z ; z++)
                   {
                         Cube[x][y][z]->Update(t);
                         Cube[x][y][z]->Draw(0,-20,zz+20);
                    }
     glPopMatrix();
     glDisable(GL_LIGHTING);
     glDisable(GL_TEXTURE_2D);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        drawFloor();
     glDisable(GL_BLEND);
     glEnable(GL_TEXTURE_2D);
     glEnable(GL_LIGHTING);
     for (x = 0 ; x < NB_CUBE_X ; x++)
        for (int y = 0 ; y < NB_CUBE_Y ; y++)
            for (int z = 0 ; z < NB_CUBE_Z ; z++)
                Cube[x][y][z]->Draw(0,-20,zz+20);
glPopMatrix();

مع دالة رسم المجسم العاكس
void drawFloor()
{
   glColor4f(0.5f, 0.5f, 0.5f, 0.75f);
   glBegin(GL_QUADS);
      glVertex3f(0.0, 0.0, -20.0);
      glVertex3f(12.0, 0.0, -20.0);
      glVertex3f(12.0, 0.0, -70.0);
      glVertex3f(0.0, 0.0, -70.0);
   glEnd();
}

هناك مشكل : الإنعكاس قد يتجاوز المساحة المحددة للإنعكاس أي قد يظهر الإنعكاس خارج الجسم المستطيل و يظهرعلى مجسمات أخرى, مثل ذاكرة العمق تسمح بالرسم أو عدم, ذلك حسب الشروط المعمول بها, فالإستنسل يسمح بتحديد حدود الرؤية.
و المبدأ هو:
مسح ذاكرة الإستنسل بدالة glClear ثم قمت بوضع "1" , و نرسم سطح عاكس (نرسم المسطح مع تعطيل جميع الخواص ما عدا ذاكرةالإستنسلStencil-Buffer) , و نرسم المجسم المعكوس لما يكون الإستنسل "1".و بالتالي لا يتغير, معظم كارت الشاشة ما عدا 3Dfx (على ماأعتقد) تدير ذاكرة الإستنسل في الأجهزة.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glPushMatrix();
    gluLookAt(CUBE_R*NB_CUBE_X/2, 37, 30,CUBE_R*NB_CUBE_X/2, 36.7, 29 ,0, 1, 0);
    float zz=float(2*CUBE_R * (3 + 2*sin(tt/3000)));
    glDisable(GL_DEPTH_TEST);
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    glEnable(GL_STENCIL_TEST);
    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
       drawFloor();
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glEnable(GL_DEPTH_TEST);
    glStencilFunc(GL_EQUAL, 1, 0xffffffff);
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    glPushMatrix();
       glScalef(1.0, -1.0, 1.0);
       for (int x = 0 ; x < NB_CUBE_X ; x++)
          for (int y = 0 ; y < NB_CUBE_Y ; y++)
             for (int z = 0 ; z < NB_CUBE_Z ; z++)
             {
                   Cube[x][y][z]->Update(t);
                   Cube[x][y][z]->Draw(0,-20,zz+20);
              }
    glPopMatrix();
    glDisable(GL_STENCIL_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       drawFloor();
    glDisable(GL_BLEND);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    for (x = 0 ; x < NB_CUBE_X ; x++)
    for (int y = 0 ; y < NB_CUBE_Y ; y++)
    for (int z = 0 ; z < NB_CUBE_Z ; z++)
    Cube[x][y][z]->Draw(0,-20,zz+20);
glPopMatrix();

الشيء الوحيد الذي ينبغي القيام به هو, وجود دالة التحجيم ()glScalef بعد المسطح العاكس.

ننتقل للظلال
في هذه الحالة, من الصعوبة الكبرى أنه لا توجد طريقة بسيطة للحصول على مصفوفة الإسقاط في تطبيق OpenGL
إذن فلتحيا النسخ و اللسق, وبوجود شيفرة برمجية من Mark J. Kilgard سيكون ذلك أسهل, إلا إذا إن خالفتني الرئي وقلت لي "
اه أين تكمن الصعوبة أنا لدي الحل.
إنشاء مصفوفة لإسقاط الضل المرغوب فيه :
void shadowMatrix(GLfloat shadowMat[4][4], GLfloat groundplane[4], GLfloat lightpos[4])
{
    GLfloat dot;
    dot = groundplane[X] * lightpos[X] + groundplane[Y] * lightpos[Y] + groundplane[Z] * lightpos[Z] + groundplane[W] *   lightpos[W];

    shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
    shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
    shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
    shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];

    shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
    shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];  
    shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
    shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];

    shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
    shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
    shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
    shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];

    shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
    shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
    shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
    shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
}

البارمترات هي لدالة السطح "أ س+ب ع+ج ص+ د=0" أي"ax + by + cz + d = 0" , و لأكثر سهولة إستعمل:
/*  lإيجاد معادلة سطح تعطي ثلاثة نقاطl  */
enum { X, Y, Z, W};
enum { A, B, C, D};
void findPlane(GLfloat plane[4], GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
{
    GLfloat vec0[3], vec1[3];
    /*  l الحاجة إلى شعاعين لإيجاد ناتج العبور l   */
    vec0[X] = v1[X] - v0[X];
    vec0[Y] = v1[Y] - v0[Y];
    vec0[Z] = v1[Z] - v0[Z];
    vec1[X] = v2[X] - v0[X];
    vec1[Y] = v2[Y] - v0[Y];
    vec1[Z] = v2[Z] - v0[Z];
    /*  l إيجاد ناتج العبور لإعطاء القيم أ,ب,ج و د لدالة السطحl    */
    plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
    plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X];
    plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
    plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
}

و الأن يجب عمل التالي, الإبقاء على الرسم باللون الأسود مع 50 بالمائة من الشفافية ليمثل الضل
void drawFloor()
{
    GLfloat white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    GLfloat floorc[] = {0.5f, 0.5f, 0.5f, 0.75f};

    glMaterialfv(GL_FRONT, GL_DIFFUSE, floorc);
    glMaterialfv(GL_FRONT, GL_SPECULAR, floorc);
    glBegin(GL_QUADS);
       glVertex3fv(vfloor[0]);
       glVertex3fv(vfloor[1]);
       glVertex3fv(vfloor[2]);
       glVertex3fv(vfloor[3]);
    glEnd();
    glMaterialfv(GL_FRONT, GL_DIFFUSE, white);
    glMaterialfv(GL_FRONT, GL_SPECULAR, white);
}

تكملت الرسم
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   drawFloor();
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glStencilFunc(GL_ALWAYS, 2, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
for (x = 0 ; x < NB_CUBE_X ; x++)
   for (int y = 0 ; y < NB_CUBE_Y ; y++)
      for (int z = 0 ; z < NB_CUBE_Z ; z++)
         Cube[x][y][z]->Draw(0,-20,zz+20);
glStencilFunc(GL_EQUAL, 1, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glColor4f(0.0, 0.0, 0.0, 0.5);
static GLfloat floorPlane[4];
static GLfloat floorShadow[4][4];
findPlane(floorPlane, vfloor[1], vfloor[2], vfloor[3]);
shadowMatrix(floorShadow, floorPlane, lightZeroPosition);
glPushMatrix();
    glMultMatrixf((GLfloat *) floorShadow);
    for (x = 0 ; x < NB_CUBE_X ; x++)
       for (int y = 0 ; y < NB_CUBE_Y ; y++)
          for (int z = 0 ; z < NB_CUBE_Z ; z++)
                  Cube[x][y][z]->Draw(0,-20,zz+20);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
glDisable(GL_STENCIL_TEST);

الشيء الأول,هوأن الكائن أو المجسم بإمكانه إخفاء ضله .الضل لا يرسم إذا ما كان الإستنسل في الواحد "1".وكل نقطة لا ترسم إلا مرة واحدة لأن العديد من الأسطح يمكن أن تحمل ضلال.
تعطيل فحص العمق depth-test لأن حساب التضليل غير دقيق في حالة ما لم يعطق فحص العمق(ستظهر تشوهات لضل المجسم).

أتمنى لكم التوفيق