I'm currently attempting to implement BRDF lighting, and am hitting a bit of a snag with my specular term - the specular highlights aren't rendering correctly.
To make things simple, I'm using a single directional light rendering down the Z axis only, and manually specify the gloss/specularity of each of the following spheres.
Glossyness increases left to right, and Specularity increases bottom to top. Notice how the highlights seem shifted nearly 90 degrees upwards:
Calculating the specular BRDF
I use the generic Fresnel Shlick eq:
For the NDF, I use the GGX Trowbridge-Reitz eq:
And for the Geometric Shadowing function, I use the Smith / Shlick-Beckmann eq:
vec3 BRDF_Specular(vec3 F0, vec3 1minusF0, vec3 WorldPos, vec3 Normal)
{
vec3 LightDirection = vec3(0, 0, -1);
vec3 ViewDirection = normalize(EyePos - WorldPos);
vec3 HalfVec = normalize(LightDirection + ViewDirection);
float NdotL = clamp(dot(Normal, LightDirection), 0.0, 1.0);
float NdotV = clamp(dot(Normal, ViewDirection), 0.0, 1.0);
float NdotH = clamp(dot(Normal, HalfVec), 0.0, 1.0);
float VdotH = clamp(dot(ViewDirection, HalfVec), 0.0, 1.0);
float Roughness = max(1.0f - Gloss, 0.0); // Turn into roughness
float Roughness_2 = (Roughness * Roughness);
float NdotH_2 = NdotH * NdotH;
/* Normal Distribution Function
GGX (Trowbridge-Reitz) */
float D0 = (NdotH_2 * (a_2 - 1.0f) + 1.0f);
float D = a_2 / (M_PI * (D0 * D0));
/* Fresnel
Shlick */
vec3 Fs = F0 + 1minusF0 * pow(1.0 - VdotH, 5.0);
/* Geometric Shadowing
Smith / Shlick-Beckmann */
float k = a_2 / sqrt(2.0 / M_PI);
float GV = NdotV / (NdotV * (1.0 - k) + k);
float GL = NdotL / (NdotL * (1.0 - k) + k);
float G = GV * GL;
vec3 brdfspec = vec3(max(( Fs * D * G ) / (4.0f * NdotL * NdotV), 0.0));
return brdfspec;
}
I've been following so many different resources and mimicked many other implementations, and even reducing it down to this basic level I'm still having this issue. I don't know why I'm not getting proper results
I'm using a deferred renderer, I derive the World position of each fragment from the depth buffer, and store my normals in viewspace. I don't use a "Metalness" map, but instead use a single rgb specular map, hense why my Fresnel calculation uses Vec3's instead of floats.