法线贴图问题

Issue with normal mapping

我正在努力实现法线贴图(使用 Swift/Metal)。但是,我想我在计算切线和副切线时出错了,我找不到问题。

这是法线贴图的截图: basic shading with normal map & basic shading, normal mapping normals

这里是仅使用 obj 文件提供的法线的屏幕截图: basic shading without normal map, normals.

这里是计算切线和副切线的代码:


func calculateTangentAndBitangent(v1: VertexData, v2: VertexData, v3: VertexData) -> (vec3, vec3) {
        let pos1 = v1.position
        let pos2 = v2.position
        let pos3 = v3.position
        
        let uv1 = v1.uv
        let uv2 = v2.uv
        let uv3 = v3.uv
        
        let e1 = pos2 - pos1
        let e2 = pos3 - pos1
        let x1 = uv2.x - uv1.x
        let x2 = uv3.x - uv1.x
        let y1 = uv2.y - uv1.y
        let y2 = uv3.y - uv1.y
        
        let r = 1.0 / (x1 * y2 - x2 * y1)
        let tangent = (e1 * y2 - e2 * y1) * r
        let bitangent = (e2 * x1 - e1 * x2) * r
        
        return (tangent, bitangent)
}

for i in stride(from: 0, to: vertexBuffer.count, by: 3) {
            let v1 = vertexBuffer[i]
            let v2 = vertexBuffer[i+1]
            let v3 = vertexBuffer[i+2]
            let tb = calculateTangentAndBitangent(v1: v1, v2: v2, v3: v3)
            
            tangents[normalIndices[i]] += tb.0
            tangents[normalIndices[i+1]] += tb.0
            tangents[normalIndices[i+2]] += tb.0
            bitangents[normalIndices[i]] += tb.1
            bitangents[normalIndices[i+1]] += tb.1
            bitangents[normalIndices[i+2]] += tb.1
}
for i in 0..<vertexBuffer.count {
            var v = vertexBuffer[i]
            
            v.tangent = normalize(tangents[normalIndices[i]])
            v.bitangent = normalize(bitangents[normalIndices[i]])
            
            vertices.append(v.vertex)
}

顶点着色器:

    out.tangent = normalize((modelUniforms.modelMatrix * float4(in.tangent, 0)).xyz);
    out.bitangent = normalize((modelUniforms.modelMatrix * float4(in.bitangent, 0)).xyz);
    out.normal = normalize((modelUniforms.modelMatrix * float4(in.normal, 0)).xyz);

片段着色器:

constexpr sampler linearSampler(mip_filter::linear,
                                     mag_filter::linear,
                                     min_filter::linear);
    
    float3 normalSample   = float3(normalMap.sample(linearSampler, in.texCoord).xyz);
    
    normalSample = normalize(normalSample * 2 - 1);
    
    float3 normal = normalize(in.tangent * normalSample.x +
                                   in.bitangent * normalSample.y +
                                   in.normal * normalSample.z);
    
    float3 L = normalize(sceneUniforms.sun.direction);
    float3 NdL = max(dot(in.normal, L), 0.0);
    half3 diff = half3(NdL);

我花了很多时间研究这个问题,但我仍然无法弄清楚问题是什么。

如果您使用 MTKTextureLoader 加载纹理,请确保在加载法线贴图时指定 MTKTextureLoader.Option.SRGB: false,以便图像数据被视为线性像素数据,否则法线将全部错误。

https://developer.apple.com/documentation/metalkit/mtktextureloader/option/1536032-srgb