Teach //examples/shadows to draw a soft shadow
After this CL, this example actually draws a soft shadow. There are still a
number of shortcuts that we'll need to unwind in order to produce a fully
working system, but this CL is at least a start.
R=jeffbrown@google.com, kulakowski@chromium.org
Review URL: https://codereview.chromium.org/1810793002 .
diff --git a/examples/shadows/BUILD.gn b/examples/shadows/BUILD.gn
index f5477da..8e40db4 100644
--- a/examples/shadows/BUILD.gn
+++ b/examples/shadows/BUILD.gn
@@ -9,6 +9,8 @@
sources = [
"main.cc",
+ "penumbra_program.cc",
+ "penumbra_program.h",
"shadows_app.cc",
"shadows_app.h",
"shadows_renderer.cc",
diff --git a/examples/shadows/penumbra_program.cc b/examples/shadows/penumbra_program.cc
new file mode 100644
index 0000000..8e551f1
--- /dev/null
+++ b/examples/shadows/penumbra_program.cc
@@ -0,0 +1,184 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/shadows/penumbra_program.h"
+
+#include <memory>
+#include <GLES2/gl2.h>
+#include <glm/gtc/type_ptr.hpp>
+
+#include "base/logging.h"
+
+namespace examples {
+namespace {
+
+const char kVertexShaderSource[] = R"GLSL(
+uniform mat4 u_transform;
+
+attribute vec3 a_position;
+attribute vec3 a_occluder0;
+attribute vec3 a_occluder1;
+attribute vec3 a_occluder2;
+attribute vec3 a_occluder3;
+
+varying vec3 v_occluder0;
+varying vec3 v_occluder1;
+varying vec3 v_occluder2;
+varying vec3 v_occluder3;
+
+void main() {
+ gl_Position = u_transform * vec4(a_position, 1.0);
+
+ vec4 h_occluder0 = u_transform * vec4(a_occluder0, 1.0);
+ vec4 h_occluder1 = u_transform * vec4(a_occluder1, 1.0);
+ vec4 h_occluder2 = u_transform * vec4(a_occluder2, 1.0);
+ vec4 h_occluder3 = u_transform * vec4(a_occluder3, 1.0);
+
+ v_occluder0 = h_occluder0.xyz / h_occluder0.w;
+ v_occluder1 = h_occluder1.xyz / h_occluder1.w;
+ v_occluder2 = h_occluder2.xyz / h_occluder2.w;
+ v_occluder3 = h_occluder3.xyz / h_occluder3.w;
+}
+)GLSL";
+
+const char kFragmentShaderSource[] = R"GLSL(
+precision highp float;
+
+varying highp vec3 v_occluder0;
+varying highp vec3 v_occluder1;
+varying highp vec3 v_occluder2;
+varying highp vec3 v_occluder3;
+uniform vec2 u_inverse_size;
+
+const highp vec4 zero4 = vec4(0.0, 0.0, 0.0, 0.0);
+
+// TODO(abarth): Currently the light is located and sized in normalized device
+// coordinates, which are viewport-relative. We need to instead specify the
+// light in the world coordinate system and transform it by u_transform into
+// normalized device coordinates.
+const highp float light_z = 0.2;
+const highp float light_min_x = 0.0;
+const highp float light_min_y = 0.0;
+const highp float light_max_x = 1.0;
+const highp float light_max_y = 1.0;
+
+highp float getMin(vec4 value) {
+ return min(min(value.x, value.y), min(value.z, value.w));
+}
+
+highp float getMax(vec4 value) {
+ return max(max(value.x, value.y), max(value.z, value.w));
+}
+
+void main(void) {
+ // Convert gl_FragCoord from window coordiates to [0:1]^2 for texture lookup.
+ highp vec2 depth_coord = gl_FragCoord.xy * u_inverse_size;
+
+ // Depth is in the range [0:1].
+ // TODO(abarth): Fetch the depth from a depth texture.
+ highp float depth = 0.3;
+
+ // Convert from [0:1]^3 to [-1:1]^3 (i.e., normalized device coordinates).
+ highp vec3 position = vec3(depth_coord, depth) * 2.0 - 1.0;
+
+ highp float light_distance = light_z - position.z;
+
+ highp vec3 ray0 = v_occluder0 - position;
+ highp vec3 ray1 = v_occluder1 - position;
+ highp vec3 ray2 = v_occluder2 - position;
+ highp vec3 ray3 = v_occluder3 - position;
+
+ highp float scale0 = light_distance / ray0.z;
+ highp float scale1 = light_distance / ray1.z;
+ highp float scale2 = light_distance / ray2.z;
+ highp float scale3 = light_distance / ray3.z;
+
+ highp vec4 horizontal = clamp(vec4(scale0 * ray0.x,
+ scale1 * ray1.x,
+ scale2 * ray2.x,
+ scale3 * ray3.x),
+ light_min_x, light_max_x);
+ highp vec4 vertical = clamp(vec4(scale0 * ray0.y,
+ scale1 * ray1.y,
+ scale2 * ray2.y,
+ scale3 * ray3.y),
+ light_min_y, light_max_y);
+
+ lowp float occulded = (getMax(horizontal) - getMin(horizontal)) *
+ (getMax(vertical) - getMin(vertical));
+
+ lowp float illuminated = 1.0 - occulded;
+
+ gl_FragColor = vec4(illuminated, illuminated, illuminated, 1.0);
+}
+)GLSL";
+
+GLvoid* GetOffset(int n) {
+ return reinterpret_cast<GLvoid*>(sizeof(GLfloat) * n);
+}
+
+GLint AddVertexAttribPointer(GLint index, GLint attribute, GLint count) {
+ glVertexAttribPointer(attribute, count, GL_FLOAT, GL_FALSE,
+ sizeof(PenumbraProgram::Vertex), GetOffset(index));
+ return index + count;
+}
+
+} // namespace
+
+PenumbraProgram::Vertex::Vertex() = default;
+
+PenumbraProgram::Vertex::Vertex(glm::vec3 point,
+ glm::vec3 occluder0,
+ glm::vec3 occluder1,
+ glm::vec3 occluder2,
+ glm::vec3 occluder3)
+ : point(std::move(point)),
+ occluder0(std::move(occluder0)),
+ occluder1(std::move(occluder1)),
+ occluder2(std::move(occluder2)),
+ occluder3(std::move(occluder3)) {
+}
+
+PenumbraProgram::PenumbraProgram()
+ : vertex_shader_(GL_VERTEX_SHADER, kVertexShaderSource),
+ fragment_shader_(GL_FRAGMENT_SHADER, kFragmentShaderSource),
+ program_(&vertex_shader_, &fragment_shader_),
+ u_transform_(glGetUniformLocation(program_.id(), "u_transform")),
+ u_inverse_size_(glGetUniformLocation(program_.id(), "u_inverse_size")),
+ a_position_(glGetAttribLocation(program_.id(), "a_position")),
+ a_occluder0_(glGetAttribLocation(program_.id(), "a_occluder0")),
+ a_occluder1_(glGetAttribLocation(program_.id(), "a_occluder1")),
+ a_occluder2_(glGetAttribLocation(program_.id(), "a_occluder2")),
+ a_occluder3_(glGetAttribLocation(program_.id(), "a_occluder3")) {
+ CHECK(u_transform_ != -1);
+ CHECK(u_inverse_size_ != -1);
+ CHECK(a_position_ != -1);
+ CHECK(a_occluder0_ != -1);
+ CHECK(a_occluder1_ != -1);
+ CHECK(a_occluder2_ != -1);
+ CHECK(a_occluder3_ != -1);
+ glEnableVertexAttribArray(a_position_);
+ glEnableVertexAttribArray(a_occluder0_);
+ glEnableVertexAttribArray(a_occluder1_);
+ glEnableVertexAttribArray(a_occluder2_);
+ glEnableVertexAttribArray(a_occluder3_);
+}
+
+PenumbraProgram::~PenumbraProgram() {
+}
+
+void PenumbraProgram::Use(const glm::mat4& transform,
+ const glm::vec2& inverse_size) {
+ glUseProgram(program_.id());
+ glUniformMatrix4fv(u_transform_, 1, GL_FALSE, glm::value_ptr(transform));
+ glUniform2f(u_inverse_size_, inverse_size.x, inverse_size.y);
+ GLint i = 0;
+ i = AddVertexAttribPointer(i, a_position_, 3);
+ i = AddVertexAttribPointer(i, a_occluder0_, 3);
+ i = AddVertexAttribPointer(i, a_occluder1_, 3);
+ i = AddVertexAttribPointer(i, a_occluder2_, 3);
+ AddVertexAttribPointer(i, a_occluder3_, 3);
+}
+
+} // namespace examples
diff --git a/examples/shadows/penumbra_program.h b/examples/shadows/penumbra_program.h
new file mode 100644
index 0000000..08e6e95
--- /dev/null
+++ b/examples/shadows/penumbra_program.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXAMPLES_SHADOWS_PENUMBRA_PROGRAM_H_
+#define EXAMPLES_SHADOWS_PENUMBRA_PROGRAM_H_
+
+#include <glm/glm.hpp>
+
+#include "base/macros.h"
+#include "examples/shadows/vfx/program.h"
+#include "examples/shadows/vfx/shader.h"
+
+namespace examples {
+
+class PenumbraProgram {
+ public:
+ PenumbraProgram();
+ ~PenumbraProgram();
+
+ struct Vertex {
+ Vertex();
+ Vertex(glm::vec3 point,
+ glm::vec3 occluder0,
+ glm::vec3 occluder1,
+ glm::vec3 occluder2,
+ glm::vec3 occluder3);
+
+ glm::vec3 point;
+ glm::vec3 occluder0;
+ glm::vec3 occluder1;
+ glm::vec3 occluder2;
+ glm::vec3 occluder3;
+ };
+
+ GLuint id() const { return program_.id(); }
+
+ void Use(const glm::mat4& transform, const glm::vec2& inverse_size);
+
+ private:
+ vfx::Shader vertex_shader_;
+ vfx::Shader fragment_shader_;
+ vfx::Program program_;
+
+ GLint u_transform_;
+ GLint u_inverse_size_;
+ GLint a_position_;
+ GLint a_occluder0_;
+ GLint a_occluder1_;
+ GLint a_occluder2_;
+ GLint a_occluder3_;
+
+ DISALLOW_COPY_AND_ASSIGN(PenumbraProgram);
+};
+
+} // namespace examples
+
+#endif // EXAMPLES_SHADOWS_PENUMBRA_PROGRAM_H_
diff --git a/examples/shadows/shadows_renderer.cc b/examples/shadows/shadows_renderer.cc
index 1161ffe..0974738 100644
--- a/examples/shadows/shadows_renderer.cc
+++ b/examples/shadows/shadows_renderer.cc
@@ -19,7 +19,26 @@
{vec3( 2.f, 2.f, -2.f), green},
{vec3(-2.f, 2.f, -2.f), green},
{vec3(-2.f, -2.f, -2.f), green});
+ vec4 white(1.f, 1.f, 1.f, 1.f);
+ shapes_.AddQuad({vec3( 20.f, -20.f, -5.f), white},
+ {vec3( 20.f, 20.f, -5.f), white},
+ {vec3(-20.f, 20.f, -5.f), white},
+ {vec3(-20.f, -20.f, -5.f), white});
shapes_.BufferData(GL_STATIC_DRAW);
+
+ vec3 occluder0( 2.f, -2.f, -2.f);
+ vec3 occluder1( 2.f, 2.f, -2.f);
+ vec3 occluder2(-2.f, 2.f, -2.f);
+ vec3 occluder3(-2.f, -2.f, -2.f);
+ penumbra_.AddQuad({vec3( 20.f, -20.f, -5.f), occluder0, occluder1,
+ occluder2, occluder3},
+ {vec3( 20.f, 20.f, -5.f), occluder0, occluder1,
+ occluder2, occluder3},
+ {vec3(-20.f, 20.f, -5.f), occluder0, occluder1,
+ occluder2, occluder3},
+ {vec3(-20.f, -20.f, -5.f), occluder0, occluder1,
+ occluder2, occluder3});
+ penumbra_.BufferData(GL_STATIC_DRAW);
}
ShadowsRenderer::~ShadowsRenderer() {}
@@ -27,15 +46,17 @@
void ShadowsRenderer::Render(const mojo::Size& size) {
glViewport(0, 0, size.width, size.height);
glClearColor(0.f, 0.f, 1.f, 1.f);
- glClear(GL_COLOR_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_DEPTH_TEST);
float aspect =
static_cast<GLfloat>(size.width) / static_cast<GLfloat>(size.height);
mat4 mvp = glm::perspective(60.f, aspect, 1.f, 20.f);
+ vec2 inverse_size = vec2(1.f / size.width, 1.f / size.height);
- shapes_.Bind();
- color_program_.Use(mvp);
- shapes_.Draw();
+ penumbra_.Bind();
+ penumbra_program_.Use(mvp, inverse_size);
+ penumbra_.Draw();
}
} // namespace examples
diff --git a/examples/shadows/shadows_renderer.h b/examples/shadows/shadows_renderer.h
index d9cffd6..7afc89d 100644
--- a/examples/shadows/shadows_renderer.h
+++ b/examples/shadows/shadows_renderer.h
@@ -6,6 +6,7 @@
#define EXAMPLES_SHADOWS_SHADOWS_RENDERER_H_
#include "base/macros.h"
+#include "examples/shadows/penumbra_program.h"
#include "examples/shadows/vfx/color_program.h"
#include "examples/shadows/vfx/element_array_buffer.h"
#include "mojo/services/geometry/interfaces/geometry.mojom.h"
@@ -21,7 +22,9 @@
private:
vfx::ColorProgram color_program_;
+ PenumbraProgram penumbra_program_;
vfx::ElementArrayBuffer<vfx::ColorProgram::Vertex> shapes_;
+ vfx::ElementArrayBuffer<PenumbraProgram::Vertex> penumbra_;
DISALLOW_COPY_AND_ASSIGN(ShadowsRenderer);
};