使用法线贴图渲染会导致依赖于旋转的光照

Rendering using normal maps causes rotation-dependent lighting

当物体没有从原点旋转时,法线贴图看起来很棒,聚光灯和平行光也能工作,但是当我在原地旋转物体时,它会变暗然后再次变亮,就在顶面上.

我正在使用多维数据集进行测试。我使用几何着色器来可视化我计算出的法线(在乘以 TBN 矩阵之后),它们似乎位于正确的位置。如果我把法线贴图从等式中去掉,那么光照就很好了。

这里是计算 TBN 的地方:

void calculateTBN()
{
    //get the normal matrix
    mat3 model = mat3(transpose(inverse(mat3(transform))));
    vec3 T = normalize(vec3(model * tangent.xyz ));

    vec3 N = normalize(vec3(model * normal      ));

    vec3 B = cross(N, T);

    mat3 TBN = mat3( T , B , N);

    outputVertex.TBN =TBN;
}

并对法线进行采样和变换:

vec3 calculateNormal()
{
    //Sort the input so that the normal is between 1 and minus 1 instead of 0 and 1
    vec3 input = texture2D(normalMap, inputFragment.textureCoord).xyz;

    input = 2.0 * input - vec3(1.0, 1.0, 1.0);

    vec3 newNormal = normalize(inputFragment.TBN* input);

    return newNormal;
}  

我的光照在世界中space(据我所知,它考虑了变换矩阵但不考虑相机或投影矩阵)

我确实尝试了将 TBN 作为逆(或转置)传递然后将每个向量与法线相乘的技术。那有同样的效果。无论如何,我宁愿在世界 space 工作,因为显然这对延迟照明更好?或者我听说过。

如果您想查看任何照明代码等,我会添加它,但我认为没有必要,因为它与此不同。

编辑:: 根据要求,这里是顶点和片段着色器的一部分

    #version 330


uniform mat4 T; // Translation matrix
uniform mat4 S; // Scale matrix
uniform mat4 R; // Rotation matrix
uniform mat4 camera; // camera matrix
uniform vec4 posRelParent; // the position relative to the parent


// Input vertex packet
layout (location = 0) in vec4 position;
layout (location = 2) in vec3 normal;
layout (location = 3) in vec4 tangent;
layout (location = 4) in vec4 bitangent;
layout (location = 8) in vec2 textureCoord;


// Output vertex packet
out packet {

    vec2 textureCoord;
    vec3 normal;
    vec3 vert;
    mat3 TBN;
    vec3 tangent;
    vec3 bitangent;
    vec3 normalTBN;

} outputVertex;

mat4 transform;
mat3 TBN;

void calculateTBN()
{
    //get the model matrix, the transform of the object with scaling and transform  removeds
    mat3 model = mat3(transpose(inverse(transform)));

    vec3 T = normalize(model*tangent.xyz);

    vec3 N = normalize(model*normal);

    //I used to retrieve the bitangents by crossing the normal and tangent but now they are calculated independently
    vec3 B = normalize(model*bitangent.xyz);

    TBN = mat3( T , B , N);

    outputVertex.TBN = TBN;

    //Pass though TBN vectors for colour debugging in the fragment shader
    outputVertex.tangent = T;
    outputVertex.bitangent = B;
    outputVertex.normalTBN = N;


}

void main(void) {
    outputVertex.textureCoord = textureCoord;


    // Setup local variable pos in case we want to modify it (since position is constant)
    vec4 pos = vec4(position.x, position.y, position.z, 1.0) + posRelParent;

    //Work out the transform matrix
    transform = T * R * S;

//Work out the normal for lighting
    mat3 normalMat = transpose(inverse(mat3(transform)));

    outputVertex.normal = normalize(normalMat* normal);

    calculateTBN();

    outputVertex.vert =(transform* pos).xyz;

    //Work out the final pos of the vertex
    gl_Position = camera * transform * pos;
    }

片段的光照向量:

vec3 applyLight(Light thisLight, vec3 baseColor, vec3 surfacePos, vec3 surfaceToCamera)
{
    float attenuation = 1.0f;
    vec3 lightPos = (thisLight.finalLightMatrix*thisLight.position).xyz;
    vec3 surfaceToLight;

    vec3 coneDir = normalize(thisLight.coneDirection);

    if (thisLight.position.w == 0.0f)
    {
        //Directional Light (all rays same angle, use position as direction)
        surfaceToLight = normalize( (thisLight.position).xyz);
        attenuation = 1.0f;
    }
    else
    {
        //Point light
        surfaceToLight = normalize(lightPos - surfacePos);


        float distanceToLight = length(lightPos - surfacePos);
        attenuation = 1.0 / (1.0f + thisLight.attenuation * pow(distanceToLight, 2));

        //Work out the Cone restrictions
        float lightToSurfaceAngle = degrees(acos(dot(-surfaceToLight, normalize(coneDir))));

        if (lightToSurfaceAngle > thisLight.coneAngle)
        {
            attenuation = 0.0;
        }
    }

}

这也是碎片着色器的主要部分:

void main(void) {
    //get the base colour from the texture
    vec4 tempFragColor = texture2D(textureImage, inputFragment.textureCoord).rgba;

    //Support for objects with and without a normal map
    if (useNormalMap == 1)
    {  
        calcedNormal = calculateNormal();
    }
    else
    {
        calcedNormal = inputFragment.normal;

    }


    vec3 surfaceToCamera = normalize((cameraPos_World) - (inputFragment.vert));

    vec3 tempColour = vec3(0.0, 0.0, 0.0);

    for (int count = 0; count < numLights; count++)
    {
        tempColour += applyLight(allLights[count], tempFragColor.xyz, inputFragment.vert, surfaceToCamera);
    }


    vec3 gamma = vec3(1.0 / 2.2);

    fragmentColour = vec4(pow(tempColour,gamma), tempFragColor.a);
    //fragmentColour = vec4(calcedNormal, 1);
}

编辑 2:

几何着色器用于通过 TBN 矩阵可视化 "sampled" 法线,如下所示:

void GenerateLineAtVertex(int index)
{
    vec3 testSampledNormal = vec3(0, 0, 1);

    vec3 bitangent = cross(gs_in[index].normal, gs_in[index].tangent);

    mat3 TBN = mat3(gs_in[index].tangent, bitangent, gs_in[index].normal);

    testSampledNormal = TBN * testSampledNormal;


    gl_Position = gl_in[index].gl_Position;

    EmitVertex();

    gl_Position =
         gl_in[index].gl_Position 
        +  vec4(testSampledNormal, 0.0) * MAGNITUDE;


    EmitVertex();


    EndPrimitive();
}

而且是顶点着色器

void main(void) {

// Setup local variable pos in case we want to modify it (since position is constant)
vec4 pos = vec4(position.x, position.y, position.z, 1.0);

mat4 transform = T* R * S;

// Apply transformation to pos and store result in gl_Position
gl_Position = projection* camera* transform * pos;


mat3 normalMatrix = mat3(transpose(inverse(camera * transform)));
vs_out.tangent = normalize(vec3(projection * vec4(normalMatrix * tangent.xyz, 0.0)));
vs_out.normal =  normalize(vec3(projection * vec4(normalMatrix * normal     , 0.0)));
}

这是可视化的 TBN 向量。点上的小角度是由于我如何应用投影矩阵的问题,而不是实际向量中的错误。红线只是显示了我在纹理上绘制的箭头所在的位置,从那个角度看它们不是很清楚。仅此而已。

问题解决了! 实际上与上面的代码无关,尽管感谢所有帮助过的人。

我使用我自己的纹理加载器导入纹理,它默认使用非伽马校正的 32 位 SRGB 颜色。我将它切换为 24 位和 RGB 颜色,它立即工作。典型的开发者问题....