Hey,
I'm playing around with the IBL technique described by Epic in their 2012 Siggraph Paper: http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
I've seen quite a few topics on this paper around Gamedev, but none that address this particular issue.
The problem I'm having is that my roughness factor effectively blurs the highlights around but doesn't seem to diminish them. I would expect that by integrating over the hemisphere with a wider lobe, I would see less energy reflected towards the eye, but it appears that this is not the case--as you can see in the attached image.
The code is pretty much straight from the paper, although I'll pulled the GGX and G_Smith terms from elsewhere on these forums:
float GGX(float nDotV, float a) {
float aa = a*a;
float oneMinusAa = 1 - aa;
float nDotV2 = 2 * nDotV;
float root = aa + oneMinusAa * nDotV * nDotV;
return nDotV2 / (nDotV + sqrt(root));
}
float G_Smith(float a, float nDotV, float nDotL) {
return GGX(nDotL,a) * GGX(nDotV,a);
}
vec3 ImportanceSampleGGX( vec2 Xi, float Roughness, vec3 N ) {
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
vec3 SpecularIBL( vec3 SpecularColor, float Roughness, vec3 N, vec3 V ) {
vec3 SpecularLighting = vec3(0);
for( int i = 0; i < u_NumSamples; i++ ) {
vec2 Xi = vec2(u_Rand[i*2], u_Rand[i*2+1]);
vec3 H = ImportanceSampleGGX( Xi, Roughness, N );
vec3 L = 2 * dot( V, H ) * H - V;
float NoV = clamp( dot( N, V ), 0, 1 );
float NoL = clamp( dot( N, L ), 0, 1 );
float NoH = clamp( dot( N, H ), 0, 1 );
float VoH = clamp( dot( V, H ), 0, 1 );
if( NoL > 0 ) {
vec3 SampleColor = pow(texture( u_skybox, L, 0 ).rgb, vec3(2.2));
float G = G_Smith( Roughness, NoV, NoL );
float Fc = pow( 1 - VoH, 5 );
vec3 F = (1 - Fc) * SpecularColor + Fc;
// Incident light = SampleColor * NoL
// Microfacet specular = D*G*F / (4*NoL*NoV)
// pdf = D * NoH / (4 * VoH)
SpecularLighting += SampleColor * F * G * VoH / (NoH * NoV);
}
}
return SpecularLighting / float(u_NumSamples);
}
void main() {
vec3 V = normalize(u_eyePos - v_worldPos);
vec3 N = normalize(v_normal);
FragColor = vec4( SpecularIBL(vec3(1.0, 0.6, 0.6), u_Roughness, N, V), 1.0);
}
Looking at Disney's BRDF exporer, I see that the GGX BRDF correctly scales back the lobe based on roughness, using the D (distribution) part of the microfacet model. But within the comments, I see that the PDF of the GGX importance sampling factors the D term in, which cancels it out of the brdf.
As a test, I clamped the HDR probe to a max value of 1, which really shows how the conservation is out of whack. I assume I'm missing something in my BRDF to account for this, but the weird integration with the importance sampling distribution is making it difficult for me to piece apart what's going on.
Based on my searches, there are several members here who have successfully implemented this approach. Is there some step I'm blatantly missing? Note that I'm just doing the full importance sampling approach, not the cube map approximation.
Thanks!
Zach.