Advertisement Jump to content
Sign in to follow this  

Bug: Overly Sharp Highlights in PBR

This topic is 860 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey guys! I'm still working on my BRDF equations. Theoretically, all equations are correct, but the hilights are super sharp. I'm not sure if this is a result of  me not using Image-Based Lighting (I currently use just an env map with a LOD function). I'm using materials from here: - I use Specular/Gloss but the materials use the fragment shader to convert it.




Material Frag Shader (Excerpt) (Specifically, the Metalness-Roughness shader):

float Roughness = baseSpecular.a;
if (uTexRoughEnabled == 1)
	Roughness = texture(texRoughness, UV).r;

float Metalness = texture(texSpecular, UV).r;

// 1 - Roughness becomes Glossiness
specular = vec4(mix(vec3(0.4), albedo.rgb, Metalness), 1-Roughness);
albedo = vec4(mix(albedo.rgb, vec3(0), Metalness), 1-Roughness);

Lighting Functions:

vec3 Light_F(in vec3 Specular, in float VH) {
	// Fresnel Schlick
	return Specular + (1-Specular) * pow(1-VH, 5.0f);

float Light_D(in float alpha, in float NH) {
	// GGX
	float alphaSqr = alpha*alpha;
	float denom = NH * NH * (alphaSqr - 1) + 1;

	return alphaSqr / (pi * denom * denom);

float Light_V( in float NL, in float NV, in float alpha ) {
	// Frostbite's GGX
	float Lambda_GGXV = NL * sqrt (( - NV * alpha + NV ) * NV + alpha );
	float Lambda_GGXL = NV * sqrt (( - NL * alpha + NL ) * NL + alpha );

	return 0.5f / ( Lambda_GGXV + Lambda_GGXL );

vec3 Light_Diffuse(in float NV, in float NL, in vec3 Normal, in float alpha, in vec3 Albedo, in vec3 eyeDir, in vec3 lightDir) {
        // Oren-Nayar
        float gamma = dot( eyeDir - Normal * NV, lightDir - Normal * NL);
        float A = 1.0f - 0.5f * (alpha / (alpha + 0.57f));
        float B = 0.45f * (alpha / (alpha + 0.09));
	float diffAlpha = max( acos( NV ), acos( NL ) );
	float diffBeta  = min( acos( NV ), acos( NL ) );
	float C = sin(diffAlpha) * tan(diffBeta);
	return Albedo * (A + B * max( 0.0f, gamma ) * C) / pi;

Final lighting function in deferred renderer:

vec3 LightCalc(in vec3 Albedo, in vec4 WorldPos, in vec4 Specular, in vec3 Normal, in samplerCube envMap, in vec3 lightPos, in float lightAtten, in vec4 lightColor) {
	vec3 lightDir	= - lightPos;
	vec3 eyeDir	= normalize(eyePos -;
	vec3 eyeReflect = reflect(-eyeDir, Normal);
	float Distance	= length(lightDir);
	lightDir		= -normalize(lightDir);
	float Attenuation = clamp(1 - pow(Distance / lightAtten, 4), 0, 1);
	Attenuation = Attenuation*Attenuation/(Distance*Distance+1);
	vec3 AmbientColor = vec3(*0.01;
	float Roughness = 1-Specular.a;
	float alpha = Roughness * Roughness;
	vec3 H = normalize(eyeDir + lightDir);
	float NL = clamp(dot(Normal, lightDir), 0, 1);
	float NH = clamp(dot(Normal, H), 0, 1);
	float NV = clamp(dot(Normal, eyeDir), 0, 1);
	float LH = clamp(dot(lightDir, H), 0, 1);
	float VH = clamp(dot(eyeDir, H), 0, 1);
	float lod = compute_lod(alpha, 1, NH);
	vec3 reflectDir = normalize(eyeReflect);
	vec3 reflectPix = textureLod(envMap, reflectDir, lod).rgb;
	float D = Light_D(alpha, NH);
	vec3 F = Light_F(clamp(Specular.rgb, 0.018, 0.95), LH);
	float Vis = Light_V(NL, NV, alpha);
	vec3 Spec = (D * F * Vis) * reflectPix;
	vec3 Diffuse = Light_Diffuse(NV, NL, Normal, alpha, Albedo.rgb, eyeDir, lightDir);

	vec3 lightModifier =*lightColor.w*Attenuation;
	return NL*(Spec + max(Diffuse, AmbientColor))*lightModifier;

Feel free to point out any mistakes I made, even if not related to the issue. I've tested the materials in Unreal, and they looked correct. I doubt this is solely due to the lack of Image-Based Lighting. I also doubt this is an issue with using point lighting. Additionally, if I turn down the roughness by a lot, it becomes too dark a high roughness to be the issue.

Share this post

Link to post
Share on other sites

Would using swapping to 1-Smoothness^4 and 0.16*Specular^2 help? This was suggested in the Frostbite papers, but it might be specific to them. I've tried it, and it just lowers the reflections on the nonmetals a bit, plus widens the reflection on the metals. I'm not sure if this is physically accurate. Someone suggested I remove the final NL in the return, but without it, it's bright all over. NL is in neither the Spec or Diffuse, I moved it to optimize a bit.

Share this post

Link to post
Share on other sites

Some more information would be helpful, specifically: What is it that you are trying to accomplish that is not being accomplished? A comparison image of the reference/target render as compared to your results for example.


Just looking at the provided picture doesn't make it clear, I see 5 apparently shiny materials that I would assume are correctly being rendered as shiny, and a rough brick material that looks fairly rough. So right now no error appears obvious.

Share this post

Link to post
Share on other sites

Do you mean that they're too bright?

You need to use a HDR rendering pipeline with a good tonemapper at the end.

Share this post

Link to post
Share on other sites

I agree with Hodgman. When roughness is small, specular values can go as high as "60". It's not in the [0; 1] range. You need HDR.


One more observation: is it me, or you're not using gamma correction?

Share this post

Link to post
Share on other sites

even if it looks correct you should write several tests to ensure that it really satisfies his physical properties.
check this out. 

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using, you agree to our community Guidelines, Terms of Use, and Privacy Policy. is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!