Advertisement

PBR Lighting creating a hole with darkness surrounding

Started by May 07, 2024 06:20 PM
2 comments, last by snoken 7 months, 2 weeks ago

Hi,

I am taking a crack at implementing PBR lighting in my game-engine and I have managed to get it working but it seems to produce this odd hole with blackness surrounding it which I cannot seem to figure out.

I have tried visually outputting each term D, G, F and also the specular term to see what might be the issue but I don't see it when outputting them individually. I was wondering if anyone has experienced this issue before and could point me in the right direction of where I might be doing wrong.

Hole with surrounding darkness
const float PI = 3.14159265359;



vec3 fresnel_schlick(float cosTheta, vec3 F0)
{
	return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}

float beckmann_distribution(vec3 Normal, vec3 HalfDir, float roughness)
{
	float a = roughness * roughness; // beckmann roughness is r * r
	float a2 = a * a;
	float NdotH = max(dot(Normal, HalfDir), 0.0);
	float NdotH2 = NdotH * NdotH;

	float num = exp((NdotH2 - 1.0) / (a2 * NdotH2));
	float denom = PI * a2 * NdotH2 * NdotH2;

	return num / denom;
}

float masking_term(vec3 Normal, vec3 HalfDir, vec3 LightDirection, vec3 ViewDirection)
{
	float NdotH = max(dot(Normal, HalfDir), 0.0);
	float NdotV = max(dot(Normal, ViewDirection), 0.0);
	float VdotH = max(dot(ViewDirection, HalfDir), 0.0);
	float NdotL = max(dot(Normal, LightDirection), 0.0);

	float term1 = 2 * NdotH * NdotV / VdotH;
	float term2 = 2 * NdotH * NdotL / VdotH;

	float term = min(1, min(term1, term2));

	return term;
}

void main() {
 	vec4 lightColour = Light.color;
	vec4 lightPosition = Light.position;

	vec3 WorldPos = FragPosition.xyz;
	vec3 normal = normalize(normals);

	vec3 LightDirection = normalize(lightPosition.xyz - WorldPos);
	vec3 ViewDirection = normalize(ubo.position.xyz - WorldPos);
	vec3 HalfDir = normalize(ViewDirection + LightDirection);

	// Texture sampling 
	vec3 color = texture(textures[push.base_texture], uv).xyz;
	float metallic = texture(textures[push.metalness_texture], uv).r;
	float roughness = texture(textures[push.roughness_texture], uv).r;
	vec3 alpha = texture(textures[push.alphamask_texture], uv).rgb;

	// Lighting calc
	vec3 F0 = vec3(0.04);
	F0 = mix(F0, color, metallic);
	vec3 F = fresnel_schlick(max(dot(HalfDir, ViewDirection), 0.0), F0);

	// Normal distrbutin function
	float d = beckmann_distribution(normal, HalfDir, roughness);
	// Geometry masking term
	float g = masking_term(normal, HalfDir, LightDirection, ViewDirection);

	vec3 num = d * F * g;
	float epsilon = 0.0001;
	float denom = 4 * max(dot(normal, ViewDirection), 0.0) * max(dot(normal, LightDirection), 0.0) + epsilon;

	vec3 specular = num / denom;

	float NdotL = max(dot(normal, LightDirection), 0.0);
	vec3 L_diffuse = (color / PI) * ((vec3(1.0) - F) * (1.0 - metallic)) * lightColour.xyz * NdotL;

	float ambientAmount = 0.2;
	vec3 ambient = vec3(ambientAmount) * color;

	vec3 shading = ambient + L_diffuse + specular;




    outColor = vec4(vec3(shading), 1.0);
}

You probably have either a negative number or NaN in your final color output of the shader. Rather than max( x, 0 ), try max( x, 0.000001 ), to avoid producing NaNs.

Advertisement

Ah it was exactly this! Thank you.

Is my implementation of Beckmann correct? There's a few different formulas for it.

This topic is closed to new replies.

Advertisement