| /* |
| * 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. |
| */ |
| |
| #define GL_GLEXT_PROTOTYPES |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #include <GL/glut.h> |
| #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; |
| } |
| 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(" <shift> + cursor keys = rotate light source\n"); |
| if (HaveEXTshadowFuncs) |
| printf(" o = cycle through comparison modes\n"); |
| } |
| |
| |
| 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]); |
| glutReshapeFunc(Reshape); |
| glutKeyboardFunc(Key); |
| glutSpecialFunc(SpecialKey); |
| glutDisplayFunc(Display); |
| if (Anim) |
| glutIdleFunc(Idle); |
| Init(); |
| PrintHelp(); |
| glutMainLoop(); |
| return 0; |
| } |