From b00c4a2ee5648d25402684302122dabfffa318bf Mon Sep 17 00:00:00 2001 From: Jesse Barker Date: Fri, 7 Dec 2012 11:57:44 -0800 Subject: SceneRefract: First cut at a new scene that will demonstrate the refraction of light through a transparent object. --- data/shaders/light-refract.frag | 24 +++ data/shaders/light-refract.vert | 23 +++ src/android.cpp | 1 + src/default-benchmarks.h | 1 + src/main.cpp | 1 + src/scene-refract.cpp | 436 ++++++++++++++++++++++++++++++++++++++++ src/scene-refract.h | 97 +++++++++ src/scene.h | 17 ++ 8 files changed, 600 insertions(+) create mode 100644 data/shaders/light-refract.frag create mode 100644 data/shaders/light-refract.vert create mode 100644 src/scene-refract.cpp create mode 100644 src/scene-refract.h diff --git a/data/shaders/light-refract.frag b/data/shaders/light-refract.frag new file mode 100644 index 0000000..f7ae395 --- /dev/null +++ b/data/shaders/light-refract.frag @@ -0,0 +1,24 @@ +uniform sampler2D DistanceMap; + +varying vec3 vertex_normal; +varying vec4 vertex_position; + +void main() +{ + const vec4 lightAmbient = vec4(0.1, 0.1, 0.1, 1.0); + const vec4 lightSpecular = vec4(0.8, 0.8, 0.8, 1.0); + const vec4 matAmbient = vec4(0.2, 0.2, 0.2, 1.0); + const vec4 matSpecular = vec4(1.0, 1.0, 1.0, 1.0); + const float matShininess = 100.0; + vec3 eye_direction = normalize(-vertex_position.xyz); + vec3 light_direction = normalize(LightSourcePosition.xyz/LightSourcePosition.w - + vertex_position.xyz/vertex_position.w); + vec3 normalized_normal = normalize(vertex_normal); + vec3 reflection = reflect(-light_direction, normalized_normal); + float specularTerm = pow(max(0.0, dot(reflection, eye_direction)), matShininess); + float diffuseTerm = max(0.0, dot(normalized_normal, light_direction)); + vec4 specular = (lightSpecular * matSpecular); + vec4 ambient = (lightAmbient * matAmbient); + vec4 diffuse = (LightColor * MaterialDiffuse); + gl_FragColor = (specular * specularTerm) + ambient + (diffuse * diffuseTerm); +} diff --git a/data/shaders/light-refract.vert b/data/shaders/light-refract.vert new file mode 100644 index 0000000..8a95ca7 --- /dev/null +++ b/data/shaders/light-refract.vert @@ -0,0 +1,23 @@ +attribute vec3 position; +attribute vec3 normal; + +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 NormalMatrix; +uniform mat4 ModelViewMatrix; + +varying vec3 vertex_normal; +varying vec4 vertex_position; + +void main(void) +{ + vec4 current_position = vec4(position, 1.0); + + // Transform the normal to eye coordinates + vertex_normal = normalize(vec3(NormalMatrix * vec4(normal, 1.0))); + + // Transform the current position to eye coordinates + vertex_position = ModelViewMatrix * current_position; + + // Transform the current position to clip coordinates + gl_Position = ModelViewProjectionMatrix * current_position; +} diff --git a/src/android.cpp b/src/android.cpp index 53a17f7..d098626 100644 --- a/src/android.cpp +++ b/src/android.cpp @@ -276,6 +276,7 @@ create_and_add_scenes(std::vector& scenes, Canvas& canvas) scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); scenes.push_back(new SceneShadow(canvas)); + scenes.push_back(new SceneRefract(canvas)); } diff --git a/src/default-benchmarks.h b/src/default-benchmarks.h index 45f7672..3726a61 100644 --- a/src/default-benchmarks.h +++ b/src/default-benchmarks.h @@ -64,6 +64,7 @@ private: benchmarks.push_back("jellyfish"); benchmarks.push_back("terrain"); benchmarks.push_back("shadow"); + benchmarks.push_back("refract"); 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 d310a51..40a23d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,7 @@ add_and_register_scenes(vector& scenes, Canvas& canvas) scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); scenes.push_back(new SceneShadow(canvas)); + scenes.push_back(new SceneRefract(canvas)); for (vector::const_iterator iter = scenes.begin(); iter != scenes.end(); diff --git a/src/scene-refract.cpp b/src/scene-refract.cpp new file mode 100644 index 0000000..b1d8dcc --- /dev/null +++ b/src/scene-refract.cpp @@ -0,0 +1,436 @@ +// +// 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-refract.h" +#include "model.h" +#include "texture.h" +#include "util.h" +#include "log.h" +#include "shader-source.h" + +using std::string; +using std::vector; +using std::map; +using LibMatrix::mat4; +using LibMatrix::vec4; +using LibMatrix::vec3; + +static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f); + +// +// Public interfaces +// + +SceneRefract::SceneRefract(Canvas& canvas) : + Scene(canvas, "refract"), + priv_(0) +{ + const ModelMap& modelMap = Model::find_models(); + string optionValues; + for (ModelMap::const_iterator modelIt = modelMap.begin(); + modelIt != modelMap.end(); + modelIt++) + { + static bool doSeparator(false); + if (doSeparator) + { + optionValues += ","; + } + const string& curName = modelIt->first; + optionValues += curName; + doSeparator = true; + } + options_["model"] = Scene::Option("model", "bunny", "Which model to use", + optionValues); + optionValues = ""; + const TextureMap& textureMap = Texture::find_textures(); + for (TextureMap::const_iterator textureIt = textureMap.begin(); + textureIt != textureMap.end(); + textureIt++) + { + static bool doSeparator(false); + if (doSeparator) + { + optionValues += ","; + } + const string& curName = textureIt->first; + optionValues += curName; + doSeparator = true; + } + options_["texture"] = Scene::Option("texture", "nasa1", "Which texture to use", + optionValues); + 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"); +} + +SceneRefract::~SceneRefract() +{ + delete priv_; +} + +bool +SceneRefract::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) { + Log::error("We do not have the depth texture extension!!!\n"); + } + + return false; + } + return true; +} + +bool +SceneRefract::load() +{ + running_ = false; + return true; +} + +void +SceneRefract::unload() +{ +} + +bool +SceneRefract::setup() +{ + // If the scene isn't supported, don't bother to go through setup. + if (!supported(false) || !Scene::setup()) + { + return false; + } + + priv_ = new RefractPrivate(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; + lastUpdateTime_ = startTime_; + running_ = true; + + return true; +} + +void +SceneRefract::teardown() +{ + // Add scene-specific teardown here + priv_->teardown(); + Scene::teardown(); +} + +void +SceneRefract::update() +{ + Scene::update(); + // Add scene-specific update here + priv_->update(lastUpdateTime_ - startTime_); +} + +void +SceneRefract::draw() +{ + priv_->draw(); +} + +Scene::ValidationResult +SceneRefract::validate() +{ + return Scene::ValidationUnknown; +} + +// +// Private interfaces +// + +bool +DistanceRenderTarget::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 +DistanceRenderTarget::teardown() +{ + program_.stop(); + program_.release(); + if (tex_) { + glDeleteTextures(1, &tex_); + tex_ = 0; + } + if (fbo_) { + glDeleteFramebuffers(1, &fbo_); + fbo_ = 0; + } +} + +void +DistanceRenderTarget::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 DistanceRenderTarget::disable() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, canvas_width_, canvas_height_); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +bool +RefractPrivate::setup(map& options) +{ + // Program object setup + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-refract.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/light-refract.frag"); + static const vec4 materialDiffuse(0.0f, 0.0f, 0.0f, 0.0f); + static const vec4 lightColor(1.0, 1.0, 1.0, 1.0); + + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + frg_source.add_const("LightColor", lightColor); + frg_source.add_const("LightSourcePosition", lightPosition); + frg_source.add_const("MaterialDiffuse", materialDiffuse); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + + const string& whichTexture(options["texture"].value); + if (!Texture::load(whichTexture, &texture_, GL_LINEAR, GL_LINEAR, 0)) + return false; + + // Model setup + Model model; + const string& whichModel(options["model"].value); + bool modelLoaded = model.load(whichModel); + + if(!modelLoaded) + return false; + + // Now that we're successfully loaded, there are a few quirks about + // some of the known models that we need to account for. The draw + // logic for the scene wants to rotate the model around the Y axis. + // Most of our models are described this way. Some need adjustment + // (an additional rotation that gets the model into the correct + // orientation). + // + // Here's a summary: + // + // Angel rotates around the Y axis + // Armadillo rotates around the Y axis + // Buddha rotates around the X axis + // Bunny rotates around the Y axis + // Dragon rotates around the X axis + // Horse rotates around the Y axis + if (whichModel == "buddha" || whichModel == "dragon") + { + orientModel_ = true; + orientationAngle_ = -90.0; + orientationVec_ = vec3(1.0, 0.0, 0.0); + } + else if (whichModel == "armadillo") + { + orientModel_ = true; + orientationAngle_ = 180.0; + orientationVec_ = vec3(0.0, 1.0, 0.0); + } + + 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, 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; + } + + return true; +} +void +RefractPrivate::teardown() +{ + depthTarget_.teardown(); + program_.stop(); + program_.release(); + mesh_.reset(); +} + +void +RefractPrivate::update(double elapsedTime) +{ + rotation_ = rotationSpeed_ * elapsedTime; +} + +void +RefractPrivate::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(); + + // Ground rendering using the above generated texture... + //ground_.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); + mvp = projection_.getCurrent(); + mvp *= modelview_.getCurrent(); + + program_.start(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + program_["DistanceMap"] = 0; + program_["ModelViewProjectionMatrix"] = mvp; + + // Load the NormalMatrix uniform in the shader. The NormalMatrix is the + // inverse transpose of the model view matrix. + mat4 normal_matrix(modelview_.getCurrent()); + normal_matrix.inverse().transpose(); + program_["NormalMatrix"] = normal_matrix; + attrib_locations.clear(); + 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(); +} + diff --git a/src/scene-refract.h b/src/scene-refract.h new file mode 100644 index 0000000..2d61ab3 --- /dev/null +++ b/src/scene-refract.h @@ -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 +// +#ifndef SCENE_REFRACT_ +#define SCENE_REFRACT_ + +#include "scene.h" +#include "stack.h" + +// +// 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 DistanceRenderTarget +{ + Program program_; + unsigned int canvas_width_; + unsigned int canvas_height_; + unsigned int width_; + unsigned int height_; + unsigned int tex_; + unsigned int fbo_; +public: + DistanceRenderTarget() : + canvas_width_(0), + canvas_height_(0), + width_(0), + height_(0), + tex_(0), + fbo_(0) {} + ~DistanceRenderTarget() {} + bool setup(unsigned int width, unsigned int height); + void teardown(); + void enable(const LibMatrix::mat4& mvp); + void disable(); + unsigned int texture() { return tex_; } + Program& program() { return program_; } +}; + +class RefractPrivate +{ + Canvas& canvas_; + DistanceRenderTarget depthTarget_; + Program program_; + LibMatrix::Stack4 modelview_; + LibMatrix::Stack4 projection_; + Mesh mesh_; + LibMatrix::vec3 centerVec_; + bool orientModel_; + float orientationAngle_; + LibMatrix::vec3 orientationVec_; + float radius_; + float rotation_; + float rotationSpeed_; + unsigned int texture_; + bool useVbo_; + +public: + RefractPrivate(Canvas& canvas) : + canvas_(canvas), + orientModel_(false), + orientationAngle_(0.0), + radius_(0.0), + rotation_(0.0), + rotationSpeed_(36.0), + texture_(0), + useVbo_(true) {} + ~RefractPrivate() {} + + bool setup(std::map& options); + void teardown(); + void update(double elapsedTime); + void draw(); +}; + +#endif // SCENE_REFRACT_ diff --git a/src/scene.h b/src/scene.h index dd77ba9..88f839d 100644 --- a/src/scene.h +++ b/src/scene.h @@ -574,4 +574,21 @@ public: ValidationResult validate(); }; +class RefractPrivate; +class SceneRefract : public Scene +{ + RefractPrivate* priv_; +public: + SceneRefract(Canvas& canvas); + ~SceneRefract(); + bool supported(bool show_errors); + bool load(); + void unload(); + bool setup(); + void teardown(); + void update(); + void draw(); + ValidationResult validate(); +}; + #endif -- cgit v1.2.3