Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate tangent space in C++

I am trying to render a scene using normal mapping

Therefore I am calculating the tangent space in C++ and store the binormal and tanget seperately in an array which will be uploaded to my shader using vertexattribpointer.

Here is how I calculate the space

void ObjLoader::computeTangentSpace(MeshData &meshData) {
    GLfloat* tangents = new GLfloat[meshData.vertex_position.size()]();
    GLfloat* binormals = new GLfloat[meshData.vertex_position.size()]();

    std::vector<glm::vec3 > tangent;
    std::vector<glm::vec3 > binormal;

        for(unsigned int i = 0; i < meshData.indices.size(); i = i+3){

            glm::vec3 vertex0 = glm::vec3(meshData.vertex_position.at(meshData.indices.at(i)), meshData.vertex_position.at(meshData.indices.at(i)+1),meshData.vertex_position.at(meshData.indices.at(i)+2));
            glm::vec3 vertex1 = glm::vec3(meshData.vertex_position.at(meshData.indices.at(i+1)), meshData.vertex_position.at(meshData.indices.at(i+1)+1),meshData.vertex_position.at(meshData.indices.at(i+1)+2));
            glm::vec3 vertex2 = glm::vec3(meshData.vertex_position.at(meshData.indices.at(i+2)), meshData.vertex_position.at(meshData.indices.at(i+2)+1),meshData.vertex_position.at(meshData.indices.at(i+2)+2));

            glm::vec3 normal = glm::cross((vertex1 - vertex0),(vertex2 - vertex0));

            glm::vec3 deltaPos;
            if(vertex0 == vertex1)
                deltaPos = vertex2 - vertex0;
            else
                deltaPos = vertex1 - vertex0;

            glm::vec2 uv0 = glm::vec2(meshData.vertex_texcoord.at(meshData.indices.at(i)), meshData.vertex_texcoord.at(meshData.indices.at(i)+1));
            glm::vec2 uv1 = glm::vec2(meshData.vertex_texcoord.at(meshData.indices.at(i+1)), meshData.vertex_texcoord.at(meshData.indices.at(i+1)+1));
            glm::vec2 uv2 = glm::vec2(meshData.vertex_texcoord.at(meshData.indices.at(i+2)), meshData.vertex_texcoord.at(meshData.indices.at(i+2)+1));

            glm::vec2 deltaUV1 = uv1 - uv0;
            glm::vec2 deltaUV2 = uv2 - uv0;

            glm::vec3 tan; // tangents
            glm::vec3 bin; // binormal

            // avoid divion with 0
            if(deltaUV1.s != 0)
                tan = deltaPos / deltaUV1.s;
            else
                tan = deltaPos / 1.0f;

            tan = glm::normalize(tan - glm::dot(normal,tan)*normal);

            bin = glm::normalize(glm::cross(tan, normal));

            // write into array - for each vertex of the face the same value
            tangents[meshData.indices.at(i)]   = tan.x;
            tangents[meshData.indices.at(i)+1] = tan.y;
            tangents[meshData.indices.at(i)+2] = tan.z;

            tangents[meshData.indices.at(i+1)]   = tan.x;
            tangents[meshData.indices.at(i+1)+1] = tan.y;
            tangents[meshData.indices.at(i+1)+2] = tan.z;

            tangents[meshData.indices.at(i+2)]   = tan.x;
            tangents[meshData.indices.at(i+2)+1] = tan.y;
            tangents[meshData.indices.at(i+2)+1] = tan.z;

            binormals[meshData.indices.at(i)]   = bin.x;
            binormals[meshData.indices.at(i)+1] = bin.y;
            binormals[meshData.indices.at(i)+2] = bin.z;

            binormals[meshData.indices.at(i+1)]   = bin.x;
            binormals[meshData.indices.at(i+1)+1] = bin.y;
            binormals[meshData.indices.at(i+1)+2] = bin.z;

            binormals[meshData.indices.at(i+2)]   = bin.x;
            binormals[meshData.indices.at(i+2)+1] = bin.y;
            binormals[meshData.indices.at(i+2)+1] = bin.z;
    }
        // Copy the tangent and binormal to meshData
        for(unsigned int i = 0; i < meshData.vertex_position.size(); i++){
            meshData.vertex_tangent.push_back(tangents[i]);
            meshData.vertex_binormal.push_back(binormals[i]);
        }
}

And here are my vertex and fragment shader

Vertex Shader

#version 330
layout(location = 0) in vec3 vertex;
layout(location = 1) in vec3 vertex_normal;
layout(location = 2) in vec2 vertex_texcoord;
layout(location = 3) in vec3 vertex_tangent;
layout(location = 4) in vec3 vertex_binormal;

struct LightSource {
  vec3 ambient_color;
  vec3 diffuse_color;
  vec3 specular_color;
  vec3 position;
};

uniform vec3 lightPos;

out vec3 vertexNormal; 
out vec3 eyeDir;
out vec3 lightDir;
out vec2 textureCoord;


uniform mat4 view;
uniform mat4 modelview;
uniform mat4 projection;

out vec4 myColor;


void main() {

  mat4 normalMatrix = transpose(inverse(modelview));

  gl_Position = projection * modelview * vec4(vertex, 1.0);

  vec4 binormal = modelview * vec4(vertex_binormal,1);
  vec4 tangent = modelview * vec4(vertex_tangent,1);
  vec4 normal =  vec4(vertex_normal,1); 

  mat3 tangentMatrix = mat3(tangent.xyz,binormal.xyz,normal.xyz); 
  vec3 vertexInCamSpace = (modelview * vec4(vertex, 1.0)).xyz;
  eyeDir = tangentMatrix * normalize( -vertexInCamSpace);  
  vec3 lightInCamSpace = (view * vec4(lightPos, 1.0)).xyz;
  lightDir = tangentMatrix * normalize((lightInCamSpace - vertexInCamSpace));

  textureCoord = vertex_texcoord;
}

Fragment Shader

#version 330

struct LightSource {
  vec3 ambient_color;
  vec3 diffuse_color;
  vec3 specular_color;
  vec3 position;
};

struct Material {
  vec3 ambient_color;
  vec3 diffuse_color;
  vec3 specular_color;
  float specular_shininess;
};

uniform LightSource light;
uniform Material material;

in vec3 vertexNormal;
in vec3 eyeDir;
in vec3 lightDir;
in vec2 textureCoord;


uniform sampler2D texture;
uniform sampler2D normals;


out vec4 color;


in vec4 myColor;
in vec3 bin;
in vec3 tan;


void main() {
          vec3 diffuse  = texture2D(texture,textureCoord).rgb;


          vec3 E = normalize(eyeDir); 

          vec3 N = texture2D(normals,textureCoord).xyz;   
          N = (N - 0.5) * 2.0;

          vec3 ambientTerm = vec3(0);
          vec3 diffuseTerm = vec3(0);
          vec3 specularTerm = vec3(0);
          vec3 L, H;

            L = normalize(lightDir);        
            H = normalize(E + L);
            ambientTerm += light.ambient_color;
            diffuseTerm += light.diffuse_color * max(dot(L, N), 0);
            specularTerm += light.specular_color * pow(max(dot(H, N), 0), material.specular_shininess);

          ambientTerm *= material.ambient_color;
          diffuseTerm *= material.diffuse_color;
          specularTerm *= material.specular_color;

         color = vec4(diffuse, 1) * vec4(ambientTerm + diffuseTerm + specularTerm, 1);  


}

The problem is that sometimes I dont have values for tangent and binormal in the shader.. Here are three screenshots which I hope will clearify my problem:

This is how the scene currently looks like when I render it with the code above:

Scene with code from above

This is how the scene looks like, when I use lightDir as color

LightDir as color

And the third shows the scene with eyeDir as color

EyeDir as color

All the pictures are taken from the same angle without moving camera or rotating anything. I've already compared my code to several different sources in the www but I didn't found the error I've done...

Additional information:

I am iterating over all current faces. Three indices will give me one triangle. The UV values for each vertex are stored at the same index. having a lot of debugging there, I am very sure that this are the correct values as I can find the right values in the .obj file when searching using gedit.

After calculating tangent and binormal I am storing the normal at the same index as the vertex position is in the array. For my understanding this should give me the correct position and I am calculating this for each vertex. For each vertex in a face I am using the same tangent basis, which is maybe later overwritten when another face is using this vertex, this could mess up my final result but only in very small details...

EDIT: For any other questions, here is the whole project:

http://www.incentivelabs.de/Sourcecode/normal_mapping.zip

like image 383
glethien Avatar asked Mar 23 '23 15:03

glethien


1 Answers

In your vertex shader you have:

vec4 binormal = modelview * vec4(vertex_binormal,1);
vec4 tangent = modelview * vec4(vertex_tangent,1);
vec4 normal =  vec4(vertex_normal,1); 

This should be:

vec4 binormal = modelview * vec4(vertex_binormal,0);
vec4 tangent = modelview * vec4(vertex_tangent,0);
vec4 normal =  modelview * vec4(vertex_normal,0); 

Note the '0' instead of '1' (also I'm assuming you meant to transform your normal too). You use '0' here because you want to ignore the translation part of the modelview transformation (you're transforming a vector not a point).

like image 141
GuyRT Avatar answered Apr 05 '23 04:04

GuyRT