grimfang4 / sdl-gpu

A library for high-performance, modern 2D graphics with SDL written in C.
MIT License
1.19k stars 123 forks source link

Post processing shaders? #240

Closed avahe-kellenberger closed 1 year ago

avahe-kellenberger commented 2 years ago

I'm porting a game I've made with Godot over to use sdl-gpu and have successfully converted some shaders, but I'm unsure how do a post-processing shader.

The setup:

  1. Render a background shader, then invoke GPU_DeactivateShaderProgram
  2. Render some items on the screen
  3. Attempt to render another shader, for a "shockwave" effect, then invoke GPU_DeactivateShaderProgram

Part of the problem is, Godot has a variable provided to their shaders called SCREEN_TEXTURE, which I'm not sure how to use with sdl-gpu.

I've attempted to recreate this by using GPU_SetShaderImage, and providing a copy of the current GPU_Target (using GPU_CopyImageFromTarget). This resulted in every draw call rendering the entire screen, scaled to the size of other image draws, etc.

The shader I'm trying to translate is very small and simple:

shader_type canvas_item;

uniform float force = 0.025;
uniform vec2 center = vec2(0.5, 0.5);
uniform float size = 0.5;
uniform float thickness = 0.5;

void fragment() {
  float ratio = SCREEN_PIXEL_SIZE.x / SCREEN_PIXEL_SIZE.y;
  vec2 scaledUV = (SCREEN_UV - vec2(0.5, 0.0)) / vec2(ratio, 1.0) + vec2(0.5, 0.0);

  float distFromCenter = length(scaledUV - center);
  float mask =
    (1.0 - smoothstep(size - 0.1, size, distFromCenter)) *
    smoothstep(size - thickness - 0.1, size - thickness, distFromCenter);

  vec2 displacement = normalize(scaledUV - center) * force * mask;
  COLOR = texture(SCREEN_TEXTURE, SCREEN_UV - displacement);
}

If you can point in me the right direction, I'd appreciate it. I've been trying to figure this out for a while and haven't been making much progress.

Here's an example of what it looks like in Godot: shockwave.webm

avahe-kellenberger commented 1 year ago

Ping @grimfang4 if you have any insight, I'd really like to get this working.

grimfang4 commented 1 year ago

Nice effect!

The simplest way to think of post-processing with SDL_gpu is to insert a render stage. Instead of rendering to the screen, create a "fake screen" render texture with the same dimensions. Use this everywhere you would normally draw to the screen. Then at the end of each frame, you activate your post-processing shader and render this fake screen onto the real one.

avahe-kellenberger commented 1 year ago

Thanks, that seems to work just fine!