/* shadowvol.c - by Tom McReynolds, SGI */ /* Shadows: Shadow maps */ #include #include /* Demonstrate shadow volumes */ /* Create a single component texture map */ GLfloat * make_texture(int maxs, int maxt) { int s, t; static GLfloat *texture; texture = (GLfloat *) malloc(maxs * maxt * sizeof(GLfloat)); for (t = 0; t < maxt; t++) { for (s = 0; s < maxs; s++) { texture[s + maxs * t] = ((s >> 4) & 0x1) ^ ((t >> 4) & 0x1); } } return texture; } enum { SPHERE = 1, CONE, LIGHT, SHADOWVOL }; typedef struct { GLfloat *verticies; GLfloat *normal; int n; /* number of verticies */ } ShadObj; GLfloat shadVerts[] = {30.f, 30.f, -350.f, 60.f, 20.f, -340.f, 40.f, 40.f, -400.f}; GLfloat shadNormal[] = {1.f, 1.f, 1.f}; ShadObj shadower; enum { X, Y, Z }; /* simple way to extend a point to build shadow volume */ void extend(GLfloat new[3], GLfloat light[3], GLfloat vertex[3], GLfloat t) { GLfloat delta[3]; delta[X] = vertex[X] - light[X]; delta[Y] = vertex[Y] - light[Y]; delta[Z] = vertex[Z] - light[Z]; new[X] = light[X] + delta[X] * t; new[Y] = light[Y] + delta[Y] * t; new[Z] = light[Z] + delta[Z] * t; } /* Create a shadow volume in a display list */ /* XXX light should have 4 compoents */ void makeShadowVolume(ShadObj * shadower, GLfloat light[3], GLfloat t, GLint dlist) { int i; GLfloat newv[3]; glNewList(dlist, GL_COMPILE); glDisable(GL_LIGHTING); glBegin(GL_QUADS); /* for debugging */ glColor3f(.2f, .8f, .4f); for (i = 0; i < shadower->n; i++) { glVertex3fv(&shadower->verticies[i * 3]); extend(newv, light, &shadower->verticies[i * 3], t); glVertex3fv(newv); extend(newv, light, &shadower->verticies[((i + 1) % shadower->n) * 3], t); glVertex3fv(newv); glVertex3fv(&shadower->verticies[((i + 1) % shadower->n) * 3]); } glEnd(); glEnable(GL_LIGHTING); glEndList(); } void sphere(void) { glPushMatrix(); glTranslatef(60.f, -50.f, -360.f); glCallList(SPHERE); glPopMatrix(); } void cone(void) { glPushMatrix(); glTranslatef(-40.f, -40.f, -400.f); glCallList(CONE); glPopMatrix(); } enum { NONE, NOLIGHT, VOLUME, SHADOW }; int rendermode = NONE; void menu(int mode) { rendermode = mode; glutPostRedisplay(); } GLfloat leftwallshadow[4][4]; GLfloat floorshadow[4][4]; GLfloat lightpos[] = {50.f, 50.f, -340.f, 1.f}; /* render while jittering the shadows */ void render(ShadObj * obj) { static GLfloat shad_mat[] = {1.f, .1f, .1f, 1.f}; GLfloat *v; /* vertex pointer */ int i; /* material properties for objects in scene */ static GLfloat wall_mat[] = {1.f, 1.f, 1.f, 1.f}; /* Note: wall verticies are ordered so they are all front facing this lets me do back face culling to speed things up. */ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat); /* floor */ /* make the floor textured */ glEnable(GL_TEXTURE_2D); /* Since we want to turn texturing on for floor only, we have to make floor a separate glBegin()/glEnd() sequence. You can't turn texturing on and off between begin and end calls */ glBegin(GL_QUADS); glNormal3f(0.f, 1.f, 0.f); glTexCoord2i(0, 0); glVertex3f(-100.f, -100.f, -320.f); glTexCoord2i(1, 0); glVertex3f(100.f, -100.f, -320.f); glTexCoord2i(1, 1); glVertex3f(100.f, -100.f, -520.f); glTexCoord2i(0, 1); glVertex3f(-100.f, -100.f, -520.f); glEnd(); glDisable(GL_TEXTURE_2D); /* walls */ glBegin(GL_QUADS); /* left wall */ glNormal3f(1.f, 0.f, 0.f); glVertex3f(-100.f, -100.f, -320.f); glVertex3f(-100.f, -100.f, -520.f); glVertex3f(-100.f, 100.f, -520.f); glVertex3f(-100.f, 100.f, -320.f); /* right wall */ glNormal3f(-1.f, 0.f, 0.f); glVertex3f(100.f, -100.f, -320.f); glVertex3f(100.f, 100.f, -320.f); glVertex3f(100.f, 100.f, -520.f); glVertex3f(100.f, -100.f, -520.f); /* ceiling */ glNormal3f(0.f, -1.f, 0.f); glVertex3f(-100.f, 100.f, -320.f); glVertex3f(-100.f, 100.f, -520.f); glVertex3f(100.f, 100.f, -520.f); glVertex3f(100.f, 100.f, -320.f); /* back wall */ glNormal3f(0.f, 0.f, 1.f); glVertex3f(-100.f, -100.f, -520.f); glVertex3f(100.f, -100.f, -520.f); glVertex3f(100.f, 100.f, -520.f); glVertex3f(-100.f, 100.f, -520.f); glEnd(); cone(); sphere(); glCallList(LIGHT); /* draw shadowing object */ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, shad_mat); glBegin(GL_POLYGON); glNormal3fv(obj->normal); for (v = obj->verticies, i = 0; i < obj->n; i++) { glVertex3fv(v); v += 3; } glEnd(); } void redraw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); switch (rendermode) { case NONE: render(&shadower); break; case NOLIGHT: glDisable(GL_LIGHT0); render(&shadower); glEnable(GL_LIGHT0); break; case VOLUME: render(&shadower); glCallList(SHADOWVOL); break; case SHADOW: glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); render(&shadower); /* render scene in depth buffer */ glEnable(GL_STENCIL_TEST); glDepthMask(GL_FALSE); glStencilFunc(GL_ALWAYS, 0, 0); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glCullFace(GL_BACK); /* increment using front face of shadow volume */ glCallList(SHADOWVOL); glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); glCullFace(GL_FRONT); /* increment using front face of shadow volume */ glCallList(SHADOWVOL); glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glCullFace(GL_BACK); glDepthFunc(GL_LEQUAL); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc(GL_EQUAL, 1, 1); /* draw shadowed part */ glDisable(GL_LIGHT0); render(&shadower); glStencilFunc(GL_EQUAL, 0, 1); /* draw lit part */ glEnable(GL_LIGHT0); render(&shadower); glDepthFunc(GL_LESS); glDisable(GL_STENCIL_TEST); break; } glutSwapBuffers(); /* high end machines may need this */ } /* ARGSUSED1 */ void key(unsigned char key, int x, int y) { if (key == '\033') exit(0); } const int TEXDIM = 256; /* Parse arguments, and set up interface between OpenGL and window system */ int main(int argc, char *argv[]) { GLfloat *tex; GLUquadricObj *sphere, *cone, *base; static GLfloat sphere_mat[] = {1.f, .5f, 0.f, 1.f}; static GLfloat cone_mat[] = {0.f, .5f, 1.f, 1.f}; glutInit(&argc, argv); glutInitWindowSize(512, 512); glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL | GLUT_DOUBLE); (void) glutCreateWindow("shadow volumes"); glutDisplayFunc(redraw); glutKeyboardFunc(key); glutCreateMenu(menu); glutAddMenuEntry("No Shadows", NONE); glutAddMenuEntry("No Light", NOLIGHT); glutAddMenuEntry("Show Volume", VOLUME); glutAddMenuEntry("Shadows", SHADOW); glutAttachMenu(GLUT_RIGHT_BUTTON); /* draw a perspective scene */ glMatrixMode(GL_PROJECTION); glFrustum(-100., 100., -100., 100., 320., 640.); glMatrixMode(GL_MODELVIEW); /* turn on features */ glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_CULL_FACE); /* place light 0 in the right place */ glLightfv(GL_LIGHT0, GL_POSITION, lightpos); /* remove back faces to speed things up */ glCullFace(GL_BACK); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); /* make display lists for sphere and cone; for efficiency */ glNewList(SPHERE, GL_COMPILE); sphere = gluNewQuadric(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat); gluSphere(sphere, 20.f, 20, 20); gluDeleteQuadric(sphere); glEndList(); glNewList(CONE, GL_COMPILE); cone = gluNewQuadric(); base = gluNewQuadric(); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat); glRotatef(-90.f, 1.f, 0.f, 0.f); gluDisk(base, 0., 20., 20, 1); gluCylinder(cone, 20., 0., 60., 20, 20); gluDeleteQuadric(cone); gluDeleteQuadric(base); glEndList(); glNewList(LIGHT, GL_COMPILE); sphere = gluNewQuadric(); glPushMatrix(); glTranslatef(lightpos[X], lightpos[Y], lightpos[Z]); glDisable(GL_LIGHTING); glColor3f(.9f, .9f, .6f); gluSphere(sphere, 5.f, 20, 20); glEnable(GL_LIGHTING); glPopMatrix(); gluDeleteQuadric(sphere); glEndList(); /* load pattern for current 2d texture */ tex = make_texture(TEXDIM, TEXDIM); glTexImage2D(GL_TEXTURE_2D, 0, 1, TEXDIM, TEXDIM, 0, GL_RED, GL_FLOAT, tex); free(tex); shadower.verticies = shadVerts; shadower.normal = shadNormal; shadower.n = sizeof(shadVerts) / (3 * sizeof(GLfloat)); makeShadowVolume(&shadower, lightpos, 10.f, SHADOWVOL); glutMainLoop(); return 0; /* ANSI C requires main to return int. */ }