summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2009-07-28 14:30:10 -0700
committerEric Anholt <eric@anholt.net>2009-07-28 21:03:43 -0700
commit05ca561ec8d2bd121bf023082a774b63e7fb0ac3 (patch)
tree17352a4d2c41858d5186aa84200d6f6197f4f2aa
parent02f26e9129b85708921a5037f247d24b7bc37574 (diff)
Start implementing shadow texturing.
-rw-r--r--Makefile.am4
-rw-r--r--glass.c65
-rw-r--r--glass.h27
-rw-r--r--shadow.frag4
-rw-r--r--shadow.vert6
-rw-r--r--shadow_map.c142
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
+
diff --git a/glass.c b/glass.c
index 543ab14..41c8f83 100644
--- a/glass.c
+++ b/glass.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;
diff --git a/glass.h b/glass.h
index 524fb40..1a7c75f 100644
--- a/glass.h
+++ b/glass.h
@@ -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);
+}