PDA

View Full Version : projection shadows with d!=0

dunken69
10-30-2002, 01:05 PM
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 );

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

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 );

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);
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;
[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;
[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;
[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;
[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;
[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( x*WALLLENGTH, 0, y*WALLLENGTH);

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
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);
}

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

// matrix stuff

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]);
}