In my Direct Light Physically Based Renderer, when I set the roughness too low (ie. 0.0 or close) the specular reflection doesn't show, not even at high resolutions and at perfect angle with the camera / light position.

I'm using Cook-Torrance's BRDF with Schlick Smith GGX Geometry Function, Shlick Fresnel Function and Trowbridge-Reitz GGX Normal Distribution Function.

I suspect the whole reason behind this is because when the roughness is 0.0, the result of the Normal Distribution Function is also 0.0 and, consequently, the specular part will also be null.

// Trowbridge-Reitz GGX Normal Distribution Function
float NormalDistributionFunction(vec3 N, vec3 H, float roughness) {
    float a = roughness * roughness;
    float aSquare = a * a;
    float NdotH = max(dot(N, H), 0.0);
    float NdotHSquare = NdotH * NdotH;

    float numerator = aSquare;
    float denominator = (NdotHSquare * (aSquare - 1.0) + 1.0);
    denominator = PI * denominator * denominator;

    return numerator / denominator;

// Cook-Torrance Bidirectional Reflective Distribution Function
vec3 BRDF(vec3 L, vec3 V, vec3 N, vec3 radiance, vec4 albedo, float metallic, float roughness, vec3 F0) {
    vec3 H = normalize (V + L);

    float D = NormalDistributionFunction(N, H, roughness);   
    float G = GeometryFunction(N, V, L, roughness);      
    vec3  F = FresnelFunction(max(dot(H, V), 0.0), F0);

    vec3 numerator = D * G * F; 
    float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001; // Prevent division by zero.
    vec3 specular = numerator / denominator;

    vec3 kS = F;
    vec3 kD = vec3(1.0) - kS;
    kD *= 1.0 - metallic;     

    float NdotL = max(dot(N, L), 0.0);        

    vec3 color = (kD * albedo.rgb / PI + specular) * radiance * NdotL;
    return color;

enter image description here

Does this physically make sense?
The only workaround I see is either not using a perfect mirror value of 0.0 roughness or always clamp the minimum value to something like 0.04.


1 Answer 1


Yes, I think it's expected that setting roughness = 0, combined with using point lights for illumination, leads to no visible specular highlight. The size of the highlight is infinitesimally small, so the sample points (e.g. pixel centers) almost surely miss it. The math breaks down as well, as the reflectance would become infinite on the zero-sized highlight (a delta function).

Clamping roughness to a minimum seems like a physically reasonable thing to do. 0.04 feels like too large a minimum, though; I might pick 0.001 or 0.0001 or something like that.

If you use area lights, then a perfect mirror surface (roughness = 0) could still work and would show the sharp reflected image of the area light. This would probably still require special-casing the perfect-mirror case in shading code, to avoid the problems with infinities.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.