Working with SDFs both on the CPU and on a shader

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
melanchollie
Prole
Posts: 1
Joined: Tue Jul 16, 2024 9:29 pm

Working with SDFs both on the CPU and on a shader

Post by melanchollie »

Hey there guys
I'm planning on making a simple game where you deform the terrain of stages to move a little ball around. With a little research, I have stumbled on SDFs and as destiny has it, I found a recent library for Playdate by robga with some very helpful examples (https://github.com/pdstuff/PlaydateSDF/tree/main), which provided me with a foundation for the collision and basic physics with smooth slopes that was easy to port to Love.

On the CPU side of things, things seem to be going well, I think I can handle the adjustments and optimizations to physics and collision I need. However, I have a big problem which is rendering the SDFs. On CPU, it is very slow, but with shaders I think it can be done while maintaining good performance.

The problem is, I need to evaluate the SDFs both on CPU for collision and on the GPU for rendering. For simple scenes, I can hardcode everything I need on both sides and it works, but scaling this seems like a nightmare.

I will start with a problem I'm having which I imagine the solution to be simple and then describe a much bigger thing to be solved.

So, I have 0 experience with shaders. While I managed to set up the rendering shader matching the rendered SDF with the one used on CPU for collision, I'm getting mixed up on the coordinates. To get things working, I'm scaling every argument passed to the SDF function by the screen height and for the SDF position, I'm offsetting the position like so:

Code: Select all

	//Code for the shader, 
    float height = love_ScreenSize.y;
    vec2 p = screen_coords/height;
    vec2 pos = (400,300) //Position the the SDF should be drawn on screen

    //sdCircle is a function that defines the SDF for a circle, taking a vec2 for position and a float for radius
    float d = sdCircle(pos - p, 30/height);
Which kinda works, the position and radius match what I would get with a Love-drawn circle with radius 30 on 400, 300. But with this, I'm getting the SDF in the original coordinates alongside the transformed one. I just kinda guessed this screen_coords/height thing and dividing the arguments by height and it happened to work, but I don't really know if this is the approach I should be taking to matching the texture coordinates with the actual game screen.

So for the second, big problem. Let's imagine a scenario for a very simple game where each of its stages is defined by the union a fixed number of circle SDFs (i.e the min of all the SDFs). I can define the function for a circle SDF on both Lua and GLSL, a mapping function for the union and pass the arguments for each circle to the Love function that takes care of collisions and send also them to the shader. As a proof of concept, this should work well.

But if we go just a bit deeper, like adding an arbitrary number of SDFs for different types of primitives per scene, I'd need to define on a shader the function for every possible primitive, send to it information about how many and which primitives to use and the arguments for each SDF. If I want to compose a scene using different operations between SDFs or send the results of a SDF to another one to add distortions and stuff, I'd need either to parse a whole lot of data inside a shader in a huge mapping function.

It seems to me that the best way to go about this is coming up with a syntax that can handle defining primitives and operations between them, to be parsed directly for CPU collision and injecting shader text file with the definitions for the function I will be using and the mapping function to be used, avoiding the need to iterate through a bunch of data sent to a shader and evaluating nested functions accordingly.

I'm I overthinking this or approaching it from the wrong angle? Any suggestions on how to handle this would be welcome. I probably could use some other method of slopes and terrain deformation, but I've grown fond of SDFs in the little time I had with them. I want to see this working in some capacity.

I'm attaching to this a simple example of what I'm working on. It's a ball rolling around a horseshoe. Collisions handled on the CPU and rendering on a shader. You can see my jank implementation of the shader on main.lua and the collision and physics on collision.lua and Ball.lua.
sdf.lua defines the horseshoe SDF and vec2.lua is just a lib for vector operations, these can be ignored.
sdf_example.love
(3.59 KiB) Downloaded 208 times
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Amazon [Bot], Google [Bot] and 4 guests