Blinn Phong Normalization

Started by
4 comments, last by wil_ 7 years, 11 months ago

Hello,

Im working with the Source engine, and im wondering about the correct and best way of implementation of normalizing blinn phong

Here is the default implementation of the basic brdf in source.

All this code is publicly available on their source 2013 github page.

/////diffuse term is;



float3 DiffuseTerm(const bool bHalfLambert, const float3 worldNormal, const float3 lightDir,
  const bool bDoLightingWarp, in sampler lightWarpSampler )
{
float fResult;

float NDotL = dot( worldNormal, lightDir ); // Unsaturated dot (-1 to 1 range)

fResult = saturate( NDotL ); // Saturate pure Lambertian term

float3 fOut = float3( fResult, fResult, fResult );


return fOut;
}

and main implementation is


diffuseLighting = vColor * fAtten * DiffuseTerm( bHalfLambert, worldNormal, lightDir, bDoLightingWarp, lightWarpSampler );


float3 vHalfAngle = normalize( vEyeDir.xyz + vLightDir.xyz );
float flNDotH = saturate( dot( vWorldNormal.xyz, vHalfAngle.xyz ) );


specularLighting = SourceBlinn(vWorldNormal,vEyeDir, vLightDir, fSpecularExponent); 
specularLighting *= pow( saturate( dot( vWorldNormal, vLightDir ) ), 0.5 ); // Mask with N.L raised to a power
specularLighting *= color;

float3 diffuseComponent = albedo * diffuseLighting;

final =  diffuseComponent + specularLighting

This is the default implementation of BlinnPhong ;


half SourceBlinn(half3 vWorldNormal, half3 vEyeDir, half3 vLightDir, half Expo)
{
 
  float3 vHalfAngle = normalize( vEyeDir.xyz + vLightDir.xyz );
  return  pow( saturate(dot(vWorldNormal, vHalfAngle)),    Expo);
    
}

and then Cryengine's Normalized.


#define ONE_OVER_PI 0.31831h
#define ONE_OVER_TWO_PI 0.159155h

half SourceBlinnCE(half3 vWorldNormal, half3 vEyeDir, half3 vLightDir, half Expo)
{
  half fNormFactor = Expo * ONE_OVER_TWO_PI + ONE_OVER_PI;	

  float3 vHalfAngle = normalize( vEyeDir.xyz + vLightDir.xyz );
  return fNormFactor * pow( saturate(dot(vWorldNormal, vHalfAngle)),    Expo+1e-6); <-- cryengine also adds this modification
    
}

Would this be the correct way of doing it?

Similar to how its done in Cryengine I think.

Every engine that I have looked at so far has implemented it different.

Marmoset does;



half Marmo(half3 N, half3 V, half3 L, half Expo)
{
	
  half3 H = normalize(V + L);

  half fNormFactor = (Expo + 4.0)/(8.0*3.141592);	

  return fNormFactor *  pow(saturate(dot(N, H)), Expo);
 }

But I have also seen;

 half fNormFactor = (Expo + 2.0) / 8.0; ... with seemingly no adjustment to the diffuse portion of the brdf.

Also  half fNormFactor =  (Expo+8.0) / 8.0;


Advertisement

The right way to do it depends on the rest of the BRDF. You're not just normalizing the Blinn Phong function -- you're normalizing the whole BRDF.

The most common one I've seen would be fNormFactor = (Expo + 1) * ONE_OVER_TWO_PI;

Thanks Hodgman, ive updated my first post with the rest of the brdf.

Ive seen so manyyyyy different implementations.

Another form of the normal distribution func for binn-phong is 1/(pi*a^2)*(n.m)^(2/a^2-2), according to ue4 implementations, assume a=roughness^2.

^^ that previous post is relating to physically based shading, and isn't applicable to normalisation of your brdf in this case. Hopefully someone with more knowledge can drop by and explain the correct way to normalise sources shading. Sorry I can't be much help.

By the way, here is a "famous" article on the subject: The Blinn-Phong Normalization Zoo.

This topic is closed to new replies.

Advertisement