Volumetric Scattering (screen-space) done efficiently ?

Started by
14 comments, last by kalle_h 9 years, 3 months ago

Is there a way to do this efficiently without loosing the "sharpness/detail" of the sun shafts (aka downsample to 1/2 or 1/4) ?

Something like this: http://gaminggix.com/wp-content/uploads/2014/12/dai_emerald_graves1024.jpg

Doing this in full resolution is quite expensive and needs above 100 samples to look good.

I've come across this presentation (http://crytek.com/download/GDC08_SousaT_CrysisEffects.ppt) that says that they used an iterative approach each time with 8 samples summing up to virtual 512 samples. Has anyone successfully implemented this approach ? Because I've yet to get it working...

Advertisement

You can used interleaved sampling to increase how good it looks with fewer samples. However you will need to run a blur pass over the result so it does not look noisy.

I've not used the Crysis approach but will look at that paper.

Oh and Intel has a nice approach that uses epipolar lines. It looks great, and I started working on a version that used it, but its not a drop-in type thing, there is a lot of preprocessing work, and you have to do some cleanup on the results to avoid artifacts.

There is also an uber-cheap yet great looking approach that I dont fully understand, even after reading it, but I will post the link to the paper in case you do. It uses 'rectified shadow maps'

http://jcgt.org/published/0003/03/02/

Might be a little more overhead than you're looking for, but http://bglatzel.movingblocks.net/wp-content/uploads/2014/05/Volumetric-Lighting-for-Many-Lights-in-Lords-of-the-Fallen-With-Notes.pdf

While these more advanced techniques are pretty interesting...what I'm trying to do right now is have the more basic screen space radial blur working fast (and good looking!).

@DASTech can you give me more details about this interleaved sampling approach ? I thought about some kind of temporal supersampling but I'm not sure how to do that.

Cryteks approach is really good quailty performance wise if you dont want to downsample, though you really need the 8x8x8, 4 samples per pass doesn't really cut it for a sun scatter. You just have to divide your sample dist by half after every blur. They dont say do so in their paper, but I assume they mean to, looks no good other wise. I quickly set up 3 radial blurs as an example for you.

one pass 128 Samples

[attachment=25602:Full128Samples.png]

one pass 512 Samples

[attachment=25603:Full512Samples.png]

three pass 8x8x8 samples effectively 512

[attachment=25604:3Pass8x8x8Samples.png]

So let me get this right....you use the blurred output from the first pass with 8 samples as the input to the second pass with 8 samples and so on ?

I'm just having a hard time seeing how 8 samples (basically being 8 little ghosts/copies of the picture) combine to a smooth light beam.

Also the sample distance...would that be the deltaTexCoord from the currentTexCoord to the sun position in screen space ? So each time it has to be half the distance from before meaning effectively 1/2,1/4 ?

My result looks like this:

KUEApbR.png?1

I am doing volumetric shadows with quite aggressive temporal supersampling with jittern and bilateral upsample. This way you can get sharp details with just 8-12 saples. Screenspace effects just does not give the proper look.

https://www.dropbox.com/s/hx517wmtok21c5w/VolumetricShadowsOn.png?dl=0

https://www.dropbox.com/s/u973s7t4g8rblrs/VolumetricShadowsOff.png?dl=0

Another one with just fog contribution. https://www.dropbox.com/s/j8xxil9zk1neyac/VolumetricShadows.png?dl=0

Grains come form filmgrain and not from technique.

Correct, you use the previously blured texture for the next blur pass with the sample distance divided always in half every next pass, the code from the example is below in glsl. Im not additively blending the samples together but should make no difference if blended correctly. Your result should look much smoother, looks like your still downsampling, maybe this method doesn't play nicely with it never tried.

I was shocked myself when I first tried cryteks method of doing most types of blurs, with how smooth it was with so few samples and just multiple passes. Crytek said they used the same method for their motion blur in that paper to.


uniform sampler2D inputTexture;

vec2 texCoord = gl_TexCoord[0].xy;

uniform float x;
uniform float y;

vec2 lightPos = vec2(x, y);

uniform float density = 1.0; // next pass 0.5 then 0.25 and so on

const int SAMPLES = 8;

vec4 radialBlur(vec2 texCoord)
{

    
    vec2 deltaTexCoord = gl_TexCoord[0].st - lightPos;
    deltaTexCoord *= 1.0 / SAMPLES * density;
    
    vec4 color;
    
    for(int i = 0; i < SAMPLES; i++)
    {
        texCoord -= deltaTexCoord;
        
        color += texture2D(inputTexture, texCoord);
        
    }

    color /= SAMPLES;
    
    return color;
}


void main(void)
{

    vec4 color = radialBlur(texCoord);

    gl_FragColor = color;
}

Hmm I'm using a similar code based on the sample from GPU gems 3


// Performs a directional blur from the light source to approximate volumetric scattering
float3 Scatter(in VSOutput input) : SV_Target
{
	// Calculate vector from pixel to light source in screen space
	float2 sunSS = SunPositionSS.xy;
	float2 deltaTexCoord = (input.TexCoord - sunSS.xy);

	// Divide by number of samples and scale by control factor
	deltaTexCoord *= (1.0f / numSamples) * Density;
	
	// Store initial sample
	float3 color = InputTexture0.Sample(LinearClampSampler, input.TexCoord).rgb;
		
	// Set up illumination decay factor
	float illuminationDecay = 1.0f;
	float2 uv = input.TexCoord;

	// Evaluate summation n iterations
	for (int i = 0; i < numSamples; i++)
	{
		// Step sample location along ray
		uv -= deltaTexCoord;

		// Retrieve sample at new location
		float3 sample = InputTexture0.Sample(LinearClampSampler, uv).rgb;

		// Apply sample attenuation scale/decay factor
		sample *= illuminationDecay * Weight;
	
	        // Accumulate combined color
		color += sample;

		// Update exponential decay factor
		illuminationDecay *= Decay;
	}


	return  (exposure * color);
}

I just tried your code and it does seem to produce a difference result but still not as smooth as in your screenshot.

This topic is closed to new replies.

Advertisement