如何修复不正确的 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。我还将假设您的 inPosition
和 inNormal
属性位于对象 space 坐标中。
基于所有这些,您的 viewPos
只是采用视图矩阵的最后一列,它将 不 包含世界中的相机位置 space.最后一行也不会。视图矩阵从世界 space 转换为视图 space。它的最后一列对应于从相机的角度来看指向世界 space 原点的矢量。您的 FragPos
和 Normal
将在对象 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
,您可以获取逆视图矩阵的最后一列(如果您出于某种原因碰巧已经在某处拥有它),或者只是将相机位置作为附加制服传递。此外,与其一次又一次地乘以模型视图和投影矩阵,不如为每个顶点一次,考虑将组合的模型-视图-投影矩阵传递给着色器……
除此之外:请注意,如果表面实际上朝向光线,您很可能只想拥有镜面反射分量。
我正在尝试在 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。我还将假设您的 inPosition
和 inNormal
属性位于对象 space 坐标中。
基于所有这些,您的 viewPos
只是采用视图矩阵的最后一列,它将 不 包含世界中的相机位置 space.最后一行也不会。视图矩阵从世界 space 转换为视图 space。它的最后一列对应于从相机的角度来看指向世界 space 原点的矢量。您的 FragPos
和 Normal
将在对象 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
,您可以获取逆视图矩阵的最后一列(如果您出于某种原因碰巧已经在某处拥有它),或者只是将相机位置作为附加制服传递。此外,与其一次又一次地乘以模型视图和投影矩阵,不如为每个顶点一次,考虑将组合的模型-视图-投影矩阵传递给着色器……
除此之外:请注意,如果表面实际上朝向光线,您很可能只想拥有镜面反射分量。