我正在使用LWJGL处理3D OpenGL引擎。我设法用Assimp做模型导入部分,现在正在进行法线贴图。 幸运的是,Assimp可以为我计算切线空间,但它无法正常工作。 这是一张如何在sponza模型上工作的图片:
这是从TBN矩阵可视化的T:
代码在顶点着色器中如下所示:
mat3 normalMatrix = transpose(inverse(mat3(transformationMatrix)));
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(normalMatrix * normal);
T = normalize(T - dot(T, N) * N);
vec3 B = normalize(normalMatrix * bitangent);
if (dot(cross(N, T), B) < 0.0){
T = T * -1.0;
}
TBN = transpose(mat3(T, B, N));
检查模型是否具有symetric UV,并修复它。 我认为这会导致问题,因为来自TBN的B看起来更糟糕。
几个星期后,我试图自己计算切线和比特,但我的bitangents看起来比Assimps更糟糕:
private void generateTangentBitangent(){
for(int i = 0; i < indices.size(); i+=3){
Vector3f v0 = new Vector3f (vertices.get(indices.get(i)).getPos());
Vector3f v1 = new Vector3f (vertices.get(indices.get(i+1)).getPos());
Vector3f v2 = new Vector3f (vertices.get(indices.get(i+2)).getPos());
Vector2f uv0 = new Vector2f (vertices.get(indices.get(i)).getTextureCoord());
Vector2f uv1 = new Vector2f (vertices.get(indices.get(i+1)).getTextureCoord());
Vector2f uv2 = new Vector2f (vertices.get(indices.get(i+2)).getTextureCoord());
Vector3f e1 = new Vector3f (v1.sub(v0));
Vector3f e2 = new Vector3f (v2.sub(v0));
Vector2f deltaUV1 = new Vector2f (uv1.sub(uv0));
Vector2f deltaUV2 = new Vector2f (uv2.sub(uv0));
float r = (float) (1.0f / (deltaUV1.x() * deltaUV2.y() - deltaUV1.y() * deltaUV2.x()));
Vector3f tangent = new Vector3f ((e1.mul(deltaUV2.y()).sub(e2.mul(deltaUV1.y()))).mul(r));
//System.out.println(tangent);
Vector3f bitangent = new Vector3f ((e2.mul(deltaUV1.x()).sub(e1.mul(deltaUV2.x()))).mul(r));
if(vertices.get(indices.get(i)).getTangent() == null)
vertices.get(indices.get(i)).setTangent(new Vector3f(0,0,0));
if(vertices.get(indices.get(i)).getBitangent() == null)
vertices.get(indices.get(i)).setBitangent(new Vector3f(0,0,0));
if(vertices.get(indices.get(i+1)).getTangent() == null)
vertices.get(indices.get(i+1)).setTangent(new Vector3f(0,0,0));
if(vertices.get(indices.get(i+1)).getBitangent() == null)
vertices.get(indices.get(i+1)).setBitangent(new Vector3f(0,0,0));
if(vertices.get(indices.get(i+2)).getTangent() == null)
vertices.get(indices.get(i+2)).setTangent(new Vector3f(0,0,0));
if(vertices.get(indices.get(i+2)).getBitangent() == null)
vertices.get(indices.get(i+2)).setBitangent(new Vector3f(0,0,0));
vertices.get(indices.get(i)).getTangent().add(tangent);
vertices.get(indices.get(i)).getBitangent().add(bitangent);
vertices.get(indices.get(i+1)).getTangent().add(tangent);
vertices.get(indices.get(i+1)).getBitangent().add(bitangent);
vertices.get(indices.get(i+2)).getTangent().add(tangent);
vertices.get(indices.get(i+2)).getBitangent().add(bitangent);
}
for (int i = 0 ; i < vertices.size() ; i++) {
vertices.get(i).getTangent().normalize();
vertices.get(i).getBitangent().normalize();
}
}
有人可以帮我解决导致问题的原因吗?
答案 0 :(得分:0)
您在图像上显示的切线/比特数/法线是哪个空间?从您的代码示例来看,它们可能会在切线空间中。
然而,可以更容易地获得切线空间矢量。您不必使用tangent
转换normal
,bitangent
和normalMatrix
,而只需使用对象空间中的轴:
vec3 normalOS = normalize(normal);
vec3 tangentOS = normalize(tangent);
vec3 bitangentOS = normalize(bitangent) // or calculate via cross(normalOS, tangentOS);
mat3 TStoOS = mat3(tangentOS, bitangentOS, normalOS); // transform from tangent space into object space
mat3 OStoTS = transpose(matrixTStoOS); // The transpose of an orthogonal matrix is it's inverse matrix
如果您需要从切线空间转换为世界空间的矩阵,您可以按如下方式计算:
vec3 normalWS = normalize(normalMatrix * normal);
vec3 tangentWS = normalize(normalMatrix * tangent);
vec3 bitangentWS = normalize(cross(normalWS, tangentWS))
mat3 TStoWS = mat3(tangentWS, bitangentWS, normalWS); // transform from tangent space into world space
mat3 WStoTS = transpose(matrixTStoWS); // transform from world space into tangent space