bevy_smud icon indicating copy to clipboard operation
bevy_smud copied to clipboard

Pass custom instance data to sdf shader function (to allow single-shader parametrized shapes)

Open johanhelsing opened this issue 2 years ago • 1 comments

For some shapes, for instance rectangles, circles, rounded rectangels, it makes a lot of sense to be able to send per instance data into the sdf function so you could change side length/radius without compiling a new shader.

I'm not really sure what the best way to implement this would be... One simple way could be to just have a couple of floats of "user data" that will just always be passed around. It may come with a performance cost, though.

Perhaps some way to toggle it with shader defs would be good, and then have it be an optional component SdfParameters(Vec4) so we don't pay the price for it when not used.

johanhelsing avatar Feb 22 '22 12:02 johanhelsing

Hey. Just letting you know, that I've had a go at doing this with const generics and it's working great for me but my implementation probably isn't ergonomic enough for general use.

Basically I added a const PARAMS: usize everywhere and you set that to highest number of params you need for any of your sdf/fill combinations. I also added a ShaderParamUsage argument which controls which params are sent to the sdf and which to the fill. It does what I need it to do but is a bit of a pain to use and it probably isn't optimal performance wise so I'm definitely not suggesting this be merged or that this approach is even the right one, just wanted to share in case this was useful to anyone.

My code is here but

Just some notes:

  • I couldn't find a way to pass an array of f32 to the Vertex shader so I passed all the params individually. I'm not sure if this affects performance, but it does make passing an arbitrary subset of the params to each function much easier.
  • I had to write some unsafe code (empty impls of Pod and Zeroable for ShapeVertex since the derive macros don't with with const generics). I think this is fine but I'm not 100% sure.
  • I actually would like a way to pass in u32 params as well as f32. I'm getting away with it by using from_bits on the rust side and bitcast<u32> on the other but I have to be careful of NaNs and can't use the full range of possible values. It might actually be better to go the other way round and pass in everything as a u32.
  • I started passing in the position argument to the fill functions. It let me make some nice gradients

image

wainwrightmark avatar Nov 27 '23 17:11 wainwrightmark