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

标签 c++ glsl shader vulkan light

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

输出如下所示: Incorrect lighting

灯光位置应在相机右侧的后面,这在图里上正确表示,但在圆圈上却没有。我不希望在圆圈中间有高强度点。

灯光位置在坐标 (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 教程。

最佳答案

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

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

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

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

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

此外,即使 viewPos 是世界空间相机位置,这

vec3 viewDir = normalize(viewPos - FragPos);

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

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

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

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

关于c++ - 如何修复不正确的 Blinn-Phong 光照,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55443308/

相关文章:

c++ - 将 Gravity(一种脚本语言)调用转换为 native C 函数调用

c++ - 将 HLSL 像素着色器应用于 Win32 屏幕捕获

c++ - 对 OpenGL 中着色器的行为感到困惑 - 切换声明会导致错误和崩溃

c++ - 对 vector push_back 的使用

c++ - 平台 SDK 6.1 中的语言代码

c++ - 将 char 的值传递给 string 以用于比较

c++ - OpenGL 着色器问题(不显示任何内容)

Opengl双溢出

javascript - 启动一些顶点/三 Angular 形供顶点着色器使用

ios - 从 Unity 4.x 升级后,Unity 5 自定义着色器损坏