I Replaced baked pixel-art dungeon props with real-time SDF shaders in Bevy — before/after, looking for feedback & ideas
Hey all — working solo on CryptFall, a 2D roguelite dungeon crawler in Bevy 0.15, and just wrapped an experiment I'd love feedback on.
Why I started this: a lot of my world props and decor just didn't feel like they belonged in the world — they read as pasted-on clutter rather than objects that actually lived in the room, which made the dungeon feel flatter and less alive than I wanted. Digging into why, I traced it back to a technical ceiling.
The problem: my "hero" props (pillars, barrels, tagged-room furniture) were procedurally-generated pixel art, baked to 32×32 PNGs at build time by a custom generator, then loaded as ordinary Sprites. No matter how much I tuned the CPU-side lighting math, a small object's cross-section only had a handful of texels to carry a shading gradient across — that pixel budget was a hard ceiling I kept slamming into, and it's a big part of why nothing quite sold as "a real object sitting in this room."
What I did instead: moved these props onto a Material2d/AsBindGroup/WGSL pipeline and draw them as signed-distance-field shapes directly in the fragment shader — circles for pillars, capsules for barrels, and hand-composited unions of line-segment + disc primitives for the tagged furniture (weapon rack, treasure pile, bedroll, chain cage). Antialiasing comes from an fwidth()-based smoothstep over the distance field, and lighting is a per-pixel cosine/Lambertian term computed live instead of anything baked in. Continuous math instead of a fixed grid — no more texel ceiling.
Before/after screenshots attached.
This image is of before the changes.
This is an image of after the changes. Currently only affecting a few of the objects.
Still very much WIP (this is a feature branch, not merged): furniture placement needed a couple of follow-up passes to actually read as "resting against a wall" instead of floating in the middle of the room, and I haven't touched the ~24 plain floor-debris decor variants or the floor/wall tiles themselves yet — that last one's the scary one, since it's 35k+ tiles and can't be one draw call per tile the way these props are, so it'll need a single big procedural quad instead.
Curious what this community thinks:
- Anyone pushed SDF shape rendering this far in a 2D Bevy game before? Pitfalls I should know about before I commit to this for floor/walls too?
- Techniques worth stealing to make the shading feel more "in the world" — next up I'm looking at reacting to actual nearby torch position/color instead of a fixed light direction.
- Honest gut-check on whether this reads as an improvement — I've been staring at it too long to trust my own eyes.
Happy to share more shader code if it's useful to anyone.