blob: 112ee0dad08bda270e31cf3ec3ff27ed2e72b833 [file] [log] [blame]
Brian Paul0221e4d2001-02-28 18:41:50 +00001/* $Id: shadowtex.c,v 1.4 2001/02/28 18:41:50 brianp Exp $ */
Brian Paul1399d762001-02-20 16:43:50 +00002
3/*
4 * Shadow demo using the GL_SGIX_depth_texture, GL_SGIX_shadow and
5 * GL_SGIX_shadow_ambient extensions.
6 *
7 * Brian Paul
8 * 19 Feb 2001
9 *
10 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30
Brian Paul0221e4d2001-02-28 18:41:50 +000031#include <assert.h>
Brian Paul1399d762001-02-20 16:43:50 +000032#include <stdio.h>
33#include <stdlib.h>
34#include <math.h>
35#include <GL/glut.h>
36#include "../util/showbuffer.c"
37
38
39#define DEG_TO_RAD (3.14159 / 180.0)
40
41static GLint WindowWidth = 450, WindowHeight = 300;
42static GLfloat Xrot = 15, Yrot = 0, Zrot = 0;
43
44static GLfloat Red[4] = {1, 0, 0, 1};
45static GLfloat Green[4] = {0, 1, 0, 1};
46static GLfloat Blue[4] = {0, 0, 1, 1};
47static GLfloat Yellow[4] = {1, 1, 0, 1};
48
49static GLfloat LightDist = 10;
50static GLfloat LightLatitude = 45.0;
51static GLfloat LightLongitude = 45.0;
52static GLfloat LightPos[4];
53static GLfloat SpotDir[3];
54static GLfloat SpotAngle = 40.0 * DEG_TO_RAD;
55static GLfloat ShadowNear = 4.0, ShadowFar = 24.0;
56static GLint ShadowTexWidth = 256, ShadowTexHeight = 256;
57
Brian Paul511378b2001-02-26 18:26:32 +000058static GLboolean LinearFilter = GL_FALSE;
Brian Paul1399d762001-02-20 16:43:50 +000059
Brian Paul1399d762001-02-20 16:43:50 +000060static GLfloat Bias = -0.06;
61
62static GLboolean Anim = GL_TRUE;
63
Brian Paul0221e4d2001-02-28 18:41:50 +000064static GLuint DisplayMode;
65#define SHOW_NORMAL 0
66#define SHOW_DEPTH_IMAGE 1
67#define SHOW_DEPTH_MAPPING 2
68#define SHOW_DISTANCE 3
69
70
Brian Paul1399d762001-02-20 16:43:50 +000071
72static void
73DrawScene(void)
74{
75 GLfloat k = 6;
76 /* sphere */
77 glPushMatrix();
78 glTranslatef(1.6, 2.2, 2.7);
79 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Green);
80 glColor4fv(Green);
81 glutSolidSphere(1.5, 15, 15);
82 glPopMatrix();
83 /* dodecahedron */
84 glPushMatrix();
85 glTranslatef(-2.0, 1.2, 2.1);
86 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red);
87 glColor4fv(Red);
88 glutSolidDodecahedron();
89 glPopMatrix();
90 /* icosahedron */
91 glPushMatrix();
92 glTranslatef(-0.6, 1.3, -0.5);
93 glScalef(1.5, 1.5, 1.5);
94 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Yellow);
95 glColor4fv(Red);
96 glutSolidIcosahedron();
97 glPopMatrix();
98 /* a plane */
99 glPushMatrix();
100 glTranslatef(0, -1.1, 0);
101 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue);
102 glColor4fv(Blue);
103 glNormal3f(0, 1, 0);
104 glBegin(GL_POLYGON);
105 glVertex3f(-k, 0, -k);
106 glVertex3f( k, 0, -k);
107 glVertex3f( k, 0, k);
108 glVertex3f(-k, 0, k);
109 glEnd();
110 glPopMatrix();
111}
112
113
114/*
115 * Load the GL_TEXTURE matrix with the projection from the light
116 * source's point of view.
117 */
118static void
Brian Paul0221e4d2001-02-28 18:41:50 +0000119MakeShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
120 GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
Brian Paul1399d762001-02-20 16:43:50 +0000121{
122 GLfloat d;
123
124 glMatrixMode(GL_TEXTURE);
125 glLoadIdentity();
126 glTranslatef(0.5, 0.5, 0.5 + Bias);
127 glScalef(0.5, 0.5, 0.5);
128 d = shadowNear * tan(spotAngle);
129 glFrustum(-d, d, -d, d, shadowNear, shadowFar);
130 gluLookAt(lightPos[0], lightPos[1], lightPos[2],
131 lightPos[0] + spotDir[0],
132 lightPos[1] + spotDir[1],
133 lightPos[2] + spotDir[2],
134 0, 1, 0);
135 glMatrixMode(GL_MODELVIEW);
136}
137
138
139static void
Brian Paul0221e4d2001-02-28 18:41:50 +0000140EnableIdentityTexgen(void)
Brian Paul1399d762001-02-20 16:43:50 +0000141{
142 /* texgen so that texcoord = vertex coord */
143 static GLfloat sPlane[4] = { 1, 0, 0, 0 };
144 static GLfloat tPlane[4] = { 0, 1, 0, 0 };
145 static GLfloat rPlane[4] = { 0, 0, 1, 0 };
146 static GLfloat qPlane[4] = { 0, 0, 0, 1 };
147
148 glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
149 glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
150 glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
151 glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane);
152 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
153 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
154 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
155 glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
156
157 glEnable(GL_TEXTURE_GEN_S);
158 glEnable(GL_TEXTURE_GEN_T);
159 glEnable(GL_TEXTURE_GEN_R);
160 glEnable(GL_TEXTURE_GEN_Q);
161}
162
163
Brian Paul0221e4d2001-02-28 18:41:50 +0000164/*
165 * Setup 1-D texgen so that the distance from the light source, between
166 * the near and far planes maps to s=0 and s=1. When we draw the scene,
167 * the grayness will indicate the fragment's distance from the light
168 * source.
169 */
170static void
171EnableDistanceTexgen(const GLfloat lightPos[4], const GLfloat lightDir[3],
172 GLfloat lightNear, GLfloat lightFar)
173{
174 GLfloat m, d;
175 GLfloat sPlane[4];
176 GLfloat nearPoint[3];
177
178 m = sqrt(lightDir[0] * lightDir[0] +
179 lightDir[1] * lightDir[1] +
180 lightDir[2] * lightDir[2]);
181
182 d = lightFar - lightNear;
183
184 /* nearPoint = point on light direction vector which intersects the
185 * near plane of the light frustum.
186 */
187 nearPoint[0] = LightPos[0] + lightDir[0] / m * lightNear;
188 nearPoint[1] = LightPos[1] + lightDir[1] / m * lightNear;
189 nearPoint[2] = LightPos[2] + lightDir[2] / m * lightNear;
190
191 sPlane[0] = lightDir[0] / d / m;
192 sPlane[1] = lightDir[1] / d / m;
193 sPlane[2] = lightDir[2] / d / m;
194 sPlane[3] = -(sPlane[0] * nearPoint[0]
195 + sPlane[1] * nearPoint[1]
196 + sPlane[2] * nearPoint[2]);
197
198 glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
199 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
200 glEnable(GL_TEXTURE_GEN_S);
201}
202
203
Brian Paul1399d762001-02-20 16:43:50 +0000204static void
205DisableTexgen(void)
206{
207 glDisable(GL_TEXTURE_GEN_S);
208 glDisable(GL_TEXTURE_GEN_T);
209 glDisable(GL_TEXTURE_GEN_R);
210 glDisable(GL_TEXTURE_GEN_Q);
211}
212
213
214static void
215ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude,
216 GLfloat pos[4], GLfloat dir[3])
217{
Brian Paul0221e4d2001-02-28 18:41:50 +0000218
Brian Paul1399d762001-02-20 16:43:50 +0000219 pos[0] = dist * sin(longitude * DEG_TO_RAD);
220 pos[1] = dist * sin(latitude * DEG_TO_RAD);
221 pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD);
222 pos[3] = 1;
223 dir[0] = -pos[0];
224 dir[1] = -pos[1];
225 dir[2] = -pos[2];
226}
227
228
229static void
230Display(void)
231{
232 GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
233 GLfloat d;
234
235 ComputeLightPos(LightDist, LightLatitude, LightLongitude,
236 LightPos, SpotDir);
237 /*
238 * Step 1: render scene from point of view of the light source
239 */
240 /* compute frustum to enclose spot light cone */
241 d = ShadowNear * tan(SpotAngle);
242 glMatrixMode(GL_PROJECTION);
243 glLoadIdentity();
244 glFrustum(-d, d, -d, d, ShadowNear, ShadowFar);
245 glMatrixMode(GL_MODELVIEW);
246 glLoadIdentity();
247 gluLookAt(LightPos[0], LightPos[1], LightPos[2], /* from */
248 0, 0, 0, /* target */
249 0, 1, 0); /* up */
250
251 glViewport(0, 0, ShadowTexWidth, ShadowTexHeight);
252 glClear(GL_DEPTH_BUFFER_BIT);
253 DrawScene();
254
255 /*
256 * Step 2: copy depth buffer into texture map
257 */
Brian Paul0221e4d2001-02-28 18:41:50 +0000258 if (DisplayMode == SHOW_DEPTH_MAPPING) {
259 /* load depth image as gray-scale luminance texture */
260 GLfloat *depth = malloc(ShadowTexWidth * ShadowTexHeight
261 * sizeof(GLfloat));
262 if (depth) {
263 glReadPixels(0, 0, ShadowTexWidth, ShadowTexHeight,
264 GL_DEPTH_COMPONENT, GL_FLOAT, depth);
265 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
266 ShadowTexWidth, ShadowTexHeight, 0,
267 GL_LUMINANCE, GL_FLOAT, depth);
268 free(depth);
269 }
270 }
271 else {
272 /* The normal shadow case */
273 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
274 0, 0, ShadowTexWidth, ShadowTexHeight, 0);
275 }
Brian Paul1399d762001-02-20 16:43:50 +0000276
277 /*
278 * Step 3: render scene from point of view of the camera
279 */
280 glViewport(0, 0, WindowWidth, WindowHeight);
Brian Paul0221e4d2001-02-28 18:41:50 +0000281 if (DisplayMode == SHOW_DEPTH_IMAGE) {
282 ShowDepthBuffer(WindowWidth, WindowHeight, 0, 1);
Brian Paul1399d762001-02-20 16:43:50 +0000283 }
284 else {
Brian Paul511378b2001-02-26 18:26:32 +0000285 glMatrixMode(GL_PROJECTION);
286 glLoadIdentity();
287 glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
288 glMatrixMode(GL_MODELVIEW);
289 glLoadIdentity();
290 glTranslatef(0.0, 0.0, -22.0);
291 glRotatef(Xrot, 1, 0, 0);
292 glRotatef(Yrot, 0, 1, 0);
293 glRotatef(Zrot, 0, 0, 1);
Brian Paul1399d762001-02-20 16:43:50 +0000294 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
295 glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
Brian Paul511378b2001-02-26 18:26:32 +0000296 if (LinearFilter) {
297 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
299 }
300 else {
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
303 }
Brian Paul0221e4d2001-02-28 18:41:50 +0000304 if (DisplayMode == SHOW_DEPTH_MAPPING) {
305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_FALSE);
306 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
307 glEnable(GL_TEXTURE_2D);
308 MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
309 EnableIdentityTexgen();
310 }
311 else if (DisplayMode == SHOW_DISTANCE) {
312 glMatrixMode(GL_TEXTURE);
313 glLoadIdentity();
314 glMatrixMode(GL_MODELVIEW);
315 EnableDistanceTexgen(LightPos, SpotDir, ShadowNear+Bias, ShadowFar);
316 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
317 glEnable(GL_TEXTURE_1D);
318 }
319 else {
320 assert(DisplayMode == SHOW_NORMAL);
321 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE);
322 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
323 glEnable(GL_TEXTURE_2D);
324 MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
325 EnableIdentityTexgen();
326 }
Brian Paul1399d762001-02-20 16:43:50 +0000327 DrawScene();
328 DisableTexgen();
Brian Paul0221e4d2001-02-28 18:41:50 +0000329 glDisable(GL_TEXTURE_1D);
Brian Paul1399d762001-02-20 16:43:50 +0000330 glDisable(GL_TEXTURE_2D);
331 }
332
333 glutSwapBuffers();
334}
335
336
337static void
338Reshape(int width, int height)
339{
340 WindowWidth = width;
341 WindowHeight = height;
Brian Paul511378b2001-02-26 18:26:32 +0000342 if (width >= 512 && height >= 512) {
343 ShadowTexWidth = ShadowTexHeight = 512;
344 }
345 else if (width >= 256 && height >= 256) {
346 ShadowTexWidth = ShadowTexHeight = 256;
347 }
348 else {
349 ShadowTexWidth = ShadowTexHeight = 128;
350 }
351 printf("Using %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight);
Brian Paul1399d762001-02-20 16:43:50 +0000352}
353
354
355static void
356Idle(void)
357{
358 Yrot += 5.0;
359 /*LightLongitude -= 5.0;*/
360 glutPostRedisplay();
361}
362
363
364static void
365Key(unsigned char key, int x, int y)
366{
367 const GLfloat step = 3.0;
368 (void) x;
369 (void) y;
370 switch (key) {
371 case 'a':
372 Anim = !Anim;
373 if (Anim)
374 glutIdleFunc(Idle);
375 else
376 glutIdleFunc(NULL);
377 break;
378 case 'b':
379 Bias -= 0.01;
Brian Paul511378b2001-02-26 18:26:32 +0000380 printf("Bias %g\n", Bias);
Brian Paul1399d762001-02-20 16:43:50 +0000381 break;
382 case 'B':
383 Bias += 0.01;
Brian Paul511378b2001-02-26 18:26:32 +0000384 printf("Bias %g\n", Bias);
Brian Paul1399d762001-02-20 16:43:50 +0000385 break;
386 case 'd':
Brian Paul0221e4d2001-02-28 18:41:50 +0000387 DisplayMode = SHOW_DISTANCE;
Brian Paul1399d762001-02-20 16:43:50 +0000388 break;
Brian Paul511378b2001-02-26 18:26:32 +0000389 case 'f':
390 LinearFilter = !LinearFilter;
391 printf("%s filtering\n", LinearFilter ? "Bilinear" : "Nearest");
392 break;
Brian Paul0221e4d2001-02-28 18:41:50 +0000393 case 'i':
394 DisplayMode = SHOW_DEPTH_IMAGE;
395 break;
396 case 'm':
397 DisplayMode = SHOW_DEPTH_MAPPING;
398 break;
399 case 'n':
400 case ' ':
401 DisplayMode = SHOW_NORMAL;
402 break;
Brian Paul1399d762001-02-20 16:43:50 +0000403 case 'z':
404 Zrot -= step;
405 break;
406 case 'Z':
407 Zrot += step;
408 break;
409 case 27:
410 exit(0);
411 break;
412 }
413 glutPostRedisplay();
Brian Paul1399d762001-02-20 16:43:50 +0000414}
415
416
417static void
418SpecialKey(int key, int x, int y)
419{
420 const GLfloat step = 3.0;
421 const int mod = glutGetModifiers();
422 (void) x;
423 (void) y;
424 switch (key) {
425 case GLUT_KEY_UP:
426 if (mod)
427 LightLatitude += step;
428 else
429 Xrot += step;
430 break;
431 case GLUT_KEY_DOWN:
432 if (mod)
433 LightLatitude -= step;
434 else
435 Xrot -= step;
436 break;
437 case GLUT_KEY_LEFT:
438 if (mod)
439 LightLongitude += step;
440 else
441 Yrot += step;
442 break;
443 case GLUT_KEY_RIGHT:
444 if (mod)
445 LightLongitude -= step;
446 else
447 Yrot -= step;
448 break;
449 }
450 glutPostRedisplay();
451}
452
453
454static void
455Init(void)
456{
457 if (!glutExtensionSupported("GL_SGIX_depth_texture") ||
458 !glutExtensionSupported("GL_SGIX_shadow")) {
459 printf("Sorry, this demo requires the GL_SGIX_depth_texture and GL_SGIX_shadow extensions\n");
460 exit(1);
461 }
462
Brian Paul0221e4d2001-02-28 18:41:50 +0000463 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
464 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
465 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
466 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
Brian Paul1399d762001-02-20 16:43:50 +0000467 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
468 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
Brian Paul0a0595f2001-02-20 17:06:35 +0000469#ifdef GL_SGIX_shadow
Brian Paul1399d762001-02-20 16:43:50 +0000470 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX,
471 GL_TEXTURE_LEQUAL_R_SGIX);
Brian Paul0a0595f2001-02-20 17:06:35 +0000472#endif
473#ifdef GL_SGIX_shadow_ambient
Brian Paul1399d762001-02-20 16:43:50 +0000474 if (glutExtensionSupported("GL_SGIX_shadow_ambient"))
475 glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3);
Brian Paul0a0595f2001-02-20 17:06:35 +0000476#endif
Brian Paul1399d762001-02-20 16:43:50 +0000477
Brian Paul0221e4d2001-02-28 18:41:50 +0000478 /* setup 1-D grayscale texture image for SHOW_DISTANCE mode */
479 {
480 GLuint i;
481 GLubyte image[256];
482 for (i = 0; i < 256; i++)
483 image[i] = i;
484 glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE,
485 256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
486 }
487
Brian Paul1399d762001-02-20 16:43:50 +0000488 glEnable(GL_DEPTH_TEST);
489 glEnable(GL_LIGHTING);
490 glEnable(GL_LIGHT0);
491}
492
493
494static void
495PrintHelp(void)
496{
497 printf("Keys:\n");
498 printf(" a = toggle animation\n");
Brian Paul0221e4d2001-02-28 18:41:50 +0000499 printf(" i = show depth texture image\n");
500 printf(" m = show depth texture mapping\n");
501 printf(" d = show fragment distance from light source\n");
502 printf(" n = show normal, shadowed image\n");
Brian Paul511378b2001-02-26 18:26:32 +0000503 printf(" f = toggle nearest/bilinear texture filtering\n");
Brian Paul1399d762001-02-20 16:43:50 +0000504 printf(" b/B = decrease/increase shadow map Z bias\n");
505 printf(" cursor keys = rotate scene\n");
506 printf(" <shift> + cursor keys = rotate light source\n");
507}
508
509
510int
511main(int argc, char *argv[])
512{
513 glutInit(&argc, argv);
514 glutInitWindowPosition(0, 0);
515 glutInitWindowSize(WindowWidth, WindowHeight);
516 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
517 glutCreateWindow(argv[0]);
518 glutReshapeFunc(Reshape);
519 glutKeyboardFunc(Key);
520 glutSpecialFunc(SpecialKey);
521 glutDisplayFunc(Display);
522 if (Anim)
523 glutIdleFunc(Idle);
524 Init();
525 PrintHelp();
526 glutMainLoop();
527 return 0;
528}