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?