I apologize in advance for not providing a minimal working example but maybe for someone the information I will provide will be enough. I have the following problem with my BRDF from learnopengl.com.
As you can see the shading is quite off, the plane has a wave like shadow(the plane is centered at the origin and is facing the positive z direction), also if the light source is too close to the plane a weird normal map like spots start to appear:
All shadow calculations are disabled for debugging purposes. All my light calculations AFAIK are in world space. Here are the shaders for Geomeotry and lighting passes:
Geometry.vert:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBiTangent;
out VS_OUT
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
mat3 TBN;
} vs_out;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.Normal = normalMatrix * aNormal;
gl_Position = projection * view * vec4(vs_out.FragPos, 1.0);
vs_out.TexCoords = aTexCoords;
vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
vec3 T = normalize(normalMatrix * aTangent);
vec3 N = normalize(normalMatrix * aNormal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
vs_out.TBN = mat3(T, B, N);
}
Geometry.frag:
#version 330 core
layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec4 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
layout (location = 3) out vec4 gLightSpaceFrag;
layout (location = 4) out vec4 gRMA;
struct Material {
sampler2D texture_diffuse;
sampler2D texture_specular;
sampler2D texture_normal;
sampler2D texture_ao;
sampler2D texture_metallic;
sampler2D texture_roughness;
sampler2D texture_displacement;
float shininess;
};
in VS_OUT
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
mat3 TBN;
} fs_in;
uniform Material material;
void main()
{
vec3 norm = texture(material.texture_normal, fs_in.TexCoords).rgb;
norm = norm * 2.0 - 1.0;
norm = normalize(fs_in.TBN * norm);
gNormal = vec4(norm, 1.0f);
gPosition = vec4(fs_in.FragPos, 1.0f);
gAlbedoSpec.rgb = texture(material.texture_diffuse, fs_in.TexCoords).rgb;
gAlbedoSpec.a = texture(material.texture_specular, fs_in.TexCoords).r;
gLightSpaceFrag = fs_in.FragPosLightSpace;
gRMA.r = texture(material.texture_roughness, fs_in.TexCoords).r;
gRMA.g = texture(material.texture_metallic, fs_in.TexCoords).r;
gRMA.b = texture(material.texture_ao, fs_in.TexCoords).r;
}
Lighting.vert:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
And Lighting.frag(only the relevant parts, all functions used in the reflectance function are directly from learnopengl.com):
vec3 calculateReflectance(vec3 F0, vec3 WorldPos, vec3 LightPosition, vec3 LightColor, vec3 N, vec3 V)
{
vec3 L = normalize(LightPosition - WorldPos);
vec3 H = normalize(V + L);
float distance = length(LightPosition - WorldPos);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = LightColor * attenuation;
// Cook-Torrance BRDF
float NDF = DistributionGGX(N, H, material.Roughness);
float G = GeometrySmith(N, V, L, material.Roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
vec3 specular = numerator / denominator;
// kS is equal to Fresnel
vec3 kS = F;
// for energy conservation, the diffuse and specular light can't
// be above 1.0 (unless the surface emits light); to preserve this
// relationship the diffuse component (kD) should equal 1.0 - kS.
vec3 kD = vec3(1.0) - kS;
// multiply kD by the inverse metalness such that only non-metals
// have diffuse lighting, or a linear blend if partly metal (pure metals
// have no diffuse light).
kD *= 1.0 - material.Metallic;
// scale light by NdotL
float NdotL = max(dot(N, L), 0.0);
// add to outgoing radiance Lo
// note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
vec3 Lo = (kD * material.Albedo / PI + specular) * radiance * NdotL;
return Lo;
}
void main()
{
fetchMaterial(TexCoords);
vec3 WorldPos = texture(gPosition, TexCoords).rgb;
vec4 FragPosLightSpace = texture(gLightSpaceFrag, TexCoords);
vec3 N = material.Normal;
vec3 V = normalize(viewPos - WorldPos);
vec3 F0 = vec3(0.04);
F0 = mix(F0, material.Albedo, material.Metallic);
vec3 result = vec3(0.f,0.f,0.f);
for(int i = 0; i < NUMBER_OF_POINT_LIGHTS; i++)
result += CalcPointLight(F0, pointLights[i], N, WorldPos, V);
vec3 ambient = vec3(0.03) * material.Albedo * material.AO;
result += ambient;
const float gamma = 2.2;
vec3 acesColor = aces(result);
// gamma correction
vec3 gammaCorrected = pow(acesColor, vec3(1.0 / gamma));
FragColor = vec4(gammaCorrected, 1.0);
}
vec3 CalcPointLight(vec3 F0, PointLight light, vec3 N, vec3 WorldPos, vec3 V)
{
// combine results
//vec3 calculateReflectance(vec3 F0, vec3 WorldPos, vec3 LightPosition, vec3 LightColor, vec3 N, vec3 V)
vec3 Lo = calculateReflectance(F0, WorldPos, light.position, light.diffuse, N, V);
return Lo;
}