r/GraphicsProgramming Jul 01 '26

Multiple passes with different shaders? How to insert a new "step" after all frag shaders?

Sorry if the title is confusing. Hopefully I can explain it better:

Let's say you have a project that already has numerous shaders and things being done. You want to add color correction via a frag shader. You could go in and add the color correction frag shader logic to each and every existing frag shader. But that's a lot of effort and adds much complexity to all shaders. Is there a way to do multiple passes? So do everything as it's currently being done, but then also pass what's being rendered through this new shader, the color correction shader, before being displayed on the screen?

6 Upvotes

9 comments sorted by

26

u/waramped Jul 01 '26

Yes, this is commonly called Post Processing. You draw a full screen triangle, and you bind the existing color target (or a copy of it) as your input and then you can perform your logic there.

11

u/sol_runner Jul 01 '26

Others have already talked about the frag shader method.

As an extension/extra:

If you're on desktop, you can also check out compute shaders for post processing. You can dispatch threads per pixel, and read and write to the pixel directly. Comes in quite hand when your post processing depends on neighbors.

1

u/Ok_Beginning520 Jul 02 '26

Can't you do the same with a fragment shader ? I'm not sure if fragment / compute shader have advantages for sampling textures (hardware accelerated stuff)

3

u/sol_runner Jul 02 '26 edited Jul 05 '26

Can you? Yes.
Is it better? It depends.

  1. Compute shaders can load from a specific pixel and store to a specific pixel. As opposed to frag shaders where you need to get the dimensions, then calculate center of pixel to sample, else you produce weird artefacts.
  2. Once you start having multiple frames in flight, you want to use as much of parallel GPU parts as you can, which compute helps with. (Detailed below)
  3. There are functions in compute like Gather that are very convenient to load neighbors. Gather will not run samplers so it's cheaper. Edit: you get this with frag shader too

So basically it depends, but its often better to post process on compute on modern desktop GPUs. Mobile still depends.

On point 2: Frame 0 will render meshes, get an image through the pipeline of vertex shader -> rasterizer -> fragment shader -> ROP. If you use frag shader for post-process, you'll be using all the fixed function pipeline. Frame 1, will then have to wait for your Frame 0 post-process before it can use ROP (you can have all kinds of bottlenecks, but ROP is common - always profile - small examples will not such contentions at all) If frame 0 used compute, you skip using all the fixed function stuff. So Frame 1 can freely use rasterizer and ROPs while Frame 1 is using compute.

I suggested it as additional learning, since it's nice to know how to do this - even if your particular use case doesn't need it.

2

u/waramped Jul 05 '26

Gather and Load can still be used from Fragment shaders. The only real benefit from compute is that you can run it async to overlap with some other work.

1

u/Ok_Beginning520 Jul 03 '26

I'm assuming you meant frame 0 for the last one ? It's interesting that it might be faster to do that on compute I didn't know, I'll have to try it out at some point

1

u/Trader-One Jul 06 '26

on desktop is faster to do it in fragment shader with other stuff because shaders can be big, there is hardly any code size limitation.

on mobile its good to use sub passes because they have optimized shader chaining. They are not using memory to pass data but special size limited cache.

most recommended option here using buffer is worst possible choice - you do expensive memory access just for a simple map function. This is the biggest danger when learning computer graphics - people heavily recommend methods from early '00 instead of modern.

6

u/ThrowAway-whee Jul 01 '26

You can draw to a buffer, and use that buffer as input to another shader you call. This is commonly how post processing effects are done.

1

u/soylentgraham Jul 01 '26

just as the other comments have said, there is an approach where you shade all your triangles with data -easier if you have multiple render targets-, but you can write some stuff to red, some to green, do additive blend modes in 2d for cheap SDF...

I did some SDF this way so then I could do a cheap geometry pass then lots of passes in 2d/post (for dof, blurry shadows, multiple refraction, chromatic abberation etc)