Question about downsampling

Started by
6 comments, last by Telanor 10 years, 8 months ago
A lot of times, usually in bloom/hdr samples, I see people taking a fullscreen image, rendering it to a 1/2 sized RT, then rendering that to a 1/4 sized RT. The pixel shader for these 2 passes is nothing more then a linear sampler being run on the input.

Is this done because the linear sampler returns a 2x2 area sample and you don't want to lose any information by downsampling straight to a 1/4 sized RT? How bad would it be to just skip straight to the 1/4th size? And what about upsampling then? I've seen the same process done for that case as well.

Also, a side question: If I need to bring a fullscreen image down to 1/8th size, would it be faster to just ask DX to generate mipmaps and use the mipmap of the size I need rather than doing 3 downscale passes? The answer will probably be "try it yourself" but I figured I'd ask anyway...
Advertisement

Going straight to 1/4 of the size can lose single pixel size bright spots - due to the 2x2 sample you pointed out. For bloom this can make a huge difference, as now your highlights can shimmer in and out of existence as that bright pixel moves in screenspace. The upsampling on each level gives a smoother result than going straight from 1/4 back to 1/1.

That said, depending on your hardware (i'm looking at both main current gen consoles here, but it probably applies to PC GPUs) its faster to go straight to the 1/4 size with a shader that samples 4 times instead. This is due to actual cost of the physical write to texture memory for the intermediate step being expensive itself. Doing this also means no precision loss that would otherwise be incurred by downsampling RGBA8 textures, as the shader does the filtering in 32-bit.

If you were to "skip" right to the 1/4 resolution you will alias due to undersampling. In order to sample at or above nyquist rate you need 2x the number of samples in each dimension, which is satisfied if you use a 2x2 filter kernel. The aliasing will manifest as blockiness in still images, and temporal flickering in moving images (which is much worse).

Also, a side question: If I need to bring a fullscreen image down to 1/8th size, would it be faster to just ask DX to generate mipmaps and use the mipmap of the size I need rather than doing 3 downscale passes? The answer will probably be "try it yourself" but I figured I'd ask anyway...

In general I'd expect it to be somewhat hardware-dependent. My intuition is that it might be faster to use mipmaps only because the down-sampling for that purpose could potentially be implemented as a fixed function on the hardware but unfortunately I have no idea if that's true in practice.

-~-The Cow of Darkness-~-

That said, depending on your hardware (i'm looking at both main current gen consoles here, but it probably applies to PC GPUs) its faster to go straight to the 1/4 size with a shader that samples 4 times instead.


Something like this?

float2 InputSize = float2(1/1280, 1/720);

float4 PS(VertexOutput input) : SV_TARGET0
{
	float4 color;
	
	color = InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(1, 1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(-1, -1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(-1, 1));
	color += InputTexture0.Sample(LinearSampler, input.TexCoord + InputSize * float2(1, -1));
	
	return color / 4;
}

Yep smile.png

I always end up drawing myself diagrams like this when I'm trying to work out texture sampling locations:

EuZ05CG.png

Black rects are original (100%) texels.

Red rects are half-res texels.

Blue rect is a quarter res texel.

If you're doing post-processing using a full-screen quad, then the pixel shader will be getting executed at the center of the texel.

For a full-res post-processing shader, this means your unmodified tex-coords (input.TexCoord) will be in the centre of the black rect.

For half-res, they will be at the centre of the red quads, which is exactly at the black +, so bilinear filtering will give you the average of 4 original texels.

For quarter res, your tex-coords will be at the centre of that image, exactly at the red +. From there if you add originalPixelSize * (1,1), you'll be offsetting to exactly on one of the black +'s, which is exactly what you want!

So, your code should give you the average value of all of the original texels (black quads) in the above diagram, by sampling at all 4 of the black +'s.

Maybe a bit off-topic but might be useful: You can also use some linear filtering trickery to speed up blurring by sampling at the right spots like this article explains.

P.S: Nice explanation Hodgman :) Drawings always help!

I did a few tests. Doing linear sampling ended up being very slightly faster than generating mipmaps. Also doing 4 linear samples in 1 pass instead of 1 sample in 2 passes was again very slightly faster.

This topic is closed to new replies.

Advertisement