-/* $Id: shadowtex.c,v 1.1 2001/02/20 16:43:50 brianp Exp $ */
-
/*
- * Shadow demo using the GL_SGIX_depth_texture, GL_SGIX_shadow and
- * GL_SGIX_shadow_ambient extensions.
+ * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and
+ * GL_ARB_shadow_ambient extensions.
*
* Brian Paul
* 19 Feb 2001
*
- * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
+ * 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"),
* 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 "../util/showbuffer.c"
-
+#include "showbuffer.h"
#define DEG_TO_RAD (3.14159 / 180.0)
static GLfloat ShadowNear = 4.0, ShadowFar = 24.0;
static GLint ShadowTexWidth = 256, ShadowTexHeight = 256;
-static GLboolean ShowDepth = GL_FALSE;
-
-static GLfloat ShadowImage[256*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);
}
-/*
- * 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
-ShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
- GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
+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);
}
static void
-EnableTexgen(void)
+EnableIdentityTexgen(void)
{
/* texgen so that texcoord = vertex coord */
static GLfloat sPlane[4] = { 1, 0, 0, 0 };
}
+/*
+ * 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)
{
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);
}
+/**
+ * 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 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
- */
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
- 0, 0, ShadowTexWidth, ShadowTexHeight, 0);
+ 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();
- /*
- * Step 3: render scene from point of view of the camera
- */
- glViewport(0, 0, WindowWidth, WindowHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
- glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
+ glOrtho(0, WindowWidth, 0, WindowHeight, -1, 1);
+
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);
- if (ShowDepth) {
- ShowDepthBuffer(WindowWidth, WindowHeight, 1, 0);
+
+ 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);
- glEnable(GL_TEXTURE_2D);
- ShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
- EnableTexgen();
+
+ 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();
- DisableTexgen();
+
+ 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));
+ }
}
{
WindowWidth = width;
WindowHeight = height;
+ 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();
}
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':
- ShowDepth = !ShowDepth;
+ 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;
}
glutPostRedisplay();
- printf("Bias %g\n", Bias);
}
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 (!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");
+ 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);
}
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ 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);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX,
- GL_TEXTURE_LEQUAL_R_SGIX);
- if (glutExtensionSupported("GL_SGIX_shadow_ambient"))
- glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3);
+ 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);
{
printf("Keys:\n");
printf(" a = toggle animation\n");
- printf(" d = toggle display of depth texture\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");
}
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);