
Explicitly store the modelview matrix and projection matrix used when the shadow map is created. These matrices only need be updated when RenderShadowMap is called. Previously, slightly different code was used to draw and project the shadow map.
791 lines
22 KiB
C
791 lines
22 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* 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 <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 GLboolean HaveFBO = GL_FALSE;
|
|
static GLboolean UseFBO = 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
|
|
|
|
|
|
|
|
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 */
|
|
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) {
|
|
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);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
|
|
GL_COMPARE_R_TO_TEXTURE_ARB);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
SetShadowTextureMatrix();
|
|
EnableIdentityTexgen();
|
|
}
|
|
|
|
DrawScene();
|
|
|
|
DisableTexgen();
|
|
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 'n':
|
|
case 's':
|
|
case ' ':
|
|
DisplayMode = SHOW_SHADOWS;
|
|
break;
|
|
case 'o':
|
|
if (HaveEXTshadowFuncs) {
|
|
Operator++;
|
|
if (Operator >= 8)
|
|
Operator = 0;
|
|
printf("Operator: %s\n", OperatorName[Operator]);
|
|
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 '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();
|
|
}
|
|
|
|
|
|
static void
|
|
Init(void)
|
|
{
|
|
static const GLfloat borderColor[4] = {1.0, 0.0, 0.0, 0.0};
|
|
|
|
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");
|
|
exit(1);
|
|
}
|
|
printf("Using GL_ARB_depth_texture and GL_ARB_shadow\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_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_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];
|
|
for (i = 0; i < 256; i++)
|
|
image[i] = i;
|
|
glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE,
|
|
256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
|
|
}
|
|
|
|
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(" 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;
|
|
}
|