法线贴图问题
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
我正在努力实现法线贴图(使用 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