我正在使用tinygltf 编写一个小型Vulkan 玩具渲染器来加载我的模型。 在过去的两周里,我一直在尝试在渲染器中实现法线贴图,但遇到了问题。
我想我将其范围缩小到了切线/TBN 矩阵计算,但此时它可以是任何东西。 我从 glTF 编辑器上的示例模型中检索了所有模型(由于信誉太低而无法链接)。正如您在附图中看到的,当使用采样和 TBN 转换后的法线时,模型中间有一条大接缝。 为了确保这不是模型本身的问题,我尝试了以下操作:
有些模型没有切线,所以我进入 Blender 并导出带有切线的模型,但接缝仍然存在。
然后,我在导入模型时尝试使用此处提供的说明自行计算切线和副切线(第 5 页,清单 7.4)。但即使这样也没有解决我的问题,导入的切线和计算的切线是完全相同的。
我尝试过的事情:
顶点着色器:
void main()
{
vec4 worldPos = primitive.model * vec4(inPosition, 1.0f);
gl_Position = ubo.viewProjection * worldPos;
mat3 modelM3 = transpose(inverse(mat3(primitive.model)));
vec3 T = normalize(modelM3 * inTangent.xyz);
vec3 N = normalize(modelM3 * inNormal);
vec3 B = normalize(cross(N, T)) * inTangent.w;
// Transposed matrix to transform light and view vectors from world to tangent space.
mat3 TBN = mat3(
T.x, B.x, N.x,
T.y, B.y, N.y,
T.z, B.z, N.z
);
vs_out.normal = N;
vs_out.tangent = vec4(T, inTangent.w);
vs_out.bitangent = normalize(modelM3 * inBitangent);
vs_out.texcoord = inTexcoord;
vs_out.lightDir = ubo.globalLightDirection.xyz * TBN;
vs_out.viewDir = (worldPos.xyz - ubo.cameraPos.xyz) * TBN;
}
片段着色器:
vec3 calculate_diffuse(vec3 color, vec3 normal)
{
float diffuse_strength = dot(normal, -normalize(fs_in.lightDir));
// HALF-LAMBERT
diffuse_strength = diffuse_strength * 0.5f + 0.5f;
diffuse_strength = clamp(diffuse_strength, 0.f, 1.f);
return color * diffuse_strength;
}
void main()
{
vec4 color = texture(samplerColorMap, fs_in.texcoord);
if (ALPHA_MASK) {
if (color.a < ALPHA_MASK_CUTOFF) {
discard;
}
}
vec3 localNormal = 2.f * texture(samplerNormalMap, fs_in.texcoord).rgb - 1.f;
vec3 normal = normalize(localNormal);
vec3 diffuse = calculate_diffuse(color.rgb, normal);
outFragColor = vec4(diffuse, color.a);
}
顶点着色器:
void main()
{
vec4 worldPos = primitive.model * vec4(inPosition, 1.0f);
gl_Position = ubo.viewProjection * worldPos;
vs_out.normal = inNormal;
vs_out.tangent = inTangent;
vs_out.bitangent = inBitangent;
vs_out.texcoord = inTexcoord;
vs_out.lightDir = ubo.globalLightDirection.xyz;
vs_out.viewDir = (worldPos.xyz - ubo.cameraPos.xyz);
vec3 T = normalize(mat3(primitive.model) * inTangent.xyz);
vec3 B = normalize(mat3(primitive.model) * inBitangent);
vec3 N = normalize(mat3(primitive.model) * inNormal);
vs_out.TBN = mat3(T, B, N);
}
片段着色器:
vec3 calculate_diffuse(vec3 color, vec3 normal)
{
float diffuse_strength = dot(normal, -normalize(fs_in.lightDir));
// HALF-LAMBERT
diffuse_strength = diffuse_strength * 0.5f + 0.5f;
diffuse_strength = clamp(diffuse_strength, 0.f, 1.f);
return color * diffuse_strength;
}
void main()
{
vec4 color = texture(samplerColorMap, fs_in.texcoord);
if (ALPHA_MASK) {
if (color.a < ALPHA_MASK_CUTOFF) {
discard;
}
}
vec3 localNormal = 2.f * texture(samplerNormalMap, fs_in.texcoord).rgb - 1.f;
vec3 normal = normalize(localNormal * fs_in.TBN);
vec3 diffuse = calculate_diffuse(color.rgb, normal);
outFragColor = vec4(diffuse, color.a);
}
我的 github 存储库可以在这里找到 导入网格并在 coral_mesh::Builder::load_from_gltf() 中计算切线,并且可以在着色器文件夹中找到着色器
提前非常感谢!
图片:
所以,问题在于普通纹理没有我想象的格式。普通纹理应该具有 VkFormat“VK_FORMAT_R8G8B8_UNORM”,这是我提供给纹理抽象构造函数的。但是,构造函数从未使用此提供的值,而是使用“VK_FORMAT_R8G8B8_SRGB”初始化所有纹理。 解决这个问题,修复所有接缝。 这是经典的图像视图格式问题困扰着我和许多其他图形程序员。