#Destructible pixels... on the GPU?

19 messages · Page 1 of 1 (latest)

glad token
#

Context: I'm working on a retro-style arcade game using pixel-based collision, where any non-transparent pixel (with an alpha > 0.0) can be collided with.

I'm comfortable with how to do this on the CPU, directly manipulating image data. Now I'd like to do this with shaders and the rendering pipeline as much as possible, mostly for the learning experience. I think I'm getting a bit bogged down in some of the concepts though (giant surprise, I know!)

I can render to an image render target, which I suspect will be part of the solution. I know how to apply a mask to the underlying image at the location of the mouse cursor, in a fragment shader:

#import bevy_sprite::mesh2d_vertex_output::VertexOutput

@group(2) @binding(0) var<uniform> cursor_position: vec2<f32>;
@group(2) @binding(1) var terrain_texture: texture_2d<f32>;
@group(2) @binding(2) var terrain_texture_sampler: sampler;
@group(2) @binding(3) var mask_texture: texture_2d<f32>;
@group(2) @binding(4) var mask_texture_sampler: sampler;

@fragment
fn fragment(mesh: VertexOutput) -> @location(0) vec4<f32> {
    var terrain_color = textureSample(terrain_texture, terrain_texture_sampler, mesh.uv);

    let diff = mesh.position.xy - cursor_position;
    if all(abs(diff) < vec2<f32>(36.0, 36.0)) {
        let mask_uv = (diff + vec2<f32>(36.0)) / 72.0;

        var mask_color = textureSample(mask_texture, mask_texture_sampler, mask_uv);
        if terrain_color.a != 0. {
            terrain_color.a = mask_color.a;
        }
    }

    return terrain_color;
}

Where I'm getting a bit iffy is, how would one approach retaining the changes to the rendered image i.e. making the pixels set to alpha persist for the duration of the level. I'm reasonably sure there's a strategy here that doesn't involve copying the entire thing back out to the CPU. Does anyone perhaps have an example I could look at, or a high-level description of the approach?

edgy umbra
#

retaining the changes to the rendered image
In general, this is done by having two textures. Read from one, render to the other. Then swap their roles next frame.

#

I don't know how best to do that in Bevy, though.

glad token
#

Hrm, I think I do. I'm guessing you'd just change the handle on the Material2d. Is this what blitting is?

#

...this is opening up some new possibilities, I'm gonna play with that. Thanks!

glad token
#

if I'm right, so long as you have an Image render target texture, and a MeshMaterial2d with a different texture handle on it, both textures live in the GPU the entire time without a whole lot of copying back and forth 💡

#

we'll see how it actually plays out, hopefully without mad flicker 😆

glad token
#

It looks as if we can use the RenderSet::PrepareAssets or similar to do this kind of texture swapping.

#

saving this for later #assets-dev message

dusty root
glad token
#

I did notice when I tried doing this a naiive way (swapping back and forth between rendering textures) that anything I tried to animate on the screen... became a track of sprites across the screen 😆 ah well, I'll keep at it

glad token
#

Coordinate Systems Are Hard™️

glad token
#

Because of the adjustment between screen space coords and the much larger 2k texture coords, I have a "shadow cursor" that must be dealt with somehow

#

still, getting there

#

I wonder if perhaps I should do the coord conversion in the shader itself instead of the updating Bevy system, that might avoid temporarily drawing to the wrong area of the screen

#

it's nice there's no flicker, at least