=== added file 'data/shaders/bump-height.frag'
@@ -0,0 +1,56 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform sampler2D HeightMap;
+
+varying vec2 TextureCoord;
+varying vec3 NormalEye;
+varying vec3 TangentEye;
+varying vec3 BitangentEye;
+
+void main(void)
+{
+ const vec4 LightSourceAmbient = vec4(0.1, 0.1, 0.1, 1.0);
+ const vec4 LightSourceDiffuse = vec4(0.8, 0.8, 0.8, 1.0);
+ const vec4 LightSourceSpecular = vec4(0.8, 0.8, 0.8, 1.0);
+ const vec4 MaterialAmbient = vec4(1.0, 1.0, 1.0, 1.0);
+ const vec4 MaterialDiffuse = vec4(1.0, 1.0, 1.0, 1.0);
+ const vec4 MaterialSpecular = vec4(0.2, 0.2, 0.2, 1.0);
+ const float MaterialShininess = 100.0;
+ const float height_factor = 13.0;
+
+ // Get the data from the height map
+ float height0 = texture2D(HeightMap, TextureCoord).x;
+ float heightX = texture2D(HeightMap, TextureCoord + vec2(TextureStepX, 0.0)).x;
+ float heightY = texture2D(HeightMap, TextureCoord + vec2(0.0, TextureStepY)).x;
+ vec2 dh = vec2(heightX - height0, heightY - height0);
+
+ // Adjust the normal based on the height map data
+ vec3 N = NormalEye - height_factor * dh.x * TangentEye -
+ height_factor * dh.y * BitangentEye;
+ N = normalize(N);
+
+ // In the lighting model we are using here (Blinn-Phong with light at
+ // infinity, viewer at infinity), the light position/direction and the
+ // half vector is constant for the all the fragments.
+ vec3 L = normalize(LightSourcePosition.xyz);
+ vec3 H = normalize(LightSourceHalfVector);
+
+ // Calculate the diffuse color according to Lambertian reflectance
+ vec4 diffuse = MaterialDiffuse * LightSourceDiffuse * max(dot(N, L), 0.0);
+
+ // Calculate the ambient color
+ vec4 ambient = MaterialAmbient * LightSourceAmbient;
+
+ // Calculate the specular color according to the Blinn-Phong model
+ vec4 specular = MaterialSpecular * LightSourceSpecular *
+ pow(max(dot(N,H), 0.0), MaterialShininess);
+
+ // Calculate the final color
+ gl_FragColor = ambient + specular + diffuse;
+
+ //gl_FragColor = vec4(height_diff_raw.xy, 0.0, 1.0);
+ //gl_FragColor = vec4(height_diff_scaled.xy, 0.0, 1.0);
+ //gl_FragColor = vec4(Tangent, 1.0);
+}
=== added file 'data/shaders/bump-height.vert'
@@ -0,0 +1,28 @@
+attribute vec3 position;
+attribute vec2 texcoord;
+attribute vec3 normal;
+attribute vec3 tangent;
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 NormalMatrix;
+
+varying vec2 TextureCoord;
+varying vec3 NormalEye;
+varying vec3 TangentEye;
+varying vec3 BitangentEye;
+
+void main(void)
+{
+ TextureCoord = texcoord;
+
+ // Transform normal, tangent and bitangent to eye space, keeping
+ // all of them perpendicular to the Normal. That is why we use
+ // NormalMatrix, instead of ModelView, to transform the tangent and
+ // bitangent.
+ NormalEye = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));
+ TangentEye = normalize(vec3(NormalMatrix * vec4(tangent, 1.0)));
+ BitangentEye = normalize(vec3(NormalMatrix * vec4(cross(normal, tangent), 1.0)));
+
+ // Transform the position to clip coordinates
+ gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);
+}
=== added file 'data/shaders/bump-normals-tangent.frag'
@@ -0,0 +1,49 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform sampler2D NormalMap;
+
+varying vec2 TextureCoord;
+varying mat4 TangentToEyeMatrix;
+
+void main(void)
+{
+ const vec4 LightSourceAmbient = vec4(0.1, 0.1, 0.1, 1.0);
+ const vec4 LightSourceDiffuse = vec4(0.8, 0.8, 0.8, 1.0);
+ const vec4 LightSourceSpecular = vec4(0.8, 0.8, 0.8, 1.0);
+ const vec4 MaterialAmbient = vec4(1.0, 1.0, 1.0, 1.0);
+ const vec4 MaterialDiffuse = vec4(1.0, 1.0, 1.0, 1.0);
+ const vec4 MaterialSpecular = vec4(0.2, 0.2, 0.2, 1.0);
+ const float MaterialShininess = 100.0;
+
+ // Get the raw normal XYZ data from the normal map
+ vec3 normal_raw = texture2D(NormalMap, TextureCoord).xyz;
+
+ // Map "color" range [0, 1.0] to normal range [-1.0, 1.0]
+ vec3 normal_scaled = normal_raw * 2.0 - 1.0;
+
+ // The normal data is in tangent space, convert it to eye space so that
+ // lighting calculations can work (light information is in eye space).
+ vec3 N = normalize(vec3(TangentToEyeMatrix * vec4(normal_scaled, 1.0)));
+
+ // In the lighting model we are using here (Blinn-Phong with light at
+ // infinity, viewer at infinity), the light position/direction and the
+ // half vector is constant for the all the fragments.
+ vec3 L = normalize(LightSourcePosition.xyz);
+ vec3 H = normalize(LightSourceHalfVector);
+
+ // Calculate the diffuse color according to Lambertian reflectance
+ vec4 diffuse = MaterialDiffuse * LightSourceDiffuse * max(dot(N, L), 0.0);
+
+ // Calculate the ambient color
+ vec4 ambient = MaterialAmbient * LightSourceAmbient;
+
+ // Calculate the specular color according to the Blinn-Phong model
+ vec4 specular = MaterialSpecular * LightSourceSpecular *
+ pow(max(dot(N,H), 0.0), MaterialShininess);
+
+ // Calculate the final color
+ gl_FragColor = ambient + specular + diffuse;
+ //gl_FragColor = vec4(N, 1.0);
+}
=== added file 'data/shaders/bump-normals-tangent.vert'
@@ -0,0 +1,39 @@
+attribute vec3 position;
+attribute vec2 texcoord;
+attribute vec3 normal;
+attribute vec3 tangent;
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 NormalMatrix;
+
+varying vec2 TextureCoord;
+varying mat4 TangentToEyeMatrix;
+
+void main(void)
+{
+ TextureCoord = texcoord;
+
+ // Calculate bitangent
+ vec3 bitangent = normalize(cross(normal, tangent));
+
+ // Calculate tangent space to eye space transformation matrix.
+ // We need this matrix in the fragment shader to transform the data
+ // from the normal map, which is in tangent space, to eye space,
+ // so that we can perform meaningful lighting calculations. Alternatively,
+ // we could have used an EyeToTangentMatrix, to convert light direction etc
+ // to tangent space.
+
+ // First calculate a tangent space to object space transformation matrix.
+ mat4 TangentToObject = mat4(tangent.x, tangent.y, tangent.z, 0.0,
+ bitangent.x, bitangent.y, bitangent.z, 0.0,
+ normal.x, normal.y, normal.z, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+
+ // Multiply with the NormalMatrix to further transform from object
+ // space to eye space (we are manipulating normals, so we can't use
+ // the ModelView matrix for this step!).
+ TangentToEyeMatrix = NormalMatrix * TangentToObject;
+
+ // Transform the position to clip coordinates
+ gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);
+}
=== added file 'data/textures/asteroid-height-map.png'
Binary files data/textures/asteroid-height-map.png 1970-01-01 00:00:00 +0000 and data/textures/asteroid-height-map.png 2011-11-14 18:01:44 +0000 differ
=== added file 'data/textures/asteroid-normal-map-tangent.png'
Binary files data/textures/asteroid-normal-map-tangent.png 1970-01-01 00:00:00 +0000 and data/textures/asteroid-normal-map-tangent.png 2011-11-11 14:40:29 +0000 differ
=== modified file 'src/default-benchmarks.h'
@@ -51,6 +51,7 @@
benchmarks.push_back("shading:shading=phong");
benchmarks.push_back("bump:bump-render=high-poly");
benchmarks.push_back("bump:bump-render=normals");
+ benchmarks.push_back("bump:bump-render=height");
benchmarks.push_back("effect2d:kernel=0,1,0;1,-4,1;0,1,0;");
benchmarks.push_back("effect2d:kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;");
benchmarks.push_back("pulsar:quads=5:texture=false:light=false");
=== modified file 'src/model.cpp'
@@ -105,7 +105,8 @@
*/
void
Model::append_object_to_mesh(const Object &object, Mesh &mesh,
- int p_pos, int n_pos, int t_pos)
+ int p_pos, int n_pos, int t_pos,
+ int nt_pos, int nb_pos)
{
size_t face_count = object.faces.size();
@@ -123,6 +124,10 @@
mesh.set_attrib(n_pos, a.n);
if (t_pos >= 0)
mesh.set_attrib(t_pos, a.t);
+ if (nt_pos >= 0)
+ mesh.set_attrib(nt_pos, a.nt);
+ if (nb_pos >= 0)
+ mesh.set_attrib(nb_pos, a.nb);
mesh.next_vertex();
if (p_pos >= 0)
@@ -131,6 +136,10 @@
mesh.set_attrib(n_pos, b.n);
if (t_pos >= 0)
mesh.set_attrib(t_pos, b.t);
+ if (nt_pos >= 0)
+ mesh.set_attrib(nt_pos, b.nt);
+ if (nb_pos >= 0)
+ mesh.set_attrib(nb_pos, b.nb);
mesh.next_vertex();
if (p_pos >= 0)
@@ -139,8 +148,11 @@
mesh.set_attrib(n_pos, c.n);
if (t_pos >= 0)
mesh.set_attrib(t_pos, c.t);
+ if (nt_pos >= 0)
+ mesh.set_attrib(nt_pos, c.nt);
+ if (nb_pos >= 0)
+ mesh.set_attrib(nb_pos, c.nb);
}
-
}
/**
@@ -178,6 +190,8 @@
int p_pos = -1;
int n_pos = -1;
int t_pos = -1;
+ int nt_pos = -1;
+ int nb_pos = -1;
mesh.reset();
@@ -192,6 +206,10 @@
n_pos = ai - attribs.begin();
else if (ai->first == AttribTypeTexcoord)
t_pos = ai - attribs.begin();
+ else if (ai->first == AttribTypeTangent)
+ nt_pos = ai - attribs.begin();
+ else if (ai->first == AttribTypeBitangent)
+ nb_pos = ai - attribs.begin();
}
mesh.set_vertex_format(format);
@@ -200,7 +218,7 @@
iter != objects_.end();
iter++)
{
- append_object_to_mesh(*iter, mesh, p_pos, n_pos, t_pos);
+ append_object_to_mesh(*iter, mesh, p_pos, n_pos, t_pos, nt_pos, nb_pos);
}
}
@@ -217,25 +235,62 @@
iter++)
{
Object &object = *iter;
- size_t face_count = object.faces.size();
- size_t vertex_count = object.vertices.size();
- for(unsigned i = 0; i < face_count; i++)
+ for (vector<Face>::const_iterator f_iter = object.faces.begin();
+ f_iter != object.faces.end();
+ f_iter++)
{
- const Face &face = object.faces[i];
+ const Face &face = *f_iter;
Vertex &a = object.vertices[face.a];
Vertex &b = object.vertices[face.b];
Vertex &c = object.vertices[face.c];
+ /* Calculate normal */
n = LibMatrix::vec3::cross(b.v - a.v, c.v - a.v);
n.normalize();
a.n += n;
b.n += n;
c.n += n;
- }
-
- for(unsigned i = 0; i < vertex_count; i++)
- object.vertices[i].n.normalize();
+
+ LibMatrix::vec3 q1(b.v - a.v);
+ LibMatrix::vec3 q2(c.v - a.v);
+ LibMatrix::vec2 u1(b.t - a.t);
+ LibMatrix::vec2 u2(c.t - a.t);
+ float det = (u1.x() * u2.y() - u2.x() * u1.y());
+
+ /* Calculate tangent */
+ LibMatrix::vec3 nt;
+ nt.x(det * (u2.y() * q1.x() - u1.y() * q2.x()));
+ nt.y(det * (u2.y() * q1.y() - u1.y() * q2.y()));
+ nt.z(det * (u2.y() * q1.z() - u1.y() * q2.z()));
+ nt.normalize();
+ a.nt += nt;
+ b.nt += nt;
+ c.nt += nt;
+
+ /* Calculate bitangent */
+ LibMatrix::vec3 nb;
+ nb.x(det * (u1.x() * q2.x() - u2.x() * q1.x()));
+ nb.y(det * (u1.x() * q2.y() - u2.x() * q1.y()));
+ nb.z(det * (u1.x() * q2.z() - u2.x() * q1.z()));
+ nb.normalize();
+ a.nb += nb;
+ b.nb += nb;
+ c.nb += nb;
+ }
+
+ for (vector<Vertex>::iterator v_iter = object.vertices.begin();
+ v_iter != object.vertices.end();
+ v_iter++)
+ {
+ Vertex &v = *v_iter;
+ /* Orthogonalize */
+ v.nt = (v.nt - v.n * LibMatrix::vec3::dot(v.nt, v.n));
+ v.n.normalize();
+ v.nt.normalize();
+ v.nb.normalize();
+ }
+
}
}
=== modified file 'src/model.h'
@@ -70,9 +70,11 @@
typedef enum {
AttribTypePosition = 1,
- AttribTypeNormal = 2,
- AttribTypeTexcoord = 4,
- AttribTypeCustom = 8
+ AttribTypeNormal,
+ AttribTypeTexcoord,
+ AttribTypeTangent,
+ AttribTypeBitangent,
+ AttribTypeCustom
} AttribType;
Model() {}
@@ -97,6 +99,8 @@
LibMatrix::vec3 v;
LibMatrix::vec3 n;
LibMatrix::vec2 t;
+ LibMatrix::vec3 nt;
+ LibMatrix::vec3 nb;
};
struct Object {
@@ -107,7 +111,8 @@
};
void append_object_to_mesh(const Object &object, Mesh &mesh,
- int p_pos, int n_pos, int t_pos);
+ int p_pos, int n_pos, int t_pos,
+ int nt_pos, int nb_pos);
bool load_3ds(const std::string &filename);
bool load_obj(const std::string &filename);
=== modified file 'src/scene-bump.cpp'
@@ -35,7 +35,7 @@
texture_(0), rotation_(0.0f), rotationSpeed_(0.0f)
{
options_["bump-render"] = Scene::Option("bump-render", "off",
- "How to render bumps [off, normals, high-poly]");
+ "How to render bumps [off, normals, normals-tangent, height, high-poly]");
}
SceneBump::~SceneBump()
@@ -159,6 +159,112 @@
}
void
+SceneBump::setup_model_normals_tangent()
+{
+ static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-normals-tangent.vert");
+ static const std::string frg_shader_filename(GLMARK_DATA_PATH"/shaders/bump-normals-tangent.frag");
+ static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
+ Model model;
+
+ if(!model.load("asteroid-low"))
+ return;
+
+ model.calculate_normals();
+
+ /* Calculate the half vector */
+ LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
+ halfVector.normalize();
+ halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
+ halfVector.normalize();
+
+ std::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));
+ attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTexcoord, 2));
+ attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTangent, 3));
+
+ model.convert_to_mesh(mesh_, attribs);
+
+ /* Load shaders */
+ ShaderSource vtx_source(vtx_shader_filename);
+ ShaderSource frg_source(frg_shader_filename);
+
+ /* Add constants to shaders */
+ frg_source.add_const("LightSourcePosition", lightPosition);
+ frg_source.add_const("LightSourceHalfVector", halfVector);
+
+ if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
+ frg_source.str()))
+ {
+ return;
+ }
+
+ std::vector<GLint> attrib_locations;
+ attrib_locations.push_back(program_["position"].location());
+ attrib_locations.push_back(program_["normal"].location());
+ attrib_locations.push_back(program_["texcoord"].location());
+ attrib_locations.push_back(program_["tangent"].location());
+ mesh_.set_attrib_locations(attrib_locations);
+
+ Texture::load(GLMARK_DATA_PATH"/textures/asteroid-normal-map-tangent.png", &texture_,
+ GL_NEAREST, GL_NEAREST, 0);
+}
+
+void
+SceneBump::setup_model_height()
+{
+ static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/bump-height.vert");
+ static const std::string frg_shader_filename(GLMARK_DATA_PATH"/shaders/bump-height.frag");
+ static const LibMatrix::vec4 lightPosition(20.0f, 20.0f, 10.0f, 1.0f);
+ Model model;
+
+ if(!model.load("asteroid-low"))
+ return;
+
+ model.calculate_normals();
+
+ /* Calculate the half vector */
+ LibMatrix::vec3 halfVector(lightPosition.x(), lightPosition.y(), lightPosition.z());
+ halfVector.normalize();
+ halfVector += LibMatrix::vec3(0.0, 0.0, 1.0);
+ halfVector.normalize();
+
+ std::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));
+ attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTexcoord, 2));
+ attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeTangent, 3));
+
+ model.convert_to_mesh(mesh_, attribs);
+
+ /* Load shaders */
+ ShaderSource vtx_source(vtx_shader_filename);
+ ShaderSource frg_source(frg_shader_filename);
+
+ /* Add constants to shaders */
+ frg_source.add_const("LightSourcePosition", lightPosition);
+ frg_source.add_const("LightSourceHalfVector", halfVector);
+ frg_source.add_const("TextureStepX", 1.0 / 1024.0);
+ frg_source.add_const("TextureStepY", 1.0 / 1024.0);
+
+ if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
+ frg_source.str()))
+ {
+ return;
+ }
+
+ std::vector<GLint> attrib_locations;
+ attrib_locations.push_back(program_["position"].location());
+ attrib_locations.push_back(program_["normal"].location());
+ attrib_locations.push_back(program_["texcoord"].location());
+ attrib_locations.push_back(program_["tangent"].location());
+ mesh_.set_attrib_locations(attrib_locations);
+
+ Texture::load(GLMARK_DATA_PATH"/textures/asteroid-height-map.png", &texture_,
+ GL_NEAREST, GL_NEAREST, 0);
+}
+
+void
SceneBump::setup()
{
Scene::setup();
@@ -168,6 +274,10 @@
Model::find_models();
if (bump_render == "normals")
setup_model_normals();
+ else if (bump_render == "normals-tangent")
+ setup_model_normals_tangent();
+ else if (bump_render == "height")
+ setup_model_height();
else if (bump_render == "off" || bump_render == "high-poly")
setup_model_plain(bump_render);
@@ -178,6 +288,7 @@
// Load texture sampler value
program_["NormalMap"] = 0;
+ program_["HeightMap"] = 0;
currentFrame_ = 0;
rotation_ = 0.0;
@@ -239,6 +350,9 @@
normal_matrix.inverse().transpose();
program_["NormalMatrix"] = normal_matrix;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+
mesh_.render_vbo();
}
@@ -263,6 +377,10 @@
ref = Canvas::Pixel(0x9c, 0x9c, 0x9c, 0xff);
else if (bump_render == "normals")
ref = Canvas::Pixel(0xa4, 0xa4, 0xa4, 0xff);
+ else if (bump_render == "normals-tangent")
+ ref = Canvas::Pixel(0x99, 0x99, 0x99, 0xff);
+ else if (bump_render == "height")
+ ref = Canvas::Pixel(0x9d, 0x9d, 0x9d, 0xff);
else
return Scene::ValidationUnknown;
=== modified file 'src/scene.h'
@@ -380,6 +380,8 @@
private:
void setup_model_plain(const std::string &type);
void setup_model_normals();
+ void setup_model_normals_tangent();
+ void setup_model_height();
};
class SceneEffect2D : public Scene