1039 lines
29 KiB
C
1039 lines
29 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.
|
|
* 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;
|
|
}
|