From bd764cff0bd3ee3def85317ec4e84cd2d941824d Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Fri, 19 Oct 2012 06:19:19 -0700 Subject: SceneShadow: Add the skeleton (stubs for derived scene members only) of a new shadow mapping scene. When complete, the scene will render geometry into a depth texture from the point of view of the light source for the scene, then render the "ground" onto which a shadow of the object would be projected (the depth values from the texture will be used for a distance-from-light comparison with the actual object), and finally render the actual object, which appears to be casting a shadow. --- src/android.cpp | 1 + src/default-benchmarks.h | 1 + src/main.cpp | 1 + src/scene-shadow.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ src/scene.h | 18 +++++++++ 5 files changed, 118 insertions(+) create mode 100644 src/scene-shadow.cpp diff --git a/src/android.cpp b/src/android.cpp index d9678f4..53a17f7 100644 --- a/src/android.cpp +++ b/src/android.cpp @@ -275,6 +275,7 @@ create_and_add_scenes(std::vector& scenes, Canvas& canvas) scenes.push_back(new SceneIdeas(canvas)); scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); + scenes.push_back(new SceneShadow(canvas)); } diff --git a/src/default-benchmarks.h b/src/default-benchmarks.h index d121e30..45f7672 100644 --- a/src/default-benchmarks.h +++ b/src/default-benchmarks.h @@ -63,6 +63,7 @@ private: benchmarks.push_back("ideas:speed=duration"); benchmarks.push_back("jellyfish"); benchmarks.push_back("terrain"); + benchmarks.push_back("shadow"); benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=0"); benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=5"); benchmarks.push_back("conditionals:vertex-steps=5:fragment-steps=0"); diff --git a/src/main.cpp b/src/main.cpp index 36cdf7b..d310a51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,6 +63,7 @@ add_and_register_scenes(vector& scenes, Canvas& canvas) scenes.push_back(new SceneIdeas(canvas)); scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); + scenes.push_back(new SceneShadow(canvas)); for (vector::const_iterator iter = scenes.begin(); iter != scenes.end(); diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp new file mode 100644 index 0000000..f7c303c --- /dev/null +++ b/src/scene-shadow.cpp @@ -0,0 +1,97 @@ +// +// Copyright © 2012 Linaro Limited +// +// This file is part of the glmark2 OpenGL (ES) 2.0 benchmark. +// +// glmark2 is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// glmark2. If not, see . +// +// Authors: +// Jesse Barker +// +#include "scene.h" +#include "util.h" + +class ShadowPrivate +{ +}; + +SceneShadow::SceneShadow(Canvas& canvas) : + Scene(canvas, "shadow"), + priv_(0) +{ +} + +SceneShadow::~SceneShadow() +{ + delete priv_; +} + +bool +SceneShadow::supported(bool show_errors) +{ + return show_errors; +} + +bool +SceneShadow::load() +{ + running_ = false; + return true; +} + +void +SceneShadow::unload() +{ +} + +bool +SceneShadow::setup() +{ + if (!Scene::setup()) + return false; + + priv_ = new ShadowPrivate(); + // Set core scene timing after actual initialization so we don't measure + // set up time. + startTime_ = Util::get_timestamp_us() / 1000000.0; + lastUpdateTime_ = startTime_; + running_ = true; + + return true; +} + +void +SceneShadow::teardown() +{ + // Add scene-specific teardown here + Scene::teardown(); +} + +void +SceneShadow::update() +{ + Scene::update(); + // Add scene-specific update here +} + +void +SceneShadow::draw() +{ +} + +Scene::ValidationResult +SceneShadow::validate() +{ + return Scene::ValidationUnknown; +} diff --git a/src/scene.h b/src/scene.h index a455fbd..dd77ba9 100644 --- a/src/scene.h +++ b/src/scene.h @@ -556,4 +556,22 @@ public: void draw(); ValidationResult validate(); }; + +class ShadowPrivate; +class SceneShadow : public Scene +{ + ShadowPrivate* priv_; +public: + SceneShadow(Canvas& canvas); + ~SceneShadow(); + bool supported(bool show_errors); + bool load(); + void unload(); + bool setup(); + void teardown(); + void update(); + void draw(); + ValidationResult validate(); +}; + #endif -- cgit v1.2.3 From 149d52c460b32ca2d93cb66a8bf8dd2939deca06 Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Fri, 19 Oct 2012 07:41:02 -0700 Subject: SceneShadow: Hook up the supported() member properly. Make setup() depend upon it as well, as the current main-loop logic only calls supported if the scene isn't marked as running (typically only happens if setup fails). --- src/scene-shadow.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp index f7c303c..18f2cb5 100644 --- a/src/scene-shadow.cpp +++ b/src/scene-shadow.cpp @@ -21,6 +21,9 @@ // #include "scene.h" #include "util.h" +#include "log.h" + +using std::string; class ShadowPrivate { @@ -40,7 +43,19 @@ SceneShadow::~SceneShadow() bool SceneShadow::supported(bool show_errors) { - return show_errors; + static const string oes_depth_texture("GL_OES_depth_texture"); + static const string arb_depth_texture("GL_ARB_depth_texture"); + if (!GLExtensions::support(oes_depth_texture) && + !GLExtensions::support(arb_depth_texture)) + { + if (show_errors) + { + Log::error("We do not have the depth texture extension!!!\n"); + } + + return false; + } + return true; } bool @@ -58,8 +73,11 @@ SceneShadow::unload() bool SceneShadow::setup() { - if (!Scene::setup()) + // If the scene isn't supported, don't bother to go through setup. + if (!supported(false) || !Scene::setup()) + { return false; + } priv_ = new ShadowPrivate(); // Set core scene timing after actual initialization so we don't measure -- cgit v1.2.3 From 7c261a377a5567cfaadb802fe297c2c10047201c Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Mon, 22 Oct 2012 09:17:26 -0700 Subject: SceneShadow: Add sufficient setup, update, and draw code to get the "normal" view of the horse going. --- src/scene-shadow.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 5 deletions(-) diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp index 18f2cb5..975f842 100644 --- a/src/scene-shadow.cpp +++ b/src/scene-shadow.cpp @@ -20,19 +20,172 @@ // Jesse Barker // #include "scene.h" +#include "model.h" #include "util.h" #include "log.h" +#include "shader-source.h" +#include "stack.h" using std::string; +using std::vector; +using std::map; +using LibMatrix::Stack4; +using LibMatrix::mat4; +using LibMatrix::vec4; +using LibMatrix::vec3; + +static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f); class ShadowPrivate { + Canvas& canvas_; + Program program_; + Stack4 modelview_; + Stack4 projection_; + Mesh mesh_; + vec3 centerVec_; + float radius_; + float rotation_; + float rotationSpeed_; + bool useVbo_; + +public: + ShadowPrivate(Canvas& canvas) : + canvas_(canvas), + radius_(0.0), + rotation_(0.0), + rotationSpeed_(36.0), + useVbo_(true) {} + ~ShadowPrivate() {} + + bool setup(map& options); + void teardown(); + void update(double elapsedTime); + void draw(); }; +bool +ShadowPrivate::setup(map& options) +{ + // Program object setup + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.frag"); + static const vec4 materialDiffuse(1.0f, 1.0f, 1.0f, 1.0f); + + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + vtx_source.add_const("LightSourcePosition", lightPosition); + vtx_source.add_const("MaterialDiffuse", materialDiffuse); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + + // Model setup + Model::find_models(); + Model model; + bool modelLoaded = model.load("horse"); + + if(!modelLoaded) { + return false; + } + + model.calculate_normals(); + + // Mesh setup + vector > attribs; + attribs.push_back(std::pair(Model::AttribTypePosition, 3)); + attribs.push_back(std::pair(Model::AttribTypeNormal, 3)); + model.convert_to_mesh(mesh_, attribs); + + useVbo_ = (options["use-vbo"].value == "true"); + bool interleave = (options["interleave"].value == "true"); + mesh_.vbo_update_method(Mesh::VBOUpdateMethodMap); + mesh_.interleave(interleave); + + if (useVbo_) { + mesh_.build_vbo(); + } + else { + mesh_.build_array(); + } + + // Calculate a projection matrix that is a good fit for the model + vec3 maxVec = model.maxVec(); + vec3 minVec = model.minVec(); + vec3 diffVec = maxVec - minVec; + centerVec_ = maxVec + minVec; + centerVec_ /= 2.0; + float diameter = diffVec.length(); + radius_ = diameter / 2; + float fovy = 2.0 * atanf(radius_ / (2.0 + radius_)); + fovy /= M_PI; + fovy *= 180.0; + float aspect(static_cast(canvas_.width())/static_cast(canvas_.height())); + projection_.perspective(fovy, aspect, 2.0, 2.0 + diameter); + + return true; +} + + +void +ShadowPrivate::teardown() +{ + program_.stop(); + program_.release(); + mesh_.reset(); +} + +void +ShadowPrivate::update(double elapsedTime) +{ + rotation_ = rotationSpeed_ * elapsedTime; +} + +void +ShadowPrivate::draw() +{ + // Draw the "normal" view of the horse + modelview_.push(); + modelview_.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.0 + radius_)); + modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f); + mat4 mvp(projection_.getCurrent()); + mvp *= modelview_.getCurrent(); + + program_.start(); + program_["ModelViewProjectionMatrix"] = mvp; + + // Load the NormalMatrix uniform in the shader. The NormalMatrix is the + // inverse transpose of the model view matrix. + LibMatrix::mat4 normal_matrix(modelview_.getCurrent()); + normal_matrix.inverse().transpose(); + program_["NormalMatrix"] = normal_matrix; + vector attrib_locations; + attrib_locations.push_back(program_["position"].location()); + attrib_locations.push_back(program_["normal"].location()); + mesh_.set_attrib_locations(attrib_locations); + if (useVbo_) { + mesh_.render_vbo(); + } + else { + mesh_.render_array(); + } + + // Per-frame cleanup + modelview_.pop(); +} + SceneShadow::SceneShadow(Canvas& canvas) : Scene(canvas, "shadow"), priv_(0) { + options_["use-vbo"] = Scene::Option("use-vbo", "true", + "Whether to use VBOs for rendering", + "false,true"); + options_["interleave"] = Scene::Option("interleave", "false", + "Whether to interleave vertex attribute data", + "false,true"); } SceneShadow::~SceneShadow() @@ -46,10 +199,8 @@ SceneShadow::supported(bool show_errors) static const string oes_depth_texture("GL_OES_depth_texture"); static const string arb_depth_texture("GL_ARB_depth_texture"); if (!GLExtensions::support(oes_depth_texture) && - !GLExtensions::support(arb_depth_texture)) - { - if (show_errors) - { + !GLExtensions::support(arb_depth_texture)) { + if (show_errors) { Log::error("We do not have the depth texture extension!!!\n"); } @@ -79,7 +230,13 @@ SceneShadow::setup() return false; } - priv_ = new ShadowPrivate(); + priv_ = new ShadowPrivate(canvas_); + if (!priv_->setup(options_)) { + delete priv_; + priv_ = 0; + return false; + } + // Set core scene timing after actual initialization so we don't measure // set up time. startTime_ = Util::get_timestamp_us() / 1000000.0; @@ -93,6 +250,7 @@ void SceneShadow::teardown() { // Add scene-specific teardown here + priv_->teardown(); Scene::teardown(); } @@ -101,11 +259,13 @@ SceneShadow::update() { Scene::update(); // Add scene-specific update here + priv_->update(lastUpdateTime_ - startTime_); } void SceneShadow::draw() { + priv_->draw(); } Scene::ValidationResult -- cgit v1.2.3 From 4abe76b5de3f8346bf4d03e2c842aa0c181e5408 Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Mon, 22 Oct 2012 09:55:55 -0700 Subject: SceneShadow: Add an object to manage the render target for the depth pass from which we'll later generate the horse's shadow when we render the ground. Notice the pass-through varying for the normal. This is a bit of a workaround for the set up of the mesh, which will attempt to enable vertex attribs for both position and normals (some shader compilers will optimize out an unused attribute, which means the attribute location is -1 and the mesh code will generate an error). --- data/shaders/depth.frag | 6 ++ data/shaders/depth.vert | 12 ++++ src/scene-shadow.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 data/shaders/depth.frag create mode 100644 data/shaders/depth.vert diff --git a/data/shaders/depth.frag b/data/shaders/depth.frag new file mode 100644 index 0000000..e2a5256 --- /dev/null +++ b/data/shaders/depth.frag @@ -0,0 +1,6 @@ +varying vec3 Normal; + +void main() +{ + gl_FragColor = vec4(Normal, 1.0); +} diff --git a/data/shaders/depth.vert b/data/shaders/depth.vert new file mode 100644 index 0000000..f424032 --- /dev/null +++ b/data/shaders/depth.vert @@ -0,0 +1,12 @@ +attribute vec3 position; +attribute vec3 normal; + +uniform mat4 ModelViewProjectionMatrix; + +varying vec3 Normal; + +void main(void) +{ + Normal = normal; + gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0); +} diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp index 975f842..cf2423c 100644 --- a/src/scene-shadow.cpp +++ b/src/scene-shadow.cpp @@ -36,9 +36,120 @@ using LibMatrix::vec3; static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f); +// +// To create a shadow map, we need a framebuffer object set up for a +// depth-only pass. The render target can then be bound as a texture, +// and the depth values sampled from that texture can be used in the +// distance-from-light computations when rendering the shadow on the +// ground below the rendered object. +// +class DepthRenderTarget +{ + Program program_; + unsigned int canvas_width_; + unsigned int canvas_height_; + unsigned int width_; + unsigned int height_; + unsigned int tex_; + unsigned int fbo_; +public: + DepthRenderTarget() : + canvas_width_(0), + canvas_height_(0), + width_(0), + height_(0), + tex_(0), + fbo_(0) {} + ~DepthRenderTarget() {} + bool setup(unsigned int width, unsigned int height); + void teardown(); + void enable(const mat4& mvp); + void disable(); + unsigned int depthTexture() { return tex_; } + Program& program() { return program_; } +}; + +bool +DepthRenderTarget::setup(unsigned int width, unsigned int height) +{ + canvas_width_ = width; + canvas_height_ = height; + width_ = canvas_width_ * 2; + height_ = canvas_height_ * 2; + + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/depth.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/depth.frag"); + + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + + glGenTextures(1, &tex_); + glBindTexture(GL_TEXTURE_2D, tex_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_, height_, 0, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + tex_, 0); + unsigned int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + Log::error("DepthRenderState::setup: glCheckFramebufferStatus failed (0x%x)\n", status); + return false; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return true; +} + +void +DepthRenderTarget::teardown() +{ + program_.stop(); + program_.release(); + if (tex_) { + glDeleteTextures(1, &tex_); + tex_ = 0; + } + if (fbo_) { + glDeleteFramebuffers(1, &fbo_); + fbo_ = 0; + } +} + +void +DepthRenderTarget::enable(const mat4& mvp) +{ + program_.start(); + program_["ModelViewProjectionMatrix"] = mvp; + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + tex_, 0); + glViewport(0, 0, width_, height_); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glClear(GL_DEPTH_BUFFER_BIT); +} + +void DepthRenderTarget::disable() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, canvas_width_, canvas_height_); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + class ShadowPrivate { Canvas& canvas_; + DepthRenderTarget depthTarget_; Program program_; Stack4 modelview_; Stack4 projection_; @@ -125,6 +236,11 @@ ShadowPrivate::setup(map& options) float aspect(static_cast(canvas_.width())/static_cast(canvas_.height())); projection_.perspective(fovy, aspect, 2.0, 2.0 + diameter); + if (!depthTarget_.setup(canvas_.width(), canvas_.height())) { + Log::error("Failed to set up the render target for the depth pass\n"); + return false; + } + return true; } @@ -132,6 +248,7 @@ ShadowPrivate::setup(map& options) void ShadowPrivate::teardown() { + depthTarget_.teardown(); program_.stop(); program_.release(); mesh_.reset(); @@ -146,11 +263,40 @@ ShadowPrivate::update(double elapsedTime) void ShadowPrivate::draw() { + // To perform the depth pass, set up the model-view transformation so + // that we're looking at the horse from the light position. That will + // give us the appropriate view for the shadow. + modelview_.push(); + modelview_.loadIdentity(); + modelview_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(), + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f); + mat4 mvp(projection_.getCurrent()); + mvp *= modelview_.getCurrent(); + modelview_.pop(); + + // Enable the depth render target with our transformation and render. + depthTarget_.enable(mvp); + vector attrib_locations; + attrib_locations.push_back(depthTarget_.program()["position"].location()); + attrib_locations.push_back(depthTarget_.program()["normal"].location()); + mesh_.set_attrib_locations(attrib_locations); + if (useVbo_) { + mesh_.render_vbo(); + } + else { + mesh_.render_array(); + } + depthTarget_.disable(); + + // TODO: Ground rendering using the above generated texture... + // Draw the "normal" view of the horse modelview_.push(); modelview_.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.0 + radius_)); modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f); - mat4 mvp(projection_.getCurrent()); + mvp = projection_.getCurrent(); mvp *= modelview_.getCurrent(); program_.start(); @@ -161,7 +307,7 @@ ShadowPrivate::draw() LibMatrix::mat4 normal_matrix(modelview_.getCurrent()); normal_matrix.inverse().transpose(); program_["NormalMatrix"] = normal_matrix; - vector attrib_locations; + attrib_locations.clear(); attrib_locations.push_back(program_["position"].location()); attrib_locations.push_back(program_["normal"].location()); mesh_.set_attrib_locations(attrib_locations); -- cgit v1.2.3 From c46d126bc97e839f135419d6ea4aaade3732ab97 Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Mon, 22 Oct 2012 10:01:50 -0700 Subject: Mesh: Fix for a situation encountered during the development of SceneShadow. In cases where a mesh will do "double duty" but not be used the same way in all cases (in SceneShadow, the shader for the depth pass would ignore the "normal" attribute), the GL can return a -1 for the attribute location, even if it is declared in the shader (compilers are free to optimize away unused attributes, etc.). So, we check for this and don't try to enable attributes whose location is -1. --- src/mesh.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mesh.cpp b/src/mesh.cpp index bcc36e6..9ccbcc6 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -543,6 +543,8 @@ void Mesh::render_array() { for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glEnableVertexAttribArray(attrib_locations_[i]); glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first, GL_FLOAT, GL_FALSE, vertex_stride_, @@ -552,6 +554,8 @@ Mesh::render_array() glDrawArrays(GL_TRIANGLES, 0, vertices_.size()); for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glDisableVertexAttribArray(attrib_locations_[i]); } } @@ -566,6 +570,8 @@ void Mesh::render_vbo() { for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glEnableVertexAttribArray(attrib_locations_[i]); glBindBuffer(GL_ARRAY_BUFFER, vbos_[i]); glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first, @@ -576,6 +582,8 @@ Mesh::render_vbo() glDrawArrays(GL_TRIANGLES, 0, vertices_.size()); for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glDisableVertexAttribArray(attrib_locations_[i]); } } -- cgit v1.2.3 From f039dbaa723236bfdd7022543bb8e4e5e9a10811 Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Mon, 22 Oct 2012 14:30:36 -0700 Subject: SceneShadow: Finally add in the ground with the shadow cast upon it. --- data/shaders/shadow.frag | 17 ++++++ data/shaders/shadow.vert | 16 ++++++ src/scene-shadow.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 data/shaders/shadow.frag create mode 100644 data/shaders/shadow.vert diff --git a/data/shaders/shadow.frag b/data/shaders/shadow.frag new file mode 100644 index 0000000..6b86ace --- /dev/null +++ b/data/shaders/shadow.frag @@ -0,0 +1,17 @@ +uniform sampler2D ShadowMap; + +varying vec4 Color; +varying vec4 ShadowCoord; + +void main() +{ + vec4 sc_perspective = ShadowCoord / ShadowCoord.w; + sc_perspective.z += 0.1505; + vec4 shadow_value = texture2D(ShadowMap, sc_perspective.st); + float light_distance = shadow_value.z; + float shadow = 1.0; + if (ShadowCoord.w > 0.0 && light_distance < sc_perspective.z) { + shadow = 0.5; + } + gl_FragColor = vec4(shadow * Color.rgb, 1.0); +} diff --git a/data/shaders/shadow.vert b/data/shaders/shadow.vert new file mode 100644 index 0000000..a8ba2b4 --- /dev/null +++ b/data/shaders/shadow.vert @@ -0,0 +1,16 @@ +attribute vec2 position; + +uniform mat4 LightMatrix; +uniform mat4 ModelViewProjectionMatrix; + +varying vec4 ShadowCoord; +varying vec4 Color; + +void main() +{ + Color = MaterialDiffuse; + + vec4 pos4 = vec4(position, 0.0, 1.0); + ShadowCoord = LightMatrix * pos4; + gl_Position = ModelViewProjectionMatrix * pos4; +} diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp index cf2423c..77f3bc0 100644 --- a/src/scene-shadow.cpp +++ b/src/scene-shadow.cpp @@ -33,6 +33,7 @@ using LibMatrix::Stack4; using LibMatrix::mat4; using LibMatrix::vec4; using LibMatrix::vec3; +using LibMatrix::vec2; static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f); @@ -65,7 +66,7 @@ public: void teardown(); void enable(const mat4& mvp); void disable(); - unsigned int depthTexture() { return tex_; } + unsigned int texture() { return tex_; } Program& program() { return program_; } }; @@ -146,10 +147,130 @@ void DepthRenderTarget::disable() glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } +// +// The actual shadow pass is really just a quad projected into the scene +// with the horse's shadow cast upon it. In the vertex stage, we compute +// a texture coordinate for the depth texture look-up by transforming the +// current vertex position using a matrix describing the light's view point. +// In the fragment stage, that coordinate is perspective corrected, and +// used to sample the depth texture. If the depth value for that fragment +// (effectively the distance from the light to the object at that point) +// is less than the Z component of that coordinate (effectively the distance +// from the light to the ground at that point) then that location is in shadow. +// +class GroundRenderer +{ + Program program_; + Stack4 light_; + Stack4 modelview_; + mat4 projection_; + int positionLocation_; + unsigned int bufferObject_; + unsigned int texture_; + vector vertices_; + vector texcoords_; + +public: + GroundRenderer() : + positionLocation_(0), + bufferObject_(0) {} + ~GroundRenderer() {} + bool setup(const mat4& projection, unsigned int texture); + void teardown(); + void draw(); +}; + +bool +GroundRenderer::setup(const mat4& projection, unsigned int texture) +{ + projection_ = projection; + texture_ = texture; + + // Program set up + static const vec4 materialDiffuse(0.3f, 0.3f, 0.3f, 1.0f); + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/shadow.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/shadow.frag"); + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + vtx_source.add_const("MaterialDiffuse", materialDiffuse); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + positionLocation_ = program_["position"].location(); + + // Set up the position data for our "quad". + vertices_.push_back(vec2(-1.0, -1.0)); + vertices_.push_back(vec2(1.0, -1.0)); + vertices_.push_back(vec2(-1.0, 1.0)); + vertices_.push_back(vec2(1.0, 1.0)); + + // Set up the VBO and stash our position data in it. + glGenBuffers(1, &bufferObject_); + glBindBuffer(GL_ARRAY_BUFFER, bufferObject_); + glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(vec2), + &vertices_.front(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // Initialize the light stack with a bias matrix that will convert + // values in the range of [-1, 1] to [0, 1)]. + light_.translate(0.5, 0.5, 0.5); + light_.scale(0.5, 0.5, 0.5); + light_ *= projection_; + light_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(), + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + + return true; +} + +void +GroundRenderer::teardown() +{ + program_.stop(); + program_.release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &bufferObject_); + bufferObject_ = 0; + texture_= 0; + vertices_.clear(); +} + +void +GroundRenderer::draw() +{ + // Need to add uniforms for the shadow texture, and transformation to + // "lay the quad down". + modelview_.push(); + modelview_.translate(projection_[0][3], projection_[1][3] - 0.8, projection_[2][3] - 0.5); + modelview_.rotate(-85.0, 1.0, 0.0, 0.0); + modelview_.scale(2.0, 2.0, 2.0); + mat4 mvp(projection_); + mvp *= modelview_.getCurrent(); + + glBindBuffer(GL_ARRAY_BUFFER, bufferObject_); + program_.start(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + program_["ShadowMap"] = 0; + program_["LightMatrix"] = light_.getCurrent(); + program_["ModelViewProjectionMatrix"] = mvp; + + glEnableVertexAttribArray(positionLocation_); + glVertexAttribPointer(positionLocation_, 2, GL_FLOAT, GL_FALSE, 0, 0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisableVertexAttribArray(positionLocation_); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + modelview_.pop(); +} + class ShadowPrivate { Canvas& canvas_; DepthRenderTarget depthTarget_; + GroundRenderer ground_; Program program_; Stack4 modelview_; Stack4 projection_; @@ -234,13 +355,18 @@ ShadowPrivate::setup(map& options) fovy /= M_PI; fovy *= 180.0; float aspect(static_cast(canvas_.width())/static_cast(canvas_.height())); - projection_.perspective(fovy, aspect, 2.0, 2.0 + diameter); + projection_.perspective(fovy, aspect, 2.0, 50.0); if (!depthTarget_.setup(canvas_.width(), canvas_.height())) { Log::error("Failed to set up the render target for the depth pass\n"); return false; } + if (!ground_.setup(projection_.getCurrent(), depthTarget_.texture())) { + Log::error("Failed to set up the ground renderer\n"); + return false; + } + return true; } @@ -249,6 +375,7 @@ void ShadowPrivate::teardown() { depthTarget_.teardown(); + ground_.teardown(); program_.stop(); program_.release(); mesh_.reset(); @@ -290,7 +417,8 @@ ShadowPrivate::draw() } depthTarget_.disable(); - // TODO: Ground rendering using the above generated texture... + // Ground rendering using the above generated texture... + ground_.draw(); // Draw the "normal" view of the horse modelview_.push(); -- cgit v1.2.3 From 3981d164f058eee239af611c4d96956fc69d32da Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Mon, 22 Oct 2012 14:43:11 -0700 Subject: SceneShadow: We don't really need a matrix stack for the light transform, so just use a simple matrix. --- src/scene-shadow.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp index 77f3bc0..4fd27c7 100644 --- a/src/scene-shadow.cpp +++ b/src/scene-shadow.cpp @@ -161,7 +161,7 @@ void DepthRenderTarget::disable() class GroundRenderer { Program program_; - Stack4 light_; + mat4 light_; Stack4 modelview_; mat4 projection_; int positionLocation_; @@ -213,14 +213,15 @@ GroundRenderer::setup(const mat4& projection, unsigned int texture) &vertices_.front(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); - // Initialize the light stack with a bias matrix that will convert - // values in the range of [-1, 1] to [0, 1)]. - light_.translate(0.5, 0.5, 0.5); - light_.scale(0.5, 0.5, 0.5); + // Set up the light matrix with a bias that will convert values + // in the range of [-1, 1] to [0, 1)], then add in the projection + // and the "look at" matrix from the light position. + light_ *= LibMatrix::Mat4::translate(0.5, 0.5, 0.5); + light_ *= LibMatrix::Mat4::scale(0.5, 0.5, 0.5); light_ *= projection_; - light_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(), - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0); + light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(), + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); return true; } @@ -254,7 +255,7 @@ GroundRenderer::draw() glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_); program_["ShadowMap"] = 0; - program_["LightMatrix"] = light_.getCurrent(); + program_["LightMatrix"] = light_; program_["ModelViewProjectionMatrix"] = mvp; glEnableVertexAttribArray(positionLocation_); -- cgit v1.2.3