1

I am trying to calculate the Tangent and Bitangent vectors for my terrain. I followed this tutorial to figure out how to calculate these 2 vectors and from what I understand, Tangent Bitangent and Normal vectors should all be orthogonal to each other right? I am checking that and it seems like I am doing something wrong since they are not orthogonal. Can someone please explain to me what I am doing wrong here? Here is my code.

for (int i = 0; i < indices.size(); i += 3) {
    glm::vec3 v0 = vertices.at(indices.at(i)).position;
    glm::vec3 v1 = vertices.at(indices.at(i + 1)).position;
    glm::vec3 v2 = vertices.at(indices.at(i + 2)).position;

    std::cout << "v0 " << glm::to_string(v0) << std::endl;
    std::cout << "v1 " << glm::to_string(v1) << std::endl;
    std::cout << "v2 " << glm::to_string(v2) << std::endl;

    glm::vec2 uv0 = vertices.at(indices.at(i)).texcoord;
    glm::vec2 uv1 = vertices.at(indices.at(i + 1)).texcoord;
    glm::vec2 uv2 = vertices.at(indices.at(i + 2)).texcoord;

    std::cout << "uv0 " << glm::to_string(uv0) << std::endl;
    std::cout << "uv1 " << glm::to_string(uv1) << std::endl;
    std::cout << "uv2 " << glm::to_string(uv2) << std::endl;

    glm::vec3 deltaPos1 = v1 - v0;
    glm::vec3 deltaPos2 = v2 - v0;

    std::cout << "e1 " << glm::to_string(deltaPos1) << std::endl;
    std::cout << "e2 " << glm::to_string(deltaPos2) << std::endl;

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

    std::cout << "deltaUV1 " << glm::to_string(deltaUV1) << std::endl;
    std::cout << "deltaUV2 " << glm::to_string(deltaUV2) << std::endl;

    GLfloat r = (GLfloat) (1.f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x));

    std::cout << "r is: " << r << std::endl;

    glm::vec3 tangent = glm::normalize((deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r);
    glm::vec3 bitangent = glm::normalize((deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r);

    std::cout << "normal 1 " << glm::to_string(vertices.at(indices.at(0)).normal) << std::endl;
    std::cout << "normal 2 " << glm::to_string(vertices.at(indices.at(1)).normal) << std::endl;
    std::cout << "normal 3 " << glm::to_string(vertices.at(indices.at(2)).normal) << std::endl;

    std::cout << "tangent " << glm::to_string(tangent) << std::endl;
    std::cout << "bitangent " << glm::to_string(bitangent) << std::endl;

    glm::vec3 normal1 = vertices.at(indices.at(i)).normal;
    glm::vec3 normal2 = vertices.at(indices.at(i + 1)).normal;
    glm::vec3 normal3 = vertices.at(indices.at(i + 2)).normal;
    for (int j = 0; j < 3; j++) {
        glm::vec3 norm;
        if (j == 0) {
            norm = normal1;
        } else if (j == 1) {
            norm = normal2;
        } else {
            norm = normal3;
        }
        GLfloat tangentDot = glm::dot(norm, tangent);
        GLfloat bitangentDot = glm::dot(norm, bitangent);

        if (tangentDot != 0 || bitangentDot != 0) {
            std::cerr << "ERROR::Terrain::Tangent or bitangent were not orthogonal" << std::endl;
            std::cerr << tangentDot << " " << bitangentDot << std::endl;
        }
    }

    vertices.at(indices.at(i)).tangents = tangent;
    vertices.at(indices.at(i)).bitangents = bitangent;
    vertices.at(indices.at(i + 1)).tangents = tangent;
    vertices.at(indices.at(i + 1)).bitangents = bitangent;
    vertices.at(indices.at(i + 2)).tangents = tangent;
    vertices.at(indices.at(i + 2)).bitangents = bitangent;
}

And here are the debug outputs that I get for the first iteration as an example.

v0 vec3(0.000000, 0.078430, 0.000000)
v1 vec3(0.000000, 0.078430, 3.137255)
v2 vec3(3.137255, 0.078430, 0.000000)
uv0 vec2(0.000000, 0.000000)
uv1 vec2(0.000000, 0.003922)
uv2 vec2(0.003922, 0.000000)
e1 vec3(0.000000, 0.000000, 3.137255)
e2 vec3(3.137255, 0.000000, 0.000000)
deltaUV1 vec2(0.000000, 0.003922)
deltaUV2 vec2(0.003922, 0.000000)
r is: -65025
normal 1 vec3(-0.039155, 0.998466, -0.039155)
normal 2 vec3(-0.039185, 0.999232, 0.000000)
normal 3 vec3(0.000000, 0.999232, -0.039185)
tangent vec3(1.000000, -0.000000, -0.000000)
bitangent vec3(-0.000000, -0.000000, 1.000000)

ERROR::Terrain::Tangent or bitangent were not orthogonal
-0.0391549 -0.0391549
ERROR::Terrain::Tangent or bitangent were not orthogonal
-0.039185 0
ERROR::Terrain::Tangent or bitangent were not orthogonal
0 -0.039185

The dot product of Tangent and Normal is pretty small for the first iteration but it gets much bigger for some of them. Am I understanding the concept correctly? Do these 3 vectors have to be orthogonal? I checked the same dot product for vectors from imported meshes that I import with ASSIMP and they are all orthogonal.

-- Edit: Here is how I calculate my normals.

glm::vec3 Terrain::calculateNormal(int x, int z, unsigned char *textureData, int imageWidth, int imageHeight, int imageChannels) {
// TODO: Not optimal since we are calculating the same vertice height multiple times
// TODO: We also do the same calls above, maybe it will be smarter to just create a lookup table?

float heightL = getHeight(x - 1, z, textureData,imageWidth, imageHeight, imageChannels);
float heightR = getHeight(x + 1, z, textureData,imageWidth, imageHeight, imageChannels);
float heightD = getHeight(x, z - 1, textureData,imageWidth, imageHeight, imageChannels);
float heightU = getHeight(x, z + 1, textureData,imageWidth, imageHeight, imageChannels);

glm::vec3 normal = glm::vec3{heightL - heightR, 2.f, heightD - heightU};
normal = glm::normalize(normal);

return normal;
}

As pointed out in the comments, each of the vertices has it`s own normal and they may vary from one another. How would I handle the tangent and bitangent calculation in this case?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Saik
  • 993
  • 1
  • 16
  • 40
  • How are the normals calculated? Are they smoothed by averaging over all adjacent faces? If that's the case, you have to average the tangents also. – BDL May 28 '20 at 07:15
  • 1
    You calculate the (bi)tangent per triangle, but have a different normal at each vertex of said triangle. Of course they cannot be simultaneously orthogonal to each other! Did you read the "Orthogonalization" section in the article you linked? – Yakov Galka May 28 '20 at 07:42
  • @ybungalobill you are correct, and the fact that all 3 vertices of the triangle can have different vertices confuses me even further :) How can I handle the Tangent Bitangent calculation for this case? I posted the code that I use for calculating the normals just in case. – Saik May 28 '20 at 14:44
  • 1
    There is absolutely _no_ need to havbe TBN form orthonormal basis - the only reason why people want that is so that they can use the transpose instead the inverse when going from wolrd into tangent space. However, if you calculate the lighting in world space, that problem goes away, as you simply transform the tangent space normal from the normal map into world space. – derhass May 29 '20 at 23:33

0 Answers0