Directional lightmapped specular

Started by
2 comments, last by redspike474 8 years, 5 months ago

Hello,

Im trying to implement a phong-based specular for my lightmapped geometry.

Im currently doing something similar to this here http://www.gamedev.net/topic/659784-specular-highlights-with-directional-lightmaps/

However is there a better way to handle this?

My main confusion is obtaining a dominant light direction from the lightmap...

Advertisement

Found this implementation of specular lightmaps https://github.com/Geenz/GzRNM/blob/719a8a7340fc2cd16b9a9072cd76feddaec0e7c9/GzRNMOS/Assets/GzRNM/Shaders/GzRNMPro/GzRNMInc.cginc

But this produces three specular lobes (one for each basis) and is rather wrong looking, the developer addresses it on this forum page http://forum.unity3d.com/threads/geenzs-rnm-shaders-now-released.71648/page-2 but he never released the update as far as i can see.

Will continue to experiment!

The short answer is: yes, you can definitely do better than that. Even if you just want some hacky specular out of a Half-Life 2-style lightmap, I don't think you want to do what he or she was doing in the post that you linked. You'll probably get better results out of just treating the lighting map info as 3 directional lights oriented about the basis vectors, and then computing Phong or Blinn-Phong specular from that. You could also try computing a "dominant direction" from your lightmap info and treating that as a directional light, but these approaches can sometimes suffer from interpolation issues where the dominant direction doesn't match up from texel to another. To compute the dominant direction you could probably just use a weighted average: compute a single luminance value for each of your 3 lightmap values, multiply each basis direction by the luminance, sum the results, and then normalize the result. You can then create a virtual directional light oriented in that dominant direction, and for the color you could look up the irradiance in that direction (exactly the same way that you look up irradiance for diffuse: dot the direction with each basis vector, and multiply the result of that dot product with the lightmap value that corresponds to that basis vector). This is somewhat similar to what Naughty Dog did for The Last of Us, except that they pre-computed the dominant direction and a color for that dominant direction, and combined that with a "flat" ambient term that didn't depend on the direction at all.

If you're interested in achieving a better approximation of environment specular, you'll want to use a technique that at least attempts to approximate the integral of a specular BRDF with the radiance of the environment. One way to do this is to represent your incoming radiance using spherical harmonics, which provide a framework for computing the integral by means of frequency domain convolution. This was the approach taken by Bungie for Halo 3, and was discussed in their SIGGRAPH presentation as well as their course notes.

More recently, we presented some info at SIGGRAPH about our lightmap baking for The Order. Instead of representing incoming radiance with spherical harmonics, we used a fixed-size set of spherical gaussians. With SG's you have analytical formulas for integrating the product of two SG's, and so you can compute a specular term as long as you can approximate your BRDF using a gaussian.

Thanks @MJP for the incredibly informative reply! that's a tonne of useful information.

This was the approach taken by Bungie for Halo 3, and was discussed in their SIGGRAPH presentation as well as their course notes.

More recently, we presented some info at SIGGRAPH about our lightmap baking for The Order.

Ideally i would love to do something like either of these implementations but it isn't currently feasible for this particular project, its also slightly beyond my current capability.

You could also try computing a "dominant direction" from your lightmap info and treating that as a directional light, but these approaches can sometimes suffer from interpolation issues where the dominant direction doesn't match up from texel to another.

I took your advice and implemented a hacky phong specular with a dominant direction, It produces much better results than my previous implementation! here is my code, please feel free to pick it apart and point out any mistakes or suggest any improvements, Im still very much a novice. Though LOOKS right ingame........


#if( PHONG && !PS4)
{	
	float3 vEyeDir = normalize( g_EyePos - worldPos );	
	half SpecExponentMap = tex2D(SpecExponentSampler, coords.xy).r;

	float lumer1 = ( Luminance( lightmapColor1.rgb )  );	
	float lumer2 = ( Luminance( lightmapColor2.rgb )  );	
	float lumer3 = ( Luminance( lightmapColor3.rgb )  );		
			
	float3 	vLightDomDir = normalize (lumer1 * bumpBasis[0] + lumer2 * bumpBasis[1] + lumer3 * bumpBasis[2]);
	
	float3 vHalfAngle = normalize( vEyeDir.xyz + vLightDomDir.xyz );
	float flNDotH = saturate( dot( worldSpaceNormal.xyz, vHalfAngle.xyz ) );
		
	SpecPhong = pow( flNDotH, SpecExponentMap ); // Raise to specular exponent
	SpecPhong *= pow( saturate( dot( worldSpaceNormal, vLightDomDir ) ), 0.5 ); // Mask with N.L raised to a power
	
	
	float3 d_irradiance = 0;
	d_irradiance.x = dot(bumpBasis[0], vLightDomDir);
	d_irradiance.y = dot(bumpBasis[1], vLightDomDir);
	d_irradiance.z = dot(bumpBasis[2], vLightDomDir);
	d_irradiance*= d_irradiance;
	d_irradiance = d_irradiance.x *  lightmapColor1.rgb +  d_irradiance.y * lightmapColor2.rgb + d_irradiance.z * lightmapColor3.rgb;
	SpecPhong *=   d_irradiance; // color the specular from the d_irradiance

	float3  vSpecularTint =1; // leaving this in for now, models have the ability to tint specular

	float fSpecMask = 1.0f;
	fSpecMask =  baseColor.a; // hardcode for now
	//fSpecMask = lerp( normalTexel.a, baseColor.a, g_fBaseMapAlphaPhongMask );
	//fSpecMask = lerp( fSpecMask, baseLum, g_fBaseLumPhongMask );

	SpecPhong *= fSpecMask * vSpecularTint;
		

	float3 g_FresnelRanges = float3 ( 0.3, 9.0, 28 ); // test vaules to match models material // need to hook up to material
	float fFresnelRanges;
	fFresnelRanges = Fresnelcustom( worldSpaceNormal, vEyeDir, g_FresnelRanges );
	SpecPhong *= fFresnelRanges;
	
	//to do look at this again.
	#if ( ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) )
	{	
	// Apply csm shadows
	//SpecPhong.rgb *= shadowscalarpassthrough;
	}
	#endif
		
	//note note - not sure why we would want rim lighting on brushwork so left it out. but could bring back for cheap;
}

Thanks! smile.png

This topic is closed to new replies.

Advertisement