用C ++计算切线空间

时间:2013-06-08 14:01:08

标签: c++ opengl graphics cg

我正在尝试使用法线贴图来渲染场景

因此,我正在计算C ++中的切线空间,并将binormal和tanget分别存储在一个数组中,该数组将使用vertexattribpointer上传到我的着色器。

以下是我如何计算空间

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]);
        }
}

这是我的顶点和片段着色器

顶点着色器

#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;
}

片段着色器

#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);  


}

问题是有时候我在着色器中没有切线和副法线的值。这里有三个截图,我希望能解决我的问题:

当我使用上面的代码渲染时,这就是当前场景的样子:

Scene with code from above

当我使用lightDir作为颜色

时,这就是场景的样子

LightDir as color

第三个显示eyeDir为彩色的场景

EyeDir as color

所有照片均采用相同角度拍摄,无需移动相机或旋转任何物体。 我已经将我的代码与www中的几个不同来源进行了比较,但我没有发现我做过的错误......

其他信息:

我正在迭代所有当前的面孔。三个指数将给我一个三角形。每个顶点的UV值存储在相同的索引处。在那里有很多调试,我很确定这是正确的值,因为我可以在使用gedit搜索时在.obj文件中找到正确的值。

在计算切线和副法线后,我将法线存储在与顶点位置在数组中相同的索引处。根据我的理解,这应该给我正确的位置,我正在为每个顶点计算这个。对于一个面中的每个顶点,我使用相同的切线基础,这可能会在另一个面使用此顶点时被覆盖,这可能会弄乱我的最终结果,但只是在非常小的细节中......

编辑: 对于任何其他问题,这是整个项目:

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

1 个答案:

答案 0 :(得分:6)

在您的顶点着色器中,您有:

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

这应该是:

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

注意'0'而不是'1'(我也假设你的意思是改变你的法线)。你在这里使用'0'是因为你想忽略模型视图转换的转换部分(你正在转换一个向量而不是一个点)。