如何修复不正确的 Blinn-Phong 光照

How to fix incorrect Blinn-Phong lighting

我正在尝试在 Vulkan 着色器中为单个光源实现 Blinn-Phong 着色,但我得到的结果不是我所期望的。

输出如下图:

灯光位置应该在相机的右后方,这在图里上是正确的,但在圆圈上却没有。没想到圆圈中间有强度高的点。

灯光位置在坐标(10, 10, 10)。

圆心强度高的点为(0,0,0)。

顶点着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 0) uniform MVP {
    mat4 model;
    mat4 view;
    mat4 proj;
} mvp;

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 3) in vec3 inNormal;

layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
layout(location = 2) out vec3 Normal;
layout(location = 3) out vec3 FragPos;
layout(location = 4) out vec3 viewPos;

void main() {
    gl_Position = mvp.proj * mvp.view * mvp.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
    Normal = inNormal;
    FragPos = inPosition;
    viewPos = vec3(mvp.view[3][0], mvp.view[3][1], mvp.view[3][2]);
}

片段着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 1) uniform sampler2D texSampler;
layout(binding = 2) uniform LightUBO{
    vec3 position;
    vec3 color;
} Light;

layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 2) in vec3 Normal;
layout(location = 3) in vec3 FragPos;
layout(location = 4) in vec3 viewPos;

layout(location = 0) out vec4 outColor;

void main() {
    vec3 color = texture(texSampler, fragTexCoord).rgb;
    // ambient
    vec3 ambient = 0.2 * color;

    // diffuse
    vec3 lightDir = normalize(Light.lightPos - FragPos);
    vec3 normal = normalize(Normal);
    float diff = max(dot(lightDir, normal), 0.0);
    vec3 diffuse = diff * color;

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = 0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);  
    spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);

    vec3 specular = vec3(0.25) * spec;
    outColor = vec4(ambient + diffuse + specular, 1.0);
}

注意: 我正在尝试将 this 教程中的着色器实现到 Vulkan 中。

这似乎只是使用正确坐标系的问题。由于您的问题缺少一些重要信息,因此我将不得不做出一些假设。首先,基于你有一个模型矩阵并且场景中显然有多个对象这一事实,我会假设你的世界 space 和对象 space 通常是不一样的。此外,我假设您的 model 矩阵从对象 space 转换为世界 space,您的 view 矩阵从世界 space 转换为视图 space 并且您的 proj 矩阵从视图 space 转换为剪辑 space。我还将假设您的 inPositioninNormal 属性位于对象 space 坐标中。

基于所有这些,您的 viewPos 只是采用视图矩阵的最后一列,它将 包含世界中的相机位置 space.最后一行也不会。视图矩阵从世界 space 转换为视图 space。它的最后一列对应于从相机的角度来看指向世界 space 原点的矢量。您的 FragPosNormal 将在对象 space 中。而且,根据你在问题中所说的,你的灯光位置在世界 space 中。所以最后,您只是将相对于完全不同的坐标系的坐标混合在一起。例如:

vec3 lightDir = normalize(Light.lightPos - FragPos);

在这里,您要从世界 space 位置减去对象 space 位置,这将产生一个完全没有意义的结果。然后将这个无意义的结果归一化并用对象-space方向

点缀
float diff = max(dot(lightDir, normal), 0.0);

此外,即使 viewPos 是世界-space 相机位置,这个

vec3 viewDir = normalize(viewPos - FragPos);

仍然没有意义,因为 FragPos 是在对象-space 坐标中给出的。

只有当涉及的所有向量都相对于同一坐标系时,对坐标向量的操作才有意义。选择哪个坐标系并不重要。但是你必须选择一个。确保你所有的向量实际上都是相对于那个坐标系的,例如 world space。如果某些向量恰好不在该坐标系中,则必须将它们转换到该坐标系中。只有当你所有的向量都在同一个坐标系中,你的着色计算才会有意义......

要获得 viewPos,您可以获取逆视图矩阵的最后一列(如果您出于某种原因碰巧已经在某处拥有它),或者只是将相机位置作为附加制服传递。此外,与其一次又一次地乘以模型视图和投影矩阵,不如为每个顶点一次,考虑将组合的模型-视图-投影矩阵传递给着色器……

除此之外:请注意,如果表面实际上朝向光线,您很可能只想拥有镜面反射分量。