Sign in to follow this  
Kjell Andersson

Blinn-Phong artifact in shader

Recommended Posts

I'm implementing Blinn-Phong lighting in a pixel shader and is getting an artifact that I don't like. I'm tossing out a screenshot here to ask if this is normal for Blinn-Phong or if I have gotten something wrong with my calculations.

[attachment=29316:BlinnPhongArtifact.jpg]

 

The light source is coming from behind the sphere and is directed towards the viewer. It is supposed to be a directional light.

On the screenshot to the left the specular light is barely visible on the rim, which is perfectly fine. The problem occurs when moving a bit further behind the sphere. The specular light then becomes a cone and is detached from the rim in an unnatural way. Moving further behind the sphere makes it fade away but it keeps moving inwards the sphere before it disappears.

 

Lets toss in the code here as well if you want to inspect it (beware, experimental code that is not production-worthy).

struct VertDataIn {
	float4 Position   : POSITION;
	float3 Normal     : NORMAL;
	float4 Color      : COLOR;
	float2 Texture    : TEXCOORD0;
};

struct VertDataOut {
	float4 Position      : POSITION;
	float4 Color         : COLOR;
	float2 Texture       : TEXCOORD0;
	float3 Normal        : TEXCOORD1;
	float3 WorldPosition : TEXCOORD2;
};

VertDataOut VS_Main(VertDataIn In)
{
	VertDataOut Out;
	Out.Texture = In.Texture.xy;
	In.Position.w = 1;
	Out.Position = mul(In.Position, g_worldViewProjectionTransform);
	Out.Color = In.Color * g_diffuseColor;
	Out.Normal = mul(In.Normal, g_worldTransform);
	Out.WorldPosition = mul(In.Position, g_worldTransform);
	return Out;
}

ColorPair CalculateDirectionalLightBlinnPhong(float3 N, float3 V, int i) 
{ 
	ColorPair Out; 
	float3 L = -normalize(g_directionalLights[i].dir); 
	float NdotL = dot(N, L); 
	Out.ColorDiffuse = g_directionalLights[i].ambient; 
	Out.ColorSpecular = 0; 
	if (NdotL > 0.0f) { 
		Out.ColorDiffuse += NdotL * g_directionalLights[i].diffuse; 
	} 
	float3 H = normalize(L + V);
	Out.ColorSpecular = pow(saturate(dot(H, N)), g_materialPower) * g_directionalLights[i].specular; 
	return Out; 
}

float4 PS_Main(VertDataOut Input) : COLOR
{
	float4 col = Input.Color;
	if (g_hasDiffuseTexture) {
		col *= tex2D(Sampler, Input.Texture);
	}

	// Directional lights 
	float4 diffuseColor = float4(0, 0, 0, 0);
	float4 specularColor = float4(0, 0, 0, 0);
	for(int i = 0; i < g_numDirectionalLights; i++) { 
		ColorPair colorOut = CalculateDirectionalLightBlinnPhong(normalize(Input.Normal), normalize(g_cameraPosition - Input.WorldPosition), i); 
		diffuseColor += colorOut.ColorDiffuse; 
		specularColor += colorOut.ColorSpecular; 
	}

	col.rgb *= (diffuseColor.rgb + specularColor.rgb);

	return col;
}



Share this post


Link to post
Share on other sites

Thanks!
I kind of liked the scattering of specular around the rim before multiplying with NdotL, but I guess that should not be part of the Blinn calculations but rather some subsurface scattering effect.


You can get a more accurate version of that effect by using a microfacet specular BRDF. Such a BRDF has geometry and fresnel terms that give you interesting off-specular peak behavior, but do so in a way that's more consistent with physics compared what you're using. However this does require more calculations, which can make the shader more expensive.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this