<p>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).</p>

<p>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.</p>

<p>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.</p>

CC BY-SA 4.0
2019-09-07T18:17:32.527Z
2019-09-07T18:17:32.527Z
Nathan Reed
<p>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.</p>

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

<p>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.</p>

<pre><code>// 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;
}
</code></pre>

<p><a href="https://i.sstatic.net/zDaPU.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/zDaPU.png" alt="enter image description here"></a></p>

<p>Does this physically make sense?<br>
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.</p>

<lighting><brdf><physically-based><pbr>
Roughness Value of (Close to) Zero in Physically Based Rendering
Daniel Marques
# Roughness Value of (Close to) Zero in Physically Based Rendering

(Asked by: username_0 on 2019-09-06)

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](https://i.sstatic.net/zDaPU.png)](https://i.sstatic.net/zDaPU.png)

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.

**Tags:** lighting, brdf, physically-based, pbr


---

## 1 answers

### Answer by username_1 on 2019-09-07. Score: 7 | Accepted answer

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.


---

