/* * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and * GL_ARB_shadow_ambient extensions. * * Brian Paul * 19 Feb 2001 * * Added GL_EXT_shadow_funcs support on 23 March 2002 * Added GL_EXT_packed_depth_stencil support on 15 March 2006. * Added GL_EXT_framebuffer_object support on 27 March 2006. * Removed old SGIX extension support on 5 April 2006. * Added vertex / fragment program support on 7 June 2007 (Ian Romanick). * * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include "showbuffer.h" #define DEG_TO_RAD (3.14159 / 180.0) static GLint WindowWidth = 450, WindowHeight = 300; static GLfloat Xrot = 15, Yrot = 0, Zrot = 0; static GLfloat Red[4] = {1, 0, 0, 1}; static GLfloat Green[4] = {0, 1, 0, 1}; static GLfloat Blue[4] = {0, 0, 1, 1}; static GLfloat Yellow[4] = {1, 1, 0, 1}; static GLfloat LightDist = 10; static GLfloat LightLatitude = 45.0; static GLfloat LightLongitude = 45.0; static GLfloat LightPos[4]; static GLfloat SpotDir[3]; static GLfloat SpotAngle = 40.0 * DEG_TO_RAD; static GLfloat ShadowNear = 4.0, ShadowFar = 24.0; static GLint ShadowTexWidth = 256, ShadowTexHeight = 256; static GLboolean LinearFilter = GL_FALSE; static GLfloat Bias = -0.06; static GLboolean Anim = GL_TRUE; static GLboolean NeedNewShadowMap = GL_FALSE; static GLuint ShadowTexture, GrayTexture; static GLuint ShadowFBO; static GLfloat lightModelview[16]; static GLfloat lightProjection[16]; static GLuint vert_prog; static GLuint frag_progs[3]; static GLuint curr_frag = 0; static GLuint max_frag = 1; #define NUM_FRAG_MODES 3 static const char *FragProgNames[] = { "fixed-function", "program without \"OPTION ARB_fragment_program_shadow\"", "program with \"OPTION ARB_fragment_program_shadow\"", }; static GLboolean HaveShadow = GL_FALSE; static GLboolean HaveFBO = GL_FALSE; static GLboolean UseFBO = GL_FALSE; static GLboolean HaveVP = GL_FALSE; static GLboolean HaveFP = GL_FALSE; static GLboolean HaveFP_Shadow = GL_FALSE; static GLboolean UseVP = GL_FALSE; static GLboolean HavePackedDepthStencil = GL_FALSE; static GLboolean UsePackedDepthStencil = GL_FALSE; static GLboolean HaveEXTshadowFuncs = GL_FALSE; static GLboolean HaveShadowAmbient = GL_FALSE; static GLint Operator = 0; static const GLenum OperatorFunc[8] = { GL_LEQUAL, GL_LESS, GL_GEQUAL, GL_GREATER, GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER }; static const char *OperatorName[8] = { "GL_LEQUAL", "GL_LESS", "GL_GEQUAL", "GL_GREATER", "GL_EQUAL", "GL_NOTEQUAL", "GL_ALWAYS", "GL_NEVER" }; static GLuint DisplayMode; #define SHOW_SHADOWS 0 #define SHOW_DEPTH_IMAGE 1 #define SHOW_DEPTH_MAPPING 2 #define SHOW_DISTANCE 3 #define MAT4_MUL(dest_vec, src_mat, src_vec) \ "DP4 " dest_vec ".x, " src_mat "[0], " src_vec ";\n" \ "DP4 " dest_vec ".y, " src_mat "[1], " src_vec ";\n" \ "DP4 " dest_vec ".z, " src_mat "[2], " src_vec ";\n" \ "DP4 " dest_vec ".w, " src_mat "[3], " src_vec ";\n" #define MAT3_MUL(dest_vec, src_mat, src_vec) \ "DP3 " dest_vec ".x, " src_mat "[0], " src_vec ";\n" \ "DP3 " dest_vec ".y, " src_mat "[1], " src_vec ";\n" \ "DP3 " dest_vec ".z, " src_mat "[2], " src_vec ";\n" #define NORMALIZE(dest, src) \ "DP3 " dest ".w, " src ", " src ";\n" \ "RSQ " dest ".w, " dest ".w;\n" \ "MUL " dest ", " src ", " dest ".w;\n" /** * Vertex program for shadow mapping. */ static const char vert_code[] = "!!ARBvp1.0\n" "ATTRIB iPos = vertex.position;\n" "ATTRIB iNorm = vertex.normal;\n" "PARAM mvinv[4] = { state.matrix.modelview.invtrans };\n" "PARAM mvp[4] = { state.matrix.mvp };\n" "PARAM mv[4] = { state.matrix.modelview };\n" "PARAM texmat[4] = { state.matrix.texture[0] };\n" "PARAM lightPos = state.light[0].position;\n" "PARAM ambientCol = state.lightprod[0].ambient;\n" "PARAM diffuseCol = state.lightprod[0].diffuse;\n" "TEMP n, lightVec;\n" "ALIAS V = lightVec;\n" "ALIAS NdotL = n;\n" "OUTPUT oPos = result.position;\n" "OUTPUT oColor = result.color;\n" "OUTPUT oTex = result.texcoord[0];\n" /* Transform the vertex to clip coordinates. */ MAT4_MUL("oPos", "mvp", "iPos") /* Transform the vertex to eye coordinates. */ MAT4_MUL("V", "mv", "iPos") /* Transform the vertex to projected light coordinates. */ MAT4_MUL("oTex", "texmat", "iPos") /* Transform the normal to eye coordinates. */ MAT3_MUL("n", "mvinv", "iNorm") /* Calculate the vector from the vertex to the light in eye * coordinates. */ "SUB lightVec, lightPos, V;\n" NORMALIZE("lightVec", "lightVec") /* Compute diffuse lighting coefficient. */ "DP3 NdotL.x, n, lightVec;\n" "MAX NdotL.x, NdotL.x, {0.0};\n" "MIN NdotL.x, NdotL.x, {1.0};\n" /* Accumulate color contributions. */ "MOV oColor, diffuseCol;\n" "MAD oColor.xyz, NdotL.x, diffuseCol, ambientCol;\n" "END\n" ; static const char frag_code[] = "!!ARBfp1.0\n" "TEMP shadow, temp;\n" "TXP shadow, fragment.texcoord[0], texture[0], 2D;\n" "RCP temp.x, fragment.texcoord[0].w;\n" "MUL temp.x, temp.x, fragment.texcoord[0].z;\n" "SGE shadow, shadow.x, temp.x;\n" "MUL result.color.rgb, fragment.color, shadow.x;\n" "MOV result.color.a, fragment.color;\n" "END\n" ; static const char frag_shadow_code[] = "!!ARBfp1.0\n" "OPTION ARB_fragment_program_shadow;\n" "TEMP shadow;\n" "TXP shadow, fragment.texcoord[0], texture[0], SHADOW2D;\n" "MUL result.color.rgb, fragment.color, shadow.x;\n" "MOV result.color.a, fragment.color.a;\n" "END\n" ; static void DrawScene(void) { GLfloat k = 6; /* sphere */ glPushMatrix(); glTranslatef(1.6, 2.2, 2.7); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Green); glColor4fv(Green); glutSolidSphere(1.5, 15, 15); glPopMatrix(); /* dodecahedron */ glPushMatrix(); glTranslatef(-2.0, 1.2, 2.1); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red); glColor4fv(Red); glutSolidDodecahedron(); glPopMatrix(); /* icosahedron */ glPushMatrix(); glTranslatef(-0.6, 1.3, -0.5); glScalef(1.5, 1.5, 1.5); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Yellow); glColor4fv(Red); glutSolidIcosahedron(); glPopMatrix(); /* a plane */ glPushMatrix(); glTranslatef(0, -1.1, 0); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue); glColor4fv(Blue); glNormal3f(0, 1, 0); glBegin(GL_POLYGON); glVertex3f(-k, 0, -k); glVertex3f( k, 0, -k); glVertex3f( k, 0, k); glVertex3f(-k, 0, k); glEnd(); glPopMatrix(); } /** * Calculate modelview and project matrices for the light * * Stores the results in \c lightProjection (projection matrix) and * \c lightModelview (modelview matrix). */ static void MakeShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3], GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar) { /* compute frustum to enclose spot light cone */ const GLfloat d = shadowNear * tan(spotAngle); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glFrustum(-d, d, -d, d, shadowNear, shadowFar); glGetFloatv(GL_PROJECTION_MATRIX, lightProjection); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluLookAt(lightPos[0], lightPos[1], lightPos[2], lightPos[0] + spotDir[0], lightPos[1] + spotDir[1], lightPos[2] + spotDir[2], 0.0, 1.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX, lightModelview); glPopMatrix(); } /** * Load \c GL_TEXTURE matrix with light's MVP matrix. */ static void SetShadowTextureMatrix(void) { static const GLfloat biasMatrix[16] = { 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0, }; glMatrixMode(GL_TEXTURE); glLoadMatrixf(biasMatrix); glTranslatef(0.0, 0.0, Bias); glMultMatrixf(lightProjection); glMultMatrixf(lightModelview); glMatrixMode(GL_MODELVIEW); } static void EnableIdentityTexgen(void) { /* texgen so that texcoord = vertex coord */ static GLfloat sPlane[4] = { 1, 0, 0, 0 }; static GLfloat tPlane[4] = { 0, 1, 0, 0 }; static GLfloat rPlane[4] = { 0, 0, 1, 0 }; static GLfloat qPlane[4] = { 0, 0, 0, 1 }; glTexGenfv(GL_S, GL_EYE_PLANE, sPlane); glTexGenfv(GL_T, GL_EYE_PLANE, tPlane); glTexGenfv(GL_R, GL_EYE_PLANE, rPlane); glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); } /* * Setup 1-D texgen so that the distance from the light source, between * the near and far planes maps to s=0 and s=1. When we draw the scene, * the grayness will indicate the fragment's distance from the light * source. */ static void EnableDistanceTexgen(const GLfloat lightPos[4], const GLfloat lightDir[3], GLfloat lightNear, GLfloat lightFar) { GLfloat m, d; GLfloat sPlane[4]; GLfloat nearPoint[3]; m = sqrt(lightDir[0] * lightDir[0] + lightDir[1] * lightDir[1] + lightDir[2] * lightDir[2]); d = lightFar - lightNear; /* nearPoint = point on light direction vector which intersects the * near plane of the light frustum. */ nearPoint[0] = lightPos[0] + lightDir[0] / m * lightNear; nearPoint[1] = lightPos[1] + lightDir[1] / m * lightNear; nearPoint[2] = lightPos[2] + lightDir[2] / m * lightNear; sPlane[0] = lightDir[0] / d / m; sPlane[1] = lightDir[1] / d / m; sPlane[2] = lightDir[2] / d / m; sPlane[3] = -(sPlane[0] * nearPoint[0] + sPlane[1] * nearPoint[1] + sPlane[2] * nearPoint[2]); glTexGenfv(GL_S, GL_EYE_PLANE, sPlane); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glEnable(GL_TEXTURE_GEN_S); } static void DisableTexgen(void) { glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_GEN_Q); } static void ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude, GLfloat pos[4], GLfloat dir[3]) { pos[0] = dist * sin(longitude * DEG_TO_RAD); pos[1] = dist * sin(latitude * DEG_TO_RAD); pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD); pos[3] = 1; dir[0] = -pos[0]; dir[1] = -pos[1]; dir[2] = -pos[2]; } /** * Render the shadow map / depth texture. * The result will be in the texture object named ShadowTexture. */ static void RenderShadowMap(void) { GLenum depthFormat; /* GL_DEPTH_COMPONENT or GL_DEPTH_STENCIL_EXT */ GLenum depthType; /* GL_UNSIGNED_INT_24_8_EXT or GL_UNSIGNED_INT */ if (WindowWidth >= 1024 && WindowHeight >= 1024) { ShadowTexWidth = ShadowTexHeight = 1024; } else if (WindowWidth >= 512 && WindowHeight >= 512) { ShadowTexWidth = ShadowTexHeight = 512; } else if (WindowWidth >= 256 && WindowHeight >= 256) { ShadowTexWidth = ShadowTexHeight = 256; } else { ShadowTexWidth = ShadowTexHeight = 128; } printf("Rendering %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight); if (UsePackedDepthStencil) { depthFormat = GL_DEPTH_STENCIL_EXT; depthType = GL_UNSIGNED_INT_24_8_EXT; } else { depthFormat = GL_DEPTH_COMPONENT; depthType = GL_UNSIGNED_INT; } glMatrixMode(GL_PROJECTION); glLoadMatrixf(lightProjection); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(lightModelview); if (UseFBO) { GLenum fbo_status; glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, ShadowTexWidth, ShadowTexHeight, 0, depthFormat, depthType, NULL); /* Set the filter mode so that the texture is texture-complete. * Otherwise it will cause the framebuffer to fail the framebuffer * completeness test. */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) { fprintf(stderr, "FBO not complete! status = 0x%04x\n", fbo_status); assert(fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT); } } assert(!glIsEnabled(GL_TEXTURE_1D)); assert(!glIsEnabled(GL_TEXTURE_2D)); glViewport(0, 0, ShadowTexWidth, ShadowTexHeight); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); DrawScene(); if (UseFBO) { /* all done! */ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } else { /* * copy depth buffer into the texture map */ if (DisplayMode == SHOW_DEPTH_MAPPING) { /* load depth image as gray-scale luminance texture */ GLuint *depth = (GLuint *) malloc(ShadowTexWidth * ShadowTexHeight * sizeof(GLuint)); assert(depth); glReadPixels(0, 0, ShadowTexWidth, ShadowTexHeight, depthFormat, depthType, depth); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, ShadowTexWidth, ShadowTexHeight, 0, GL_LUMINANCE, GL_UNSIGNED_INT, depth); free(depth); } else { /* The normal shadow case - a real depth texture */ glCopyTexImage2D(GL_TEXTURE_2D, 0, depthFormat, 0, 0, ShadowTexWidth, ShadowTexHeight, 0); if (UsePackedDepthStencil) { /* debug check */ GLint intFormat; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &intFormat); assert(intFormat == GL_DEPTH_STENCIL_EXT); } } } } /** * Show the shadow map as a grayscale image. */ static void ShowShadowMap(void) { glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, WindowWidth, 0, WindowHeight, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); DisableTexgen(); /* interpret texture's depth values as luminance values */ if (HaveShadow) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBegin(GL_POLYGON); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(1, 0); glVertex2f(ShadowTexWidth, 0); glTexCoord2f(1, 1); glVertex2f(ShadowTexWidth, ShadowTexHeight); glTexCoord2f(0, 1); glVertex2f(0, ShadowTexHeight); glEnd(); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); } /** * Redraw window image */ static void Display(void) { GLenum error; ComputeLightPos(LightDist, LightLatitude, LightLongitude, LightPos, SpotDir); if (NeedNewShadowMap) { MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar); RenderShadowMap(); NeedNewShadowMap = GL_FALSE; } glViewport(0, 0, WindowWidth, WindowHeight); if (DisplayMode == SHOW_DEPTH_IMAGE) { ShowShadowMap(); } else { /* prepare to draw scene from camera's view */ const GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -22.0); glRotatef(Xrot, 1, 0, 0); glRotatef(Yrot, 0, 1, 0); glRotatef(Zrot, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLightfv(GL_LIGHT0, GL_POSITION, LightPos); if (LinearFilter) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if (DisplayMode == SHOW_DEPTH_MAPPING) { if (HaveShadow) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(GL_TEXTURE_2D); SetShadowTextureMatrix(); EnableIdentityTexgen(); } else if (DisplayMode == SHOW_DISTANCE) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); EnableDistanceTexgen(LightPos, SpotDir, ShadowNear+Bias, ShadowFar); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(GL_TEXTURE_1D); assert(!glIsEnabled(GL_TEXTURE_2D)); } else { assert(DisplayMode == SHOW_SHADOWS); if (HaveShadow) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); } if (curr_frag > 0) { glEnable(GL_FRAGMENT_PROGRAM_ARB); } else { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } glEnable(GL_TEXTURE_2D); SetShadowTextureMatrix(); if (UseVP) { glEnable(GL_VERTEX_PROGRAM_ARB); } else { glEnable(GL_LIGHTING); EnableIdentityTexgen(); } } DrawScene(); if (UseVP) { glDisable(GL_VERTEX_PROGRAM_ARB); } else { DisableTexgen(); glDisable(GL_LIGHTING); } if (curr_frag > 0) { glDisable(GL_FRAGMENT_PROGRAM_ARB); } glDisable(GL_TEXTURE_1D); glDisable(GL_TEXTURE_2D); } glutSwapBuffers(); error = glGetError(); if (error) { printf("GL Error: %s\n", (char *) gluErrorString(error)); } } static void Reshape(int width, int height) { WindowWidth = width; WindowHeight = height; NeedNewShadowMap = GL_TRUE; } static void Idle(void) { static double t0 = -1.; double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; if (t0 < 0.0) t0 = t; dt = t - t0; t0 = t; Yrot += 75.0 * dt; /*LightLongitude -= 5.0;*/ glutPostRedisplay(); } static void Key(unsigned char key, int x, int y) { const GLfloat step = 3.0; (void) x; (void) y; switch (key) { case 'a': Anim = !Anim; if (Anim) glutIdleFunc(Idle); else glutIdleFunc(NULL); break; case 'b': Bias -= 0.01; printf("Bias %g\n", Bias); break; case 'B': Bias += 0.01; printf("Bias %g\n", Bias); break; case 'd': DisplayMode = SHOW_DISTANCE; break; case 'f': LinearFilter = !LinearFilter; printf("%s filtering\n", LinearFilter ? "Bilinear" : "Nearest"); break; case 'i': DisplayMode = SHOW_DEPTH_IMAGE; break; case 'm': DisplayMode = SHOW_DEPTH_MAPPING; break; case 'M': curr_frag = (1 + curr_frag) % max_frag; if (!HaveShadow && (curr_frag == 0)) { curr_frag = 1; } printf("Using fragment %s\n", FragProgNames[curr_frag]); if (HaveFP) { glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, frag_progs[curr_frag]); } break; case 'n': case 's': case ' ': DisplayMode = SHOW_SHADOWS; break; case 'o': if (HaveEXTshadowFuncs) { Operator++; if (Operator >= 8) Operator = 0; printf("Operator: %s\n", OperatorName[Operator]); if (HaveShadow) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, OperatorFunc[Operator]); } } break; case 'p': UsePackedDepthStencil = !UsePackedDepthStencil; if (UsePackedDepthStencil && !HavePackedDepthStencil) { printf("Sorry, GL_EXT_packed_depth_stencil not supported\n"); UsePackedDepthStencil = GL_FALSE; } else { printf("Use GL_DEPTH_STENCIL_EXT: %d\n", UsePackedDepthStencil); /* Don't really need to regenerate shadow map texture, but do so * to exercise more code more often. */ NeedNewShadowMap = GL_TRUE; } break; case 'v': UseVP = !UseVP && HaveVP; printf("Using vertex %s mode.\n", UseVP ? "program" : "fixed-function"); break; case 'z': Zrot -= step; break; case 'Z': Zrot += step; break; case 27: exit(0); break; } fflush(stdout); glutPostRedisplay(); } static void SpecialKey(int key, int x, int y) { const GLfloat step = 3.0; const int mod = glutGetModifiers(); (void) x; (void) y; switch (key) { case GLUT_KEY_UP: if (mod) LightLatitude += step; else Xrot += step; break; case GLUT_KEY_DOWN: if (mod) LightLatitude -= step; else Xrot -= step; break; case GLUT_KEY_LEFT: if (mod) LightLongitude += step; else Yrot += step; break; case GLUT_KEY_RIGHT: if (mod) LightLongitude -= step; else Yrot -= step; break; } if (mod) NeedNewShadowMap = GL_TRUE; glutPostRedisplay(); } /* A helper for finding errors in program strings */ static int FindLine( const char *program, int position ) { int i, line = 1; for (i = 0; i < position; i++) { if (program[i] == '\n') line++; } return line; } static GLuint compile_program(GLenum target, const char *code) { GLuint p; GLint errorPos; glGenProgramsARB(1, & p); glBindProgramARB(target, p); glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(code), code); glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); if (glGetError() != GL_NO_ERROR || errorPos != -1) { int l = FindLine(code, errorPos); printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l, (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); exit(0); } glBindProgramARB(target, 0); return p; } static void Init(void) { static const GLfloat borderColor[4] = {1.0, 0.0, 0.0, 0.0}; if (!glutExtensionSupported("GL_ARB_depth_texture")) { printf("Sorry, this demo requires the GL_ARB_depth_texture extension\n"); exit(1); } HaveShadow = glutExtensionSupported("GL_ARB_shadow"); HaveVP = glutExtensionSupported("GL_ARB_vertex_program"); HaveFP = glutExtensionSupported("GL_ARB_fragment_program"); HaveFP_Shadow = glutExtensionSupported("GL_ARB_fragment_program_shadow"); if (!HaveShadow && !HaveFP) { printf("Sorry, this demo requires either the GL_ARB_shadow extension " "or the GL_ARB_fragment_program extension\n"); exit(1); } printf("Using GL_ARB_depth_texture\n"); if (HaveShadow) { printf("and GL_ARB_shadow\n"); } if (HaveFP) { printf("and GL_ARB_fragment_program\n"); } HaveShadowAmbient = glutExtensionSupported("GL_ARB_shadow_ambient"); if (HaveShadowAmbient) { printf("and GL_ARB_shadow_ambient\n"); } HaveEXTshadowFuncs = glutExtensionSupported("GL_EXT_shadow_funcs"); HavePackedDepthStencil = glutExtensionSupported("GL_EXT_packed_depth_stencil"); UsePackedDepthStencil = HavePackedDepthStencil; #if defined(GL_EXT_framebuffer_object) HaveFBO = glutExtensionSupported("GL_EXT_framebuffer_object"); UseFBO = HaveFBO; if (UseFBO) { printf("Using GL_EXT_framebuffer_object\n"); } #endif /* * Set up the 2D shadow map texture */ glGenTextures(1, &ShadowTexture); glBindTexture(GL_TEXTURE_2D, ShadowTexture); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); if (HaveShadow) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); } if (HaveShadowAmbient) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 0.3); } #if defined(GL_EXT_framebuffer_object) if (UseFBO) { glGenFramebuffersEXT(1, &ShadowFBO); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ShadowTexture, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } #endif /* * Setup 1-D grayscale texture image for SHOW_DISTANCE mode */ glGenTextures(1, &GrayTexture); glBindTexture(GL_TEXTURE_1D, GrayTexture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); { GLuint i; GLubyte image[256]; for (i = 0; i < 256; i++) image[i] = i; glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE, 256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image); } if (HaveVP) { vert_prog = compile_program(GL_VERTEX_PROGRAM_ARB, vert_code); glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vert_prog); } max_frag = 1; frag_progs[0] = 0; if (HaveFP) { frag_progs[1] = compile_program(GL_FRAGMENT_PROGRAM_ARB, frag_code); max_frag = 2; } if (HaveFP && HaveFP_Shadow) { frag_progs[2] = compile_program(GL_FRAGMENT_PROGRAM_ARB, frag_shadow_code); max_frag = 3; } if (!HaveShadow) { curr_frag = 1; glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, frag_progs[curr_frag]); } glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } static void PrintHelp(void) { printf("Keys:\n"); printf(" a = toggle animation\n"); printf(" i = show depth texture image\n"); printf(" m = show depth texture mapping\n"); printf(" d = show fragment distance from light source\n"); printf(" n = show normal, shadowed image\n"); printf(" f = toggle nearest/bilinear texture filtering\n"); printf(" b/B = decrease/increase shadow map Z bias\n"); printf(" p = toggle use of packed depth/stencil\n"); printf(" M = cycle through fragment program modes\n"); printf(" v = toggle vertex program modes\n"); printf(" cursor keys = rotate scene\n"); printf(" + cursor keys = rotate light source\n"); if (HaveEXTshadowFuncs) printf(" o = cycle through comparison modes\n"); fflush(stdout); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(WindowWidth, WindowHeight); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); glutCreateWindow(argv[0]); glewInit(); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutSpecialFunc(SpecialKey); glutDisplayFunc(Display); if (Anim) glutIdleFunc(Idle); Init(); PrintHelp(); glutMainLoop(); return 0; }