X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=progs%2Fdemos%2Fshadowtex.c;h=c2d40bda74ea601d8e917737d4db40c2105048bb;hb=ab5750f1437e9a83bad8ce786dfc6f9074be7831;hp=c5251727d6779997b89ce295aa9a1634875d9e1a;hpb=3c70f6f2ac833dbbe9b6a9908e344095e499f295;p=mesa.git diff --git a/progs/demos/shadowtex.c b/progs/demos/shadowtex.c index c5251727d67..c2d40bda74e 100644 --- a/progs/demos/shadowtex.c +++ b/progs/demos/shadowtex.c @@ -1,15 +1,17 @@ -/* $Id: shadowtex.c,v 1.6 2002/03/23 16:34:18 brianp Exp $ */ - /* * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and - * GL_ARB_shadow_ambient extensions (or the old SGIX extensions). + * 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-2001 Brian Paul All Rights Reserved. + * 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"), @@ -29,20 +31,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +#define GL_GLEXT_PROTOTYPES #include #include #include +#include #include #include -#include "../util/showbuffer.c" - -#if 0 /* change to 1 if you want to use the old SGIX extensions */ -#undef GL_ARB_depth_texture -#undef GL_ARB_shadow -#undef GL_ARB_shadow_ambient -#endif - +#include "showbuffer.h" #define DEG_TO_RAD (3.14159 / 180.0) @@ -69,7 +65,37 @@ 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, @@ -80,17 +106,115 @@ static const char *OperatorName[8] = { static GLuint DisplayMode; -#define SHOW_NORMAL 0 +#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); @@ -129,27 +253,56 @@ DrawScene(void) } -/* - * Load the GL_TEXTURE matrix with the projection from the light - * source's point of view. +/** + * 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) { - GLfloat d; - - glMatrixMode(GL_TEXTURE); + /* compute frustum to enclose spot light cone */ + const GLfloat d = shadowNear * tan(spotAngle); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); glLoadIdentity(); - glTranslatef(0.5, 0.5, 0.5 + Bias); - glScalef(0.5, 0.5, 0.5); - d = shadowNear * tan(spotAngle); 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, 1, 0); + 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); } @@ -202,9 +355,9 @@ EnableDistanceTexgen(const GLfloat lightPos[4], const GLfloat lightDir[3], /* 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; + 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; @@ -244,74 +397,199 @@ ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude, } +/** + * Render the shadow map / depth texture. + * The result will be in the texture object named ShadowTexture. + */ static void -Display(void) +RenderShadowMap(void) { - GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight; - GLfloat d; - GLenum error; + 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; + } - ComputeLightPos(LightDist, LightLatitude, LightLongitude, - LightPos, SpotDir); - /* - * Step 1: render scene from point of view of the light source - */ - /* compute frustum to enclose spot light cone */ - d = ShadowNear * tan(SpotAngle); glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(-d, d, -d, d, ShadowNear, ShadowFar); + glLoadMatrixf(lightProjection); + glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt(LightPos[0], LightPos[1], LightPos[2], /* from */ - 0, 0, 0, /* target */ - 0, 1, 0); /* up */ + 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(); - /* - * Step 2: copy depth buffer into texture map - */ - if (DisplayMode == SHOW_DEPTH_MAPPING) { - /* load depth image as gray-scale luminance texture */ - GLfloat *depth = malloc(ShadowTexWidth * ShadowTexHeight - * sizeof(GLfloat)); - if (depth) { + 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, - GL_DEPTH_COMPONENT, GL_FLOAT, depth); + depthFormat, depthType, depth); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, ShadowTexWidth, ShadowTexHeight, 0, - GL_LUMINANCE, GL_FLOAT, depth); + 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); + } + } } - else { - /* The normal shadow case */ - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, - 0, 0, ShadowTexWidth, ShadowTexHeight, 0); +} + + +/** + * 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; } - /* - * Step 3: render scene from point of view of the camera - */ glViewport(0, 0, WindowWidth, WindowHeight); if (DisplayMode == SHOW_DEPTH_IMAGE) { - ShowDepthBuffer(WindowWidth, WindowHeight, 0, 1); + 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); @@ -320,15 +598,15 @@ Display(void) 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 defined(GL_ARB_shadow) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); -#elif defined(GL_SGIX_shadow) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_FALSE); -#endif + 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); - MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar); + + SetShadowTextureMatrix(); EnableIdentityTexgen(); } else if (DisplayMode == SHOW_DISTANCE) { @@ -338,23 +616,48 @@ Display(void) 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_NORMAL); -#if defined(GL_ARB_shadow) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, - GL_COMPARE_R_TO_TEXTURE_ARB); -#elif defined(GL_SGIX_shadow) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE); -#endif - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + 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); - MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar); - EnableIdentityTexgen(); + + SetShadowTextureMatrix(); + + if (UseVP) { + glEnable(GL_VERTEX_PROGRAM_ARB); + } + else { + glEnable(GL_LIGHTING); + EnableIdentityTexgen(); + } } + DrawScene(); - DisableTexgen(); - glDisable(GL_TEXTURE_1D); + + if (UseVP) { + glDisable(GL_VERTEX_PROGRAM_ARB); + } + else { + DisableTexgen(); + glDisable(GL_LIGHTING); + } + + if (curr_frag > 0) { + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } + glDisable(GL_TEXTURE_2D); } @@ -362,7 +665,7 @@ Display(void) error = glGetError(); if (error) { - printf("GL Error: %s\n", gluErrorString(error)); + printf("GL Error: %s\n", (char *) gluErrorString(error)); } } @@ -372,23 +675,20 @@ Reshape(int width, int height) { WindowWidth = width; WindowHeight = height; - if (width >= 512 && height >= 512) { - ShadowTexWidth = ShadowTexHeight = 512; - } - else if (width >= 256 && height >= 256) { - ShadowTexWidth = ShadowTexHeight = 256; - } - else { - ShadowTexWidth = ShadowTexHeight = 128; - } - printf("Using %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight); + NeedNewShadowMap = GL_TRUE; } static void Idle(void) { - Yrot += 5.0; + 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(); } @@ -429,9 +729,22 @@ Key(unsigned char key, int x, int y) 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_NORMAL; + DisplayMode = SHOW_SHADOWS; break; case 'o': if (HaveEXTshadowFuncs) { @@ -439,10 +752,31 @@ Key(unsigned char key, int x, int y) if (Operator >= 8) Operator = 0; printf("Operator: %s\n", OperatorName[Operator]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, - OperatorFunc[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; @@ -490,58 +824,139 @@ SpecialKey(int key, int x, int y) 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) { -#if defined(GL_ARB_depth_texture) && defined(GL_ARB_shadow) - if (!glutExtensionSupported("GL_ARB_depth_texture") || - !glutExtensionSupported("GL_ARB_shadow")) { - printf("Sorry, this demo requires the GL_ARB_depth_texture and GL_ARB_shadow extensions\n"); + 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); } - printf("Using GL_ARB_depth_texture and GL_ARB_shadow\n"); -#elif defined(GL_SGIX_depth_texture) && defined(GL_SGIX_shadow) - if (!glutExtensionSupported("GL_SGIX_depth_texture") || - !glutExtensionSupported("GL_SGIX_shadow")) { - printf("Sorry, this demo requires the GL_SGIX_depth_texture and GL_SGIX_shadow extensions\n"); + + 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_SGIX_depth_texture and GL_SGIX_shadow\n"); -#endif + + 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"); - 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); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + 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 defined(GL_ARB_shadow) - 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); -#elif defined(GL_SGIX_shadow) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX, - GL_TEXTURE_LEQUAL_R_SGIX); -#endif -#if defined(GL_ARB_shadow_ambient) - if (glutExtensionSupported("GL_ARB_shadow_ambient")) { + 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); - printf("and GL_ARB_shadow_ambient\n"); } -#elif defined(GL_SGIX_shadow_ambient) - if (glutExtensionSupported("GL_SGIX_shadow_ambient")) { - glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3); - printf("and GL_SGIX_shadow_ambient\n"); + +#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 */ + /* + * 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_WRAP_T, 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]; @@ -551,6 +966,30 @@ Init(void) 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); @@ -568,6 +1007,9 @@ PrintHelp(void) 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) @@ -581,7 +1023,7 @@ main(int argc, char *argv[]) glutInit(&argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(WindowWidth, WindowHeight); - glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); glutCreateWindow(argv[0]); glutReshapeFunc(Reshape); glutKeyboardFunc(Key);