Выбрать главу

 glStencilFunc( GL_NOTEQUAL, 0, 0xFFFFFFFFL );

 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );

 glPushMatrix();

 glLoadIdentity();

 glBegin( GL_TRIANGLE_STRIP );

  glVertex3f(-0.1f, 0.1f,-0.10f);

  glVertex3f(-0.1f,-0.1f,-0.10f);

  glVertex3f( 0.1f, 0.1f,-0.10f);

  glVertex3f( 0.1f,-0.1f,-0.10f);

 glEnd();

 glPopMatrix();

 glPopAttrib();

}

Ok, the next part draws the shadowed quads. How does that work? What happens is that you go through every face, and if it is visible, then you check all of its edges. If at the edge, there is no neighbouring face, or the neighbouring face is not visible, the edge casts a shadow. If you think about the two cases clearly, then you'll see this is true. By drawing a quadrilateral (as two triangles) comprising of the points of the edge, and the edge projected backwards through the scene you get the shadow cast by it.

The brute force approach used here just draws to "infinity", and the shadow polygon is clipped against all the polygons it encounters. This causes piercing, which will stress the video hardware. For a high-performance modification to this algorithm, you should clip the polygon to the objects behind it. This is much trickier and has problems of its own, but if that's what you want to do, you should refer to this Gamasutra article.

The code to do all of that is not as tricky as it sounds. To start with, here is a snippet that loops through the objects. By the end of it, we have an edge, j , and its neighbouring face, specified by neighbourIndex .

void doShadowPass( ShadowedObject& object, GLfloat *lightPosition ) {

 for ( int i = 0; i < object.nFaces; i++ ) {

  const Face& face = object.pFaces[i];

  if ( face.visible ) {

   // Go Through Each Edge

   for ( int j = 0; j < 3; j++ ) {

    int neighbourIndex = face.neighbourIndices[j];

Next, check if there is a visible neighbouring face to this object. If not, then this edge casts a shadow.

    // If There Is No Neighbour, Or Its Neighbouring Face Is Not Visible, Then This Edge Casts A Shadow

    if ( neighbourIndex == –1 || object.pFaces[neighbourIndex].visible == false ) {

The next segment of code will retrieve the two vertices from the current edge, v1 and v2. Then, it calculates v3 and v4, which are projected along the vector between the light source and the first edge. They are scaled to INFINITY, which was set to a very large value.

     // Get The Points On The Edge

     const Point3f& v1 = object.pVertices[face.vertexIndices[j]];

     const Point3f& v2 = object.pVertices[face.vertexIndices[( j+1 )%3]];

     // Calculate The Two Vertices In Distance

     Point3f v3, v4;

     v3.x = ( v1.x-lightPosition[0] )*INFINITY;

     v3.y = ( v1.y-lightPosition[1] )*INFINITY;

     v3.z = ( v1.z-lightPosition[2] )*INFINITY;

     v4.x = ( v2.x-lightPosition[0] )*INFINITY;

     v4.y = ( v2.y-lightPosition[1] )*INFINITY;

     v4.z = ( v2.z-lightPosition[2] )*INFINITY;

I think you'll understand the next section, it justs draws the quadrilateral defined by those four points:

     // Draw The Quadrilateral (As A Triangle Strip)

     glBegin( GL_TRIANGLE_STRIP );

      glVertex3f( v1.x, v1.y, v1.z );

      glVertex3f( v1.x+v3.x, v1.y+v3.y, v1.z+v3.z );

      glVertex3f( v2.x, v2.y, v2.z );

      glVertex3f( v2.x+v4.x, v2.y+v4.y, v2.z+v4.z );

     glEnd();

    }

   }

  }

 }

}

With that, the shadow casting section is completed. But we are not finished yet! What about drawGLScene? Lets start with the simple bits: clearing the buffers, positioning the light source, and drawing a sphere:

bool drawGLScene() {

 GLmatrix16f Minv;

 GLvector4f wlp, lp;

 // Clear Color Buffer, Depth Buffer, Stencil Buffer

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

 glLoadIdentity(); // Reset Modelview Matrix

 glTranslatef(0.0f, 0.0f, –20.0f); // Zoom Into Screen 20 Units

 glLightfv(GL_LIGHT1, GL_POSITION, LightPos); // Position Light1

 glTranslatef(SpherePos[0], SpherePos[1], SpherePos[2]); // Position The Sphere

 gluSphere(q, 1.5f, 32, 16); // Draw A Sphere

Next, we have to calculate the light's position relative to the local coordinate system of the object. The comments explain each step in detail. Minv stores the object's transformation matrix, however it is done in reverse, and with negative arguments, so it is actually the inverse of the transformation matrix. Then lp is created as a copy of the light's position, and multiplied by the matrix. Thus, lp is the light's position in the object's coordinate system.

 glLoadIdentity(); // Reset Matrix

 glRotatef(-yrot, 0.0f, 1.0f, 0.0f); // Rotate By –yrot On Y Axis

 glRotatef(-xrot, 1.0f, 0.0f, 0.0f); // Rotate By –xrot On X Axis

 glTranslatef(-ObjPos[0], –ObjPos[1], –ObjPos[2]); // Move Negative On All Axis Based On ObjPos[] Values (X, Y, Z)

 glGetFloatv(GL_MODELVIEW_MATRIX,Minv); // Retrieve ModelView Matrix (Stores In Minv)

 lp[0] = LightPos[0]; // Store Light Position X In lp[0]

 lp[1] = LightPos[1]; // Store Light Position Y In lp[1]

 lp[2] = LightPos[2]; // Store Light Position Z In lp[2]

 lp[3] = LightPos[3]; // Store Light Direction In lp[3]

 VMatMult(Minv, lp); // We Store Rotated Light Vector In 'lp' Array

Now, palm off some of the work to draw the room, and the object. Calling castShadow draws the shadow of the object.

 glLoadIdentity(); // Reset Modelview Matrix

 glTranslatef(0.0f, 0.0f, –20.0f); // Zoom Into The Screen 20 Units

 DrawGLRoom(); // Draw The Room

 glTranslatef(ObjPos[0], ObjPos[1], ObjPos[2]); // Position The Object

 glRotatef(xrot, 1.0f, 0.0f, 0.0f); // Spin It On The X Axis By xrot

 glRotatef(yrot, 0.0f, 1.0f, 0.0f); // Spin It On The Y Axis By yrot

 drawObject(obj); // Procedure For Drawing The Loaded Object

 castShadow(obj, lp); // Procedure For Casting The Shadow Based On The Silhouette

The following few lines draw a little orange circle where the light is:

 glColor4f(0.7f, 0.4f, 0.0f, 1.0f); // Set Color To An Orange

 glDisable(GL_LIGHTING); // Disable Lighting

 glDepthMask(GL_FALSE); // Disable Depth Mask

 glTranslatef(lp[0], lp[1], lp[2]); // Translate To Light's Position

 // Notice We're Still In Local Coordinate System