rledrin / WebGPU-Bloom

A cluncky example of bloom in rust with wgpu
4 stars 1 forks source link

Is ping-pong actually necessary when downsampling? #6

Open mreinstein opened 1 year ago

mreinstein commented 1 year ago

looking at this code in bloom.rs:

        // Downsample bind groups
    for i in 1..BLOOM_MIP_COUNT {
        // Ping
        bloom_mat.bind_group.push(create_bloom_bind_group(
            renderer,
            bloom_mat,
            &bloom_mat.bind_groups_textures[1].mip_view[i],
            &bloom_mat.bind_groups_textures[0].view,
            &renderer.hdr_texture.view,
            renderer.hdr_texture.sampler.as_ref().unwrap(),
            &bloom_mat.bind_groups_buffers[0],
        ));

        // Pong
        bloom_mat.bind_group.push(create_bloom_bind_group(
            renderer,
            bloom_mat,
            &bloom_mat.bind_groups_textures[0].mip_view[i],
            &bloom_mat.bind_groups_textures[1].view,
            &renderer.hdr_texture.view,
            renderer.hdr_texture.sampler.as_ref().unwrap(),
            &bloom_mat.bind_groups_buffers[0],
        ));
    }

Why not just construct all downsampled mip levels in a single texture:

        // Downsample bind groups
    for i in 1..BLOOM_MIP_COUNT {
        bloom_mat.bind_group.push(create_bloom_bind_group(
            renderer,
            bloom_mat,
            &bloom_mat.bind_groups_textures[0].mip_view[i],
            &bloom_mat.bind_groups_textures[0].mip_view[i-1],
            &renderer.hdr_texture.view,
            renderer.hdr_texture.sampler.as_ref().unwrap(),
            &bloom_mat.bind_groups_buffers[0],
        ));
    }

Wouldn't this achieve the same result, but with a lot less compute passes?

rledrin commented 1 year ago

If I remember correctly you can downsample without ping pong but the result is not so good. With the ping pong we apply the downsampling algorithm without reducing the size of the output texture twice then we output to the next mip(size/2).

For instance, if we have 2 textures of the same size A and B with 2 mips each: 1) A(Mips[0]) --> B(Mips[1]) 2) B(Mips[1]) --> A(Mips[1]) 3) A(Mips[1]) --> B(Mips[2]) 4) B(Mips[2]) --> A(Mips[2])

And this if I remember correctly gives us better looking bloom.

mreinstein commented 1 year ago

thanks for the follow-up! I'm curious, do you know how it affects the bloom visual quality? I'm trying to understand in-depth how these passes work.

rledrin commented 1 year ago

If you want a detailed explaination about the downsampling you might find this video interesting https://youtu.be/tI70-HIc5ro?t=874 I'm not an expert in visual effects, so it would be complicated for me to explain it any better than this video.

mreinstein commented 1 year ago

It's funny that you should link that video, that's the one I've already watched which is raising these questions. :-) Great video!

I'm struggling to reconcile the draw calls in the video against the approach you took in this repo. From the video:

draw call 146 - initial threshold + downsample operation      [ mip level 0?]
draw call 162 - 2nd downsample (and horizontal blur?)         [ mip level 1?]
draw call 178 - vertical blur I think (not downsampling)      [ mip level 1?]
draw call 192 - 3rd downsample (and horiz. blur?)             [ mip level 2?]
⋮                                                                   ⋮                                                                               

Looking at draw calls 162, 178 these things are doing a vertical and horizontal blur at the given mip size. But I don't think your code is doing a specific vertical or horizontal blur step. What am I getting wrong?

btw thanks again, these discussions are super helpful! ❤️

rledrin commented 1 year ago

As you said I didn't use the same blur method as in the video (vertical/horizontal).

I used 2 compute passes for each downsampling because doing more blur helps to get a better result but the number of passes is a parameter you should adjust according to your objective.

mreinstein commented 1 year ago

I used 2 compute passes for each downsampling

I guess that's crux of where I'm confused; how does that work? You can just apply the downsample twice at a given mip level and it'll have a similar effect of increasing the blur amount? Are there pros/cons of a separate horiz/vert blur pass vs 2 downsample passes?

thx!