Jump to content
  • Advertisement
Sign in to follow this  

Volumetric Lighting Acne/Aliasing Issues.

This topic is 601 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

Hi guys,

So I'm working on some volumetric lighting effects using the ray marching method. I seem to have it nearly nailed, the way this works is I render my entire scene, then I render a fullscreen quad which takes the scene render target as an input as well as shadow maps which then ray marches for N samples for each pixel and accumulates the scattering along that ray, lastly it adds it on top of the scene render. My issue is however I get some bad acne/aliasing issues.. I've narrowed it down to the scattering equation inputs, I believe I'm incorrectly computing the angle needed for the scattering phase function. Here is the issue I'm seeing (enjoy the hot pink): 


Here is my HLSL code for this:

float ComputeScattering( float fCos )
	float g = -0.94f;
	float g2 = g*g;

	float fCos2 = fCos * fCos;

	float result = 1.5f * ( ( 1.0f - g2 ) / ( 2.0f + g2 ) ) * ( 1.0f * fCos2 ) / pow( abs( 1.0f + g2 - 2.0f * g * fCos ), 1.5f );

	return result;

float3 CalculatePosition( float depth, float2 uv )
	float x = uv.x * 2 - 1;
	float y = ( 1 - uv.y ) * 2 - 1;
	float4 vProjectedPos = float4( x, y, depth, 1.0f );
	float4 vPositionVS = mul( vProjectedPos, matInvViewProj );
	return vPositionVS.xyz / vPositionVS.w;

float4 PSMain( PSInput input ) : SV_TARGET
	// Sample scene colour.
	float4 diffuseColour = diffuseTexture.Sample(diffuseSampler, input.uv);

	// Reconstruct world position from depth.
	float worldDepth = depthTexture.Sample( diffuseSampler, input.uv );
	float shadowTex = shadowTexture.Sample( diffuseSampler, input.uv );
	float3 worldPos = CalculatePosition( worldDepth, input.uv );

	// Calculate ray from world pos -> eye 
	float3 rayVector = eyePos.xyz - worldPos.xyz;
	float rayLength = length( rayVector );
	float3 rayDirection = rayVector / rayLength;

	// Calculate step
	float stepLength = rayLength / NUM_SAMPLES;
	float3 step = rayDirection * stepLength;
	float3 lightVec = float3( 0.1f, 1.0f, 0.0f );
	float4 lightCol = float4( 1.0f, 0.0f, 1.0f, 1.0f );

	float3 accumFog = float3( 0.0f, 0.0f, 0.0f );
	float3 currentPosition = worldPos.xyz;
	// Ray March 
	for( int i = 0; i < NUM_SAMPLES; i++ )
		// Advance.
		currentPosition += step;

		// Shadow mapping test.
		float4 shadowPos = mul( float4( currentPosition.xyz, 1.0f ), matShadow );
		float2 shadowCoord;
		shadowCoord.x = shadowPos.x / shadowPos.w / 2.0f + 0.5f;
		shadowCoord.y = -shadowPos.y / shadowPos.w / 2.0f + 0.5f;

		float bias = 0.002f;
		float shadowDepth = ( shadowPos.z / shadowPos.w ) - bias;
		float shadow = shadowTexture.Sample( diffuseSampler, shadowCoord.xy );

		// Check if in shadow.
		if( shadow > shadowDepth )
			// Calculate scattering.
			float d = dot( ( currentPosition ), normalize( rayDirection ) );
			float3 scattering = lightCol * ComputeScattering( d );

			// Accumulate
			accumFog += scattering;


	accumFog /= NUM_SAMPLES;

	// Combine.
	return( diffuseColour + float4( accumFog, 1.0f ) );

At first this seemed very familiar to shadow acne, but removing the shadow map test produces the same results, I believe it's down to this line:

float d = dot( ( currentPosition ), normalize( rayDirection ) );

Any advice would be greatly appreciated, thanks.


Share this post

Link to post
Share on other sites

Just as an update: I've narrowed it down exactly to the phase function, if I use the isotropic phase function:

1.0 / 4.0 * PI 

I get correct scattering. After a bit of shader debugging with the Henyey-Greenstein phase function it turns out that the dot product I am doing is causing the phase function to produce NaN results:

float d = dot( ( currentPosition ), normalize( rayDirection ) );

Is producing NaN results.. does any one know the correct vectors I should be using here? I can't find much info on this, thanks.



Here is my latest Henyey-Greenstein phase function:

float result = ( 1.0f - g2 ) / pow( 4.0f * M_PI * 1.0f + g2 - 2.0f * g * fCos, 1.5f );
Edited by Daganar

Share this post

Link to post
Share on other sites
I guess what's happening is that in the final step the ray is so close to the surface that the shadow test fails. (The artefacts show off only in shadowed regions)

Not sure what's the best way to fix it butn something like step = rayDirection * stepLength * 0.98;

Share this post

Link to post
Share on other sites

Does it work if you normalize both inputs to dot()?

float d = dot( normalize( currentPosition ), normalize( rayDirection ) );

Share this post

Link to post
Share on other sites

fCos should be the cosine of the angle between ray direction and view direction at sample point.

so the code should be

float d = dot( normalize( currentPosition - eyePos.xyz ), rayDirection );

Share this post

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

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net 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!