projection shadows with d!=0

Hi!

I have succeded in doing some projection shadow stuff, but some things still don’t work properly. In the following example, I project an object consisting of four walls (each wall consisting of one back and one front polygon, possibly tesselated) onto a floor and some other walls. Interestingly, the shadow only projects correctly if the planes it is projected onto run through the center of the coordinate system, ie. if “d” in “ax + by + cz -d = 0” is 0. In cases where d != 0, the projection gets completely wrong.
I don’t think the problem is in the math (which You’ll find in the methods calculateShadowMatrix() and calculateShadowPlane()), because I more or less copied these methods from books.
I have tried a few things, but since I’m absolutely no OpenGL-guru, I don’t really have a clue about what is wrong here. I’d appreciate it very much if someone here could have a look at the code (which is rather long…) and tell if there is something that is obviously wrong.

Thanks!

  • (void)drawRect:(NSRect)aRect
    {
    // x and z of the light can change…
    lightPosition[0] = xPos;
    lightPosition[2] = zPos;

    glViewport( 0,0,NSWidth([self bounds]),NSHeight([self bounds]) );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();

    gluPerspective( 60, 0.9, 1, 20 );
    glMatrixMode( GL_MODELVIEW );

    glLoadIdentity();
    gluLookAt( 3, 8, 5, // looking from
    3, 1, 3, // looking at
    0, 0, -1 ); // up-vector (0, 0, -1): camera top pointing down the negative z-axis

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glEnable(GL_LIGHTING);

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );

    glClearColor( 0,0,0,0 );
    glShadeModel(GL_SMOOTH);

    glMaterialfv(GL_FRONT, GL_AMBIENT, matAmbient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiffuse);

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight1);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);
    glEnable(GL_LIGHT0);

    // make stencil for floor
    [SimpleOpenGLView startStencil];
    [self drawWallAtX: 0 Y: 0 withOrientation: FLOOR];
    [self drawWallAtX: 0 Y: 1 withOrientation: FLOOR];
    [self drawWallAtX: 1 Y: 0 withOrientation: FLOOR];
    [self drawWallAtX: 1 Y: 1 withOrientation: FLOOR];

    // render shadow on floor (p1, p2 and p3 are points on that plane)
    p1[0] = 0.0f; p1[1] = 0.0f; p1[2] = 0.0f;
    p2[0] = -1.0f; p2[1] = 0.0f; p2[2] = 0.0f;
    p3[0] = 0.0f; p3[1] = 0.0f; p3[2] = 1.0f;
    [SimpleOpenGLView calculateShadowPlane:shadowPlane forP1:p1 P2:p2 andP3:p3];
    [self projectWalls];

    // make stencil for horizontal walls at y = 0
    [SimpleOpenGLView startStencil];
    [self drawWallAtX: 1 Y: 0 withOrientation: HORIZONTAL];
    [self drawWallAtX: 0 Y: 0 withOrientation: HORIZONTAL];

    // render shadow on horizontal walls at y = 0 (p1, p2 and p3 are points on that plane)
    p1[0] = 0.0f; p1[1] = 0.0f; p1[2] = 0.0f;
    p2[0] = 1.0f; p2[1] = 0.0f; p2[2] = 0.0f;
    p3[0] = 1.0f; p3[1] = 1.0f; p3[2] = 0.0f;
    [SimpleOpenGLView calculateShadowPlane:shadowPlane forP1:p1 P2:p2 andP3:p3];
    [self projectWalls];

    // make stencil for vertical walls at x = 0
    [SimpleOpenGLView startStencil];
    [self drawWallAtX: 0 Y: 1 withOrientation: VERTICAL];
    [self drawWallAtX: 0 Y: 0 withOrientation: VERTICAL];

    // render shadow on vertical walls at x = 0 (p1, p2 and p3 are points on that plane)
    p1[0] = 0.0f; p1[1] = 0.0f; p1[2] = 0.0f;
    p2[0] = 0.0f; p2[1] = 1.0f; p2[2] = 0.0f;
    p3[0] = 0.0f; p3[1] = 0.0f; p3[2] = 1.0f;
    [SimpleOpenGLView calculateShadowPlane:shadowPlane forP1:p1 P2:p2 andP3:p3];
    [self projectWalls];

    // the following shadow projections don’t work properly…
    // make stencil for horizontal walls at y = 2
    [SimpleOpenGLView startStencil];
    [self drawWallAtX: 1 Y: 1 withOrientation: HORIZONTAL];

    // render shadow on horizontal walls at y = 2 (p1, p2 and p3 are points on that plane)
    p1[0] = 0.0f; p1[1] = 0.0f; p1[2] = 2.0f;
    p2[0] = 1.0f; p2[1] = 0.0f; p2[2] = 2.0f;
    p3[0] = 1.0f; p3[1] = 1.0f; p3[2] = 2.0f;
    [SimpleOpenGLView calculateShadowPlane:shadowPlane forP1:p1 P2:p2 andP3:p3];
    [self projectWalls];

    // make stencil for horizontal walls at y = 4
    [SimpleOpenGLView startStencil];
    [self drawWallAtX: 1 Y: 2 withOrientation: HORIZONTAL];

    // render shadow on horizontal walls at y = 4 (p1, p2 and p3 are points on that plane)
    p1[0] = 0.0f; p1[1] = 0.0f; p1[2] = 4.0f;
    p2[0] = 1.0f; p2[1] = 0.0f; p2[2] = 4.0f;
    p3[0] = 1.0f; p3[1] = 1.0f; p3[2] = 4.0f;
    [SimpleOpenGLView calculateShadowPlane:shadowPlane forP1:p1 P2:p2 andP3:p3];
    [self projectWalls];

    // draw rest…
    [self drawWallAtX: 1 Y: 1 withOrientation: VERTICAL];
    [self drawWallAtX: 2 Y: 1 withOrientation: VERTICAL];

    glFlush();
    }

// this will draw a wall within a two-dimensional coordinate system.
// Imagine looking down the y-axis in 3d space. 2d’s x-axis is 3d’s
// x-axis as well, but 2d’s y-axis is really 3d’s z-axis!
// The third parameter tells us if the wall will be vertical or Vertical

  • (void)drawWallAtX:(int)x Y:(int)y withOrientation:(int)orientation;
    {
    int row;
    int column;
    // first, we translate to where we want the wall to be drawn
    glPushMatrix();
    // all walls are squares. WALLLENGTH is the length of the squares sides
    glTranslated( xWALLLENGTH, 0, yWALLLENGTH);

    if (orientation == VERTICAL)
    glRotatef( 270.0f, 0.0f, 1.0f, 0.0f );
    else if (orientation == FLOOR)
    glRotatef( 90.0f, 1.0f, 0.0f, 0.0f );

    // tesselationFactor defines how often a walls side will be split
    for ( row = 0; row < tesselationFactor; row++ ) {
    for ( column = 0; column < tesselationFactor; column++ ) {
    // front
    glBegin( GL_QUADS );
    glNormal3f( 0.0, 0.0, 1.0 );
    glVertex3f( subCoordinates[row], subCoordinates[column], 0.0 );
    glVertex3f( subCoordinates[row+1], subCoordinates[column], 0.0 );
    glVertex3f( subCoordinates[row+1], subCoordinates[column+1], 0.0 );
    glVertex3f( subCoordinates[row], subCoordinates[column+1], 0.0 );
    // back
    glNormal3f( 0.0, 0.0, -1.0 );
    glVertex3f( subCoordinates[row], subCoordinates[column+1], 0.0 );
    glVertex3f( subCoordinates[row+1], subCoordinates[column+1], 0.0 );
    glVertex3f( subCoordinates[row+1], subCoordinates[column], 0.0 );
    glVertex3f( subCoordinates[row], subCoordinates[column], 0.0 );
    glEnd();
    }
    }
    glPopMatrix();
    }

  • (void)projectWalls
    {
    [SimpleOpenGLView continueStencil];
    [self drawWallAtX: 1 Y: 1 withOrientation: HORIZONTAL];
    [self drawWallAtX: 1 Y: 1 withOrientation: VERTICAL];
    [self drawWallAtX: 1 Y: 2 withOrientation: HORIZONTAL];
    [self drawWallAtX: 2 Y: 1 withOrientation: VERTICAL];
    [SimpleOpenGLView endStencil];
    }

// Stencil methods:

  • (void)startStencil // after this, the polygons that define the stencil will be drawn
    {
    glClear(GL_STENCIL_BUFFER_BIT);
    glEnable(GL_STENCIL_TEST);
    glStencilFunc(GL_ALWAYS, stencilValue, ~0);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    }

  • (void)continueStencil // after this, the polygons to be projected will be drawn
    {
    glPushMatrix();
    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_DST_COLOR, GL_ZERO);
    glColor3f(0.8f, 0.8f, 0.8f);
    glStencilFunc(GL_EQUAL, 2, ~0);
    glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
    [SimpleOpenGLView calculateShadowMatrix:gl_shadowMatrix forLight:lightPosition andPlane:shadowPlane];
    glMultMatrixf(gl_shadowMatrix);
    }

  • (void)endStencil
    {
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
    glDisable(GL_STENCIL_TEST);
    glEnable(GL_LIGHTING);
    glPopMatrix();
    }

// matrix stuff

  • (void)calculateShadowMatrix:(float[16])matrix forLight:(float[4])lightPos andPlane:(float[4])plane {
    float dot = plane[0] * lightPos[0] + plane[1] * lightPos[1] + plane[2] * lightPos[2] + plane[3] * lightPos[3];

    // first column
    matrix[0] = dot - lightPos[0] * plane[0];
    matrix[4] = - lightPos[0] * plane[1];
    matrix[8] = - lightPos[0] * plane[2];
    matrix[12] = - lightPos[0] * plane[3];
    // second column
    matrix[1] = - lightPos[1] * plane[0];
    matrix[5] = dot - lightPos[1] * plane[1];
    matrix[9] = - lightPos[1] * plane[2];
    matrix[13] = - lightPos[1] * plane[3];
    // third column
    matrix[2] = - lightPos[2] * plane[0];
    matrix[6] = - lightPos[2] * plane[1];
    matrix[10] = dot - lightPos[2] * plane[2];
    matrix[14] = - lightPos[2] * plane[3];
    // fourth column
    matrix[3] = - lightPos[3] * plane[0];
    matrix[7] = - lightPos[3] * plane[1];
    matrix[11] = - lightPos[3] * plane[2];
    matrix[15] = dot - lightPos[3] * plane[3];
    }

  • (void)calculateShadowPlane:(float[4])plane forP1:(float[3])p1 P2:(float[3])p2 andP3:(float[3])p3 {
    plane[0] = ((p2[1]-p1[1])(p3[2]-p1[2]))-((p2[2]-p1[2])(p3[1]-p1[1]));
    plane[1] = ((p2[2]-p1[2])(p3[0]-p1[0]))-((p2[0]-p1[0])(p3[2]-p1[2]));
    plane[2] = ((p2[0]-p1[0])(p3[1]-p1[1]))-((p2[1]-p1[1])(p3[0]-p1[0]));
    plane[3] = (plane[0]*p1[0] + plane[1]*p1[1] + plane[2]*p1[2]);
    }