aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Barker <jesse.barker@linaro.org>2012-10-22 09:55:55 -0700
committerJesse Barker <jesse.barker@linaro.org>2012-10-22 09:55:55 -0700
commit4abe76b5de3f8346bf4d03e2c842aa0c181e5408 (patch)
tree95b81dbac113035b9994c8a2b87b7b66e20c7553
parent7c261a377a5567cfaadb802fe297c2c10047201c (diff)
downloadglmark2-4abe76b5de3f8346bf4d03e2c842aa0c181e5408.tar.gz
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).
-rw-r--r--data/shaders/depth.frag6
-rw-r--r--data/shaders/depth.vert12
-rw-r--r--src/scene-shadow.cpp150
3 files changed, 166 insertions, 2 deletions
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<string, Scene::Option>& options)
float aspect(static_cast<float>(canvas_.width())/static_cast<float>(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<string, Scene::Option>& 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<GLint> 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<GLint> 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);