diff options
author | Eric Anholt <eric@anholt.net> | 2009-07-28 14:30:10 -0700 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-07-28 21:03:43 -0700 |
commit | 05ca561ec8d2bd121bf023082a774b63e7fb0ac3 (patch) | |
tree | 17352a4d2c41858d5186aa84200d6f6197f4f2aa | |
parent | 02f26e9129b85708921a5037f247d24b7bc37574 (diff) |
Start implementing shadow texturing.
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | glass.c | 65 | ||||
-rw-r--r-- | glass.h | 27 | ||||
-rw-r--r-- | shadow.frag | 4 | ||||
-rw-r--r-- | shadow.vert | 6 | ||||
-rw-r--r-- | shadow_map.c | 142 |
6 files changed, 232 insertions, 16 deletions
diff --git a/Makefile.am b/Makefile.am index fe47eae..af1a63e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,5 +10,7 @@ glass_SOURCES = \ glass.c \ glass.h \ ground.c \ + load_texture.c \ matrix.c \ - load_texture.c + shadow_map.c + @@ -43,19 +43,11 @@ #define DEFAULT_WIDTH 600 #define DEFAULT_HEIGHT 700 -#define NUM_RINGS 27 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) static int win_width, win_height; -struct revolved_object { - GLuint vbo, array_obj; - int pos_offset, norm_offset, tangent_offset, texcoord_offset; - int elements_offset, elements_vert_stride; - int num_verts, num_steps; -}; - enum uniform_list { UNIFORM_MV, UNIFORM_MVP, @@ -82,9 +74,12 @@ struct uniform_desc ground_uniforms[GROUND_UNIFORM_MAX] = { [GROUND_UNIFORM_LIGHT_EYE] = { "light_eye" }, }; +struct uniform_desc shadow_uniforms[SHADOW_UNIFORM_MAX] = { + [SHADOW_UNIFORM_MVP] = { "mvp" }, +}; static GLuint glass_prog = 0; -GLuint ground_prog = 0; +GLuint ground_prog = 0, shadow_prog = 0; struct revolved_object ring; GLuint normalmap_tex, heightmap_tex; @@ -96,9 +91,12 @@ static const GLUvec4 z_axis = {{0.0, 0.0, 1.0, 1.0}}; GLUvec4 eye_world = {{0.0, 0.0, 2.0, 1.0}}; static GLUvec4 eye_center_world = {{0.0, 1.0, 2.0, 1.0}}; static GLUvec4 light_eye; +GLUvec4 light_world; GLUmat4 projection; GLUmat4 world_to_eye; GLUmat4 ring_obj_to_world[NUM_RINGS]; +GLUvec4 ring_bounding_sphere_center_world; +float ring_bounding_sphere_radius; static time_t start_tv_sec = 0; static float start_time, cur_time, last_fps_time = 0; @@ -138,7 +136,7 @@ update_brdf_constants(void) static void update_light_position(void) { - GLUmat4 light_eye_matrix, temp; + GLUmat4 light_eye_matrix, light_world_matrix, temp; float light_rotation_rads; /* Set the light position: Rotate around the Y axis from its original @@ -146,11 +144,13 @@ update_light_position(void) */ light_rotation_rads = cur_time * 2 * M_PI / 4; - gluRotate4v(&light_eye_matrix, &z_axis, light_rotation_rads); + gluRotate4v(&light_world_matrix, &z_axis, light_rotation_rads); + gluMult4m_4v(&light_world, &light_world_matrix, &light_start_world); + gluTranslate3(&temp, -eye_world.values[0], -eye_world.values[1], -eye_world.values[2]); - gluMult4m_4m(&light_eye_matrix, &temp, &light_eye_matrix); + gluMult4m_4m(&light_eye_matrix, &temp, &light_world_matrix); gluMult4m_4v(&light_eye, &light_eye_matrix, &light_start_world); light_eye.values[0] /= light_eye.values[3]; @@ -159,7 +159,7 @@ update_light_position(void) light_eye.values[3] = 1.0; } -static void +void do_ring_drawelements(void) { GLvoid *indices[ring.num_verts]; @@ -225,6 +225,24 @@ calc_new_ring_transforms(int instance) 10 + y_index * 5.0, 7.0 + z_index * 5.0); gluMult4m_4m(&ring_obj_to_world[instance], &temp, &obj_to_world); + + /* Calculate the bounding sphere of the set of rings, for use in + * shadow mapping. + */ + ring_bounding_sphere_center_world.values[0] = 0; + ring_bounding_sphere_center_world.values[1] = 10; + ring_bounding_sphere_center_world.values[2] = 7; + ring_bounding_sphere_center_world.values[3] = 1.0; + + /* The rings are roughly 2.5 wide from their center, and they can + * be oriented in pretty much any direction, and the farthest out + * would be the corners of the cubes (x/y/z_index +1 or -1). + * So we're just making a bounding box and doing a bounding sphere + * of it, really. + */ + ring_bounding_sphere_radius = sqrt((7.5 * 7.5) + + (12.5 * 12.5) + + (9.5 * 9.5)); } static void @@ -276,6 +294,8 @@ draw(void) for (instance = 0; instance < NUM_RINGS; instance++) calc_new_ring_transforms(instance); + generate_rings_shadowmap(); + glViewport(0, 0, win_width, win_height); draw_rings(); @@ -441,6 +461,7 @@ revolve(const float *verts, unsigned int num_verts, } } + glBindVertexArray(ring.array_obj); glBindBuffer(GL_ARRAY_BUFFER_ARB, ring.vbo); glVertexPointer(3, GL_FLOAT, 0, (void *)(uintptr_t)ring.pos_offset); glEnable(GL_VERTEX_ARRAY); @@ -457,6 +478,11 @@ revolve(const float *verts, unsigned int num_verts, glNormalPointer(GL_FLOAT, 0, (void *)(uintptr_t)ring.norm_offset); glEnable(GL_NORMAL_ARRAY); + + glBindVertexArray(ring.shadow_array_obj); + glBindBuffer(GL_ARRAY_BUFFER_ARB, ring.vbo); + glVertexPointer(3, GL_FLOAT, 0, (void *)(uintptr_t)ring.pos_offset); + glEnable(GL_VERTEX_ARRAY); } static void @@ -723,10 +749,21 @@ load_ground_program(void) } static void +load_shadow_program(void) +{ + if (shadow_prog != 0) + glDeleteProgram(shadow_prog); + + shadow_prog = load_program("shadow", shadow_uniforms, + ARRAY_SIZE(shadow_uniforms)); +} + +static void load_programs(void) { load_glass_program(); load_ground_program(); + load_shadow_program(); } static void @@ -782,7 +819,7 @@ init(void) ring.num_verts; glGenVertexArrays(1, &ring.array_obj); - glBindVertexArray(ring.array_obj); + glGenVertexArrays(1, &ring.shadow_array_obj); glGenBuffers(1, &vbo); ring.vbo = vbo; @@ -29,6 +29,13 @@ #include <GL/glew.h> #include <glu3.h> +struct revolved_object { + GLuint vbo, array_obj, shadow_array_obj; + int pos_offset, norm_offset, tangent_offset, texcoord_offset; + int elements_offset, elements_vert_stride; + int num_verts, num_steps; +}; + enum ground_uniform_list { GROUND_UNIFORM_MV, GROUND_UNIFORM_MVP, @@ -36,15 +43,30 @@ enum ground_uniform_list { GROUND_UNIFORM_MAX }; +enum shadow_uniform_list { + SHADOW_UNIFORM_MVP, + SHADOW_UNIFORM_MAX +}; + struct uniform_desc { const char *name; GLint location; }; +#define NUM_RINGS 27 + extern struct uniform_desc ground_uniforms[GROUND_UNIFORM_MAX]; -extern GLuint ground_prog; +extern struct uniform_desc shadow_uniforms[SHADOW_UNIFORM_MAX]; +extern GLuint ground_prog, shadow_prog; extern GLUmat4 projection; extern GLUmat4 world_to_eye; +extern GLUvec4 light_world; +extern GLUmat4 ring_obj_to_world[NUM_RINGS]; +extern struct revolved_object ring; +extern GLUvec4 ring_bounding_sphere_center_world; +extern float ring_bounding_sphere_radius; + +void do_ring_drawelements(void); /* matrix.c */ void mult_m4_p4(float *result, const float *mat4, const float *point4); @@ -63,3 +85,6 @@ GLuint load_texture_rgb(const char *filename); /* ground.c */ void draw_ground(void); void setup_ground(void); + +/* shadow_map.c */ +void generate_rings_shadowmap(void); diff --git a/shadow.frag b/shadow.frag new file mode 100644 index 0000000..bc700a3 --- /dev/null +++ b/shadow.frag @@ -0,0 +1,4 @@ +void main() +{ + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} diff --git a/shadow.vert b/shadow.vert new file mode 100644 index 0000000..ec5fbf8 --- /dev/null +++ b/shadow.vert @@ -0,0 +1,6 @@ +uniform mat4 mvp, mv; + +void main() +{ + gl_Position = mvp * gl_Vertex; +} diff --git a/shadow_map.c b/shadow_map.c new file mode 100644 index 0000000..290343a --- /dev/null +++ b/shadow_map.c @@ -0,0 +1,142 @@ +/* + * Copyright © 2009 Eric Anholt + * + * 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 (including the next + * paragraph) 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 + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <err.h> +#include <errno.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <getopt.h> + +#include "glass.h" + +extern int no_multi_draw_arrays; +extern GLUvec4 eye_world; +static GLUmat4 world_to_light_eye, world_to_light_ndc; +GLuint shadow_tex = 0, shadow_fbo; + +static void +calculate_light_projection(void) +{ + GLUvec4 light_up; + GLUmat4 temp; + + /* Wrong. So wrong. */ + light_up.values[0] = 1.0; + light_up.values[1] = 0.0; + light_up.values[2] = 0.0; + light_up.values[3] = 1.0; + + /* Transform for looking from they eye's point of view at the pile of + * rings. + */ + gluLookAt4v(&world_to_light_eye, + &light_world, + &ring_bounding_sphere_center_world, + &light_up); + gluPerspective4(&temp, 90, /*XXX: calculate me */ + 1.0, /* we're using a bounding sphere, w == h */ + 0.2, 40); + gluMult4m_4m(&world_to_light_ndc, &temp, &world_to_light_eye); +} + +/* The ground plane is located in world coordinates, so no need to do any + * object_to_world transformation. + */ +static void +install_shadow_transform(int instance) +{ + GLUmat4 mvp; + + gluMult4m_4m(&mvp, &world_to_light_ndc, &ring_obj_to_world[instance]); + + glUniformMatrix4fv(shadow_uniforms[SHADOW_UNIFORM_MVP].location, 1, 0, + (float *)&mvp); +} + +void +generate_rings_shadowmap(void) +{ + int instance; + int shadow_width = 256, shadow_height = 256; + + if (shadow_prog == 0) + return; + + glUseProgram(shadow_prog); + + calculate_light_projection(); + + if (shadow_tex == 0) { + GLenum status; + + glGenTextures(1, &shadow_tex); + glBindTexture(GL_TEXTURE_2D, shadow_tex); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + shadow_width, shadow_height, 0, + GL_RGBA, GL_FLOAT, NULL); + + glGenFramebuffersEXT(1, &shadow_fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, shadow_fbo); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, + shadow_tex, + 0); + status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + fprintf(stderr, "shadow map FBO complete: 0x%08x\n", + status); + } + + glDisable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, shadow_fbo); + glViewport(0, 0, shadow_width, shadow_height); + + /* Clear to the material color. */ + glClearColor(1.0, 0.7, 0.5, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + /* Draw the rings in black. */ + glBindVertexArray(ring.shadow_array_obj); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, ring.vbo); + for (instance = 0; instance < NUM_RINGS; instance++) { + install_shadow_transform(instance); + do_ring_drawelements(); + } + + glEnable(GL_DEPTH_TEST); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); +} |