It's probably something obvious but there seems to be a strange issue with lighting in my pipeline. I'm trying to keep everything in world space at the moment for simplicity but cannot figure out what's going on here. Take a look at my code:
Geometry pass:
helmet.vert
#version 410 core
layout (location = 0) in vec4 aPos;
layout (location = 1) in vec4 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;
out vec3 FragPos;
out vec2 TexCoords;
out vec3 Normal;
out vec3 Tangent;
out vec3 Binormal;
void main() {
vec4 worldPos = model * aPos;
FragPos = worldPos.xyz;
TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
Normal = normalize(normalMatrix * aNormal.xyz);
Tangent = normalize(normalMatrix * aTangent);
Binormal = normalize(normalMatrix * aBitangent);
gl_Position = projection * view * worldPos;
}
helmet.frag
#version 410 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gColor;
layout (location = 3) out vec4 gSpecular;
layout (location = 4) out vec4 gEmissive;
in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;
in vec3 Tangent;
in vec3 Binormal;
// Textures passed from assimp
uniform sampler2D albedo;
uniform sampler2D material;
uniform sampler2D normalmap;
uniform sampler2D ambient;
uniform sampler2D emissive;
void main() {
// world space model fragment position
gPosition = FragPos;
// world space normals translated from tangent space
mat3 tangentToWorld = mat3(Tangent, Binormal, Normal);
gNormal = tangentToWorld * (texture(normalmap, TexCoords).rgb * 2.0 - 1.0);
// diffuse per-fragment color
gColor = texture(albedo, TexCoords);
// Beyond here is material properties
gSpecular = vec4(
texture(material, TexCoords).rgb,
texture(ambient, TexCoords).a
);
gEmissive = texture(emissive, TexCoords);
}
Lighting pass:
pointlight.vert
#version 410 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main() {
// rendering onto a framebuffer quad
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
pointlight.frag
#version 410 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gColor;
uniform sampler2D gSpecular;
// light position
uniform vec3 position;
// phong (lambertian) diffuse term
float phong_diffuse() {
return (1.0 / PI);
}
// compute fresnel specular factor for given base specular and product
vec3 fresnel_factor(in vec3 f0, in float product) {
return f0 + (1.01 - f0)*pow(1.01 - product, 5.0);
}
float D_GGX(in float roughness, in float NdH) {
float m = roughness * roughness;
float m2 = m * m;
float d = (NdH * m2 - NdH) * NdH + 1.0;
return m2 / (PI * d * d);
}
float G_schlick(in float roughness, in float NdV, in float NdL) {
float k = roughness * roughness * 0.5;
float V = NdV * (1.0 - k) + k;
float L = NdL * (1.0 - k) + k;
return 0.25 / (V * L);
}
// cook-torrance specular calculation
vec3 cooktorrance_specular(in float NdL, in float NdV, in float NdH, in vec3 specular, in float roughness) {
float D = D_GGX(roughness, NdH);
float G = G_schlick(roughness, NdV, NdL);
float rim = mix(1.0 - roughness * texture(gSpecular, TexCoords).w * 0.9, 1.0, NdV);
return (1.0 / rim) * specular * G * D;
}
void main() {
// World space model vertex
vec3 v_pos = texture(gPosition, TexCoords).rgb;
// light attenuation
float A = 20.0 / dot(light_position - v_pos, light_position - v_pos);
// L, V, H, N vectors
vec3 L = normalize(light_position - v_pos);
vec3 V = normalize(-v_pos);
vec3 H = normalize(L + V);
vec3 N = normalize(texture(gNormal, TexCoords).rgb);
// albedo/specular base
vec3 base = texture(gColor, TexCoords).xyz;
// roughness
float roughness = texture(gSpecular, TexCoords).y;
// metallic
float metallic = texture(gSpecular, TexCoords).x;
// mix between metal and non-metal material, for non-metal
// constant base specular factor of 0.04 grey is used
vec3 specular = mix(vec3(0.04), base, metallic);
// compute material reflectance
float NdL = max(0.0 , dot(N, L));
float NdV = max(0.001, dot(N, V));
float NdH = max(0.001, dot(N, H));
float HdV = max(0.001, dot(H, V));
float LdV = max(0.001, dot(L, V));
// specular reflectance with COOK-TORRANCE
vec3 specfresnel = fresnel_factor(specular, HdV);
vec3 specref = cooktorrance_specular(NdL, NdV, NdH, specfresnel, roughness);
specref *= vec3(NdL);
vec3 diffref = (vec3(1.0) - specfresnel) * phong_diffuse() * NdL;
// compute lighting
vec3 reflected_light = vec3(0);
vec3 diffuse_light = vec3(0);
// point light
vec3 light_color = vec3(1.0) * A;
reflected_light += specref * light_color;
diffuse_light += diffref * light_color;
// final result
vec3 result =
diffuse_light * mix(base, vec3(0.0), metallic) +
reflected_light;
color = vec4(result, 1);
}
This is before IBL is applied but I don't think that will make a difference here. Anyways, here's the weird result I'm getting (keep in mind the camera is the same as the point light position):
For reference I'm using this model https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet . Thanks to anyone who can help

