diff mbox

[Branch,~glmark2-dev/glmark2/trunk] Rev 255: Merge of lp:~glmark2-dev/glmark2/refract

Message ID 20121217161015.1552.88064.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

Jesse Barker Dec. 17, 2012, 4:10 p.m. UTC
Merge authors:
  Alexandros Frantzis (afrantzis)
  Jesse Barker (jesse-barker)
Related merge proposals:
  https://code.launchpad.net/~glmark2-dev/glmark2/refract/+merge/139808
  proposed by: Jesse Barker (jesse-barker)
  review: Approve - Alexandros Frantzis (afrantzis)
------------------------------------------------------------
revno: 255 [merge]
committer: Jesse Barker <jesse.barker@linaro.org>
branch nick: trunk
timestamp: Mon 2012-12-17 07:45:56 -0800
message:
  Merge of lp:~glmark2-dev/glmark2/refract
  
  Adds new SceneRefract, which performs a two interface refraction through an
  object, as well as a surface reflection.  The object (model), environment
  (texture), and index of refraction of the object medium (index) are all
  adjustable via command line options, but the default is to render the Stanford
  Bunny against a background of the 'nasa1' texture with a refractive index of
  1.2; the medium surrounding the object is assumed to be air/vacuum (index==1.0).
added:
  data/shaders/light-refract.frag
  data/shaders/light-refract.vert
  src/scene-refract.cpp
  src/scene-refract.h
modified:
  src/android.cpp
  src/default-benchmarks.h
  src/main.cpp
  src/scene.h


--
lp:glmark2
https://code.launchpad.net/~glmark2-dev/glmark2/trunk

You are subscribed to branch lp:glmark2.
To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription
diff mbox

Patch

=== added file 'data/shaders/light-refract.frag'
--- data/shaders/light-refract.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/light-refract.frag	2012-12-14 00:06:17 +0000
@@ -0,0 +1,45 @@ 
+uniform sampler2D DistanceMap;
+uniform sampler2D NormalMap;
+uniform sampler2D ImageMap;
+
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec4 MapCoord;
+
+void main()
+{
+    const vec4 lightSpecular = vec4(0.8, 0.8, 0.8, 1.0);
+    const vec4 matSpecular = vec4(1.0, 1.0, 1.0, 1.0);
+    const float matShininess = 100.0;
+    const vec2 point_five = vec2(0.5);
+    // Need the normalized eye direction and surface normal vectors to
+    // compute the transmitted vector through the "front" surface of the object.
+    vec3 eye_direction = normalize(-vertex_position.xyz);
+    vec3 normalized_normal = normalize(vertex_normal);
+    vec3 front_refraction = refract(eye_direction, normalized_normal, RefractiveIndex);
+    // Find our best distance approximation through the object so we can
+    // project the transmitted vector to the back of the object to find
+    // the exit point.
+    vec3 mc_perspective = (MapCoord.xyz / MapCoord.w) + front_refraction;
+    vec2 dcoord = mc_perspective.st * point_five + point_five;
+    vec4 distance_value = texture2D(DistanceMap, dcoord);
+    vec3 back_position = vertex_position.xyz + front_refraction * distance_value.z;
+    // Use the exit point to index the map of back-side normals, and use the
+    // back-side position and normal to find the transmitted vector out of the
+    // object.
+    vec2 normcoord = back_position.st * point_five + point_five;
+    vec3 back_normal = texture2D(NormalMap, normcoord).xyz;
+    vec3 back_refraction = refract(back_position, back_normal, 1.0);
+    // Use the transmitted vector from the exit point to determine where
+    // the vector would intersect the environment (in this case a background
+    // image.
+    vec2 imagecoord = back_refraction.st * point_five + point_five;
+    vec4 texel = texture2D(ImageMap, imagecoord);
+    // Add in specular reflection, and we have our fragment value.
+    vec3 light_direction = normalize(vertex_position.xyz/vertex_position.w -
+                                     LightSourcePosition.xyz/LightSourcePosition.w);
+    vec3 reflection = reflect(light_direction, normalized_normal);
+    float specularTerm = pow(max(0.0, dot(reflection, eye_direction)), matShininess);
+    vec4 specular = (lightSpecular * matSpecular);
+    gl_FragColor = (specular * specularTerm) + texel;
+}

=== added file 'data/shaders/light-refract.vert'
--- data/shaders/light-refract.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/light-refract.vert	2012-12-10 23:17:15 +0000
@@ -0,0 +1,28 @@ 
+attribute vec3 position;
+attribute vec3 normal;
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 NormalMatrix;
+uniform mat4 ModelViewMatrix;
+uniform mat4 LightMatrix;
+
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec4 MapCoord;
+
+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 for use as texture coordinates
+    MapCoord = LightMatrix * current_position;
+
+    // Transform the current position to clip coordinates
+    gl_Position = ModelViewProjectionMatrix * current_position;
+}

=== modified file 'src/android.cpp'
--- src/android.cpp	2012-10-19 13:19:19 +0000
+++ src/android.cpp	2012-12-07 19:57:44 +0000
@@ -276,6 +276,7 @@ 
     scenes.push_back(new SceneTerrain(canvas));
     scenes.push_back(new SceneJellyfish(canvas));
     scenes.push_back(new SceneShadow(canvas));
+    scenes.push_back(new SceneRefract(canvas));
 }
 
 

=== modified file 'src/default-benchmarks.h'
--- src/default-benchmarks.h	2012-10-19 13:19:19 +0000
+++ src/default-benchmarks.h	2012-12-07 19:57:44 +0000
@@ -64,6 +64,7 @@ 
         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");

=== modified file 'src/main.cpp'
--- src/main.cpp	2012-10-19 13:19:19 +0000
+++ src/main.cpp	2012-12-07 19:57:44 +0000
@@ -64,6 +64,7 @@ 
     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<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();

=== added file 'src/scene-refract.cpp'
--- src/scene-refract.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-refract.cpp	2012-12-15 09:46:12 +0000
@@ -0,0 +1,469 @@ 
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// 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(1.0f, 1.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_["index"] = Scene::Option("index", "1.2",
+                                      "Index of refraction of the medium to simulate");
+    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");
+}
+
+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();
+    delete priv_;
+    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(2, &tex_[0]);
+    glBindTexture(GL_TEXTURE_2D, tex_[DEPTH]);
+    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, tex_[COLOR]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    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_RGBA, width_, height_, 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, 0);
+    glGenerateMipmap(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    glGenFramebuffers(1, &fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
+                           tex_[DEPTH], 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                           tex_[COLOR], 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(2, &tex_[0]);
+        tex_[DEPTH] = tex_[COLOR] = 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_[DEPTH], 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                           tex_[COLOR], 0);
+    glViewport(0, 0, width_, height_);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    glCullFace(GL_FRONT);
+}
+
+void DistanceRenderTarget::disable()
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glViewport(0, 0, canvas_width_, canvas_height_);
+    glCullFace(GL_BACK);
+}
+
+bool
+RefractPrivate::setup(map<string, Scene::Option>& 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 lightColor(0.4, 0.4, 0.4, 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);
+    float refractive_index(Util::fromString<float>(options["index"].value));
+    frg_source.add_const("RefractiveIndex", refractive_index);
+
+    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<std::pair<Model::AttribType, int> > attribs;
+    attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
+    attribs.push_back(std::pair<Model::AttribType, int>(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<float>(canvas_.width())/static_cast<float>(canvas_.height()));
+    projection_.perspective(fovy, aspect, 2.0, 2.0 + diameter);
+
+    // 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_.getCurrent();
+    light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
+                                      0.0, 0.0, 0.0,
+                                      0.0, 1.0, 0.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);
+    if (orientModel_)
+    {
+        modelview_.rotate(orientationAngle_, orientationVec_.x(), orientationVec_.y(), orientationVec_.z());
+    }
+    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();
+
+    // 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);
+    if (orientModel_)
+    {
+        modelview_.rotate(orientationAngle_, orientationVec_.x(), orientationVec_.y(), orientationVec_.z());
+    }
+    mvp = projection_.getCurrent();
+    mvp *= modelview_.getCurrent();
+
+    program_.start();
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, depthTarget_.depthTexture());
+    program_["DistanceMap"] = 0;
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, depthTarget_.colorTexture());
+    program_["NormalMap"] = 1;
+    glActiveTexture(GL_TEXTURE2);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    program_["ImageMap"] = 2;
+    // Load both the modelview*projection as well as the modelview matrix itself
+    program_["ModelViewProjectionMatrix"] = mvp;
+    program_["ModelViewMatrix"] = modelview_.getCurrent();
+    // 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;
+    program_["LightMatrix"] = light_;
+    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();
+}
+

=== added file 'src/scene-refract.h'
--- src/scene-refract.h	1970-01-01 00:00:00 +0000
+++ src/scene-refract.h	2012-12-10 23:17:15 +0000
@@ -0,0 +1,106 @@ 
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+// 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
+{
+    enum
+    {
+        DEPTH = 0,
+        COLOR
+    };
+    Program program_;
+    unsigned int canvas_width_;
+    unsigned int canvas_height_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int tex_[2];
+    unsigned int fbo_;
+public:
+    DistanceRenderTarget() :
+        canvas_width_(0),
+        canvas_height_(0),
+        width_(0),
+        height_(0),
+        fbo_(0)
+    {
+        tex_[DEPTH] = tex_[COLOR] = 0;
+    }
+    ~DistanceRenderTarget() {}
+    bool setup(unsigned int width, unsigned int height);
+    void teardown();
+    void enable(const LibMatrix::mat4& mvp);
+    void disable();
+    unsigned int depthTexture() { return tex_[DEPTH]; }
+    unsigned int colorTexture() { return tex_[COLOR]; }
+    Program& program() { return program_; }
+};
+
+class RefractPrivate
+{
+    Canvas& canvas_;
+    DistanceRenderTarget depthTarget_;
+    Program program_;
+    LibMatrix::Stack4 modelview_;
+    LibMatrix::Stack4 projection_;
+    LibMatrix::mat4 light_;
+    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<std::string, Scene::Option>& options);
+    void teardown();
+    void update(double elapsedTime);
+    void draw();
+};
+
+#endif // SCENE_REFRACT_

=== modified file 'src/scene.h'
--- src/scene.h	2012-10-19 13:19:19 +0000
+++ src/scene.h	2012-12-15 09:46:12 +0000
@@ -574,4 +574,20 @@ 
     ValidationResult validate();
 };
 
+class RefractPrivate;
+class SceneRefract : public Scene
+{
+    RefractPrivate* priv_;
+public:
+    SceneRefract(Canvas& canvas);
+    bool supported(bool show_errors);
+    bool load();
+    void unload();
+    bool setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+};
+
 #endif