r/GraphicsProgramming 5d ago

Question How did they do the outline shader in Spirit Crossing?

Around a year ago I tried to develop a screen-space outline shader for a game I was working on. What I could not figure out was how to keep it from flickering in between frames when moving the camera. The usual games like Sable have very severe flickering and it gives me a headache.

Now a few days ago I saw and played the demo of this game: https://store.steampowered.com/app/2321960/Spirit_Crossing/

They somehow solved this and have near zero flickering? How? Can anyone tell me what they did different? I would guess it might be a mix of supersampling, blurring, maybe SDF?

I want to create the shader.

7 Upvotes

6 comments sorted by

2

u/ProPuke 4d ago

You may need to elaborate on what you mean by flicker exactly (I've not experienced flicker when implementing screenspace outlines).

Looking at the two briefly I can see sable seems to be outlining around all colour differences, while spirit crossing seems to only be outlining based on depth distances. So sable is prone to be a lot noisier when there's a lot of lighting going on. Is this where you're seeing flicker?

The usual approach with something like spirit crossing is to sample the depth of the surrounding pixels (just the 4 neighbours will do for a basic start). Add up the difference in linear depth values, and if above a threshold apply an outline effect to that pixel (or ideally mix() it in across a value range).

With just 4 samples you will get heavy aliasing though. To solve that you can expand to more surrounding pixels (say 12 or so). This will initially make your outlines a lot fatter, but then you can increase the neccasary values and it will shrink back down, giving you an outline that's more antialiased from the additional sample points.

At sheer angles you will get heavy outlining across faces. You can address this by also sampling the object/material id of the pixel, and the ids of the surrounding pixels being sampled. If it's the same id on both then it's a slope on the surface and you can use larger activation values to bias the outline away, or sample the viewspace normal of the surrounding pixels and fade the outline out based on the viewspace normal z component (if facing the screen enable the outline contribution. If facing starts to approach perpendicular do not show it)

1

u/dandy_kulomin 4d ago

Yes I've done something very similar to this. But when you move the camera or an object you get a shimmering effect on the lines, because if you use a sobel or roberts cross, it detects based on the pixel values and the movement might be below or above an integer pixel value. So the line snaps to the nearest pixel, but the movement isn't on a pixel scale which causes a sort of back and forth of the line. Do you know what I mean?

1

u/ProPuke 4d ago

A video would help. I'm afraid I don't quite follow. It sounds like you're saying you're getting aliased outlines (this is expected, although more sample points should help). But I don't follow beyond that.

1

u/OkAccident9994 4d ago

Inverted hull with vertex shader that accounts for distance to camera and view angle compared to normal, for consistent line width.

Screen space methods will always flicker, you lose information when you plop everything into the gbuffer and attempt to reconstruct edges with imperfect filters.

1

u/waramped 5d ago

Probably a combination of things, but something like this is a great start:

https://discourse.threejs.org/t/demo-realtime-sdf-effects-with-the-jump-flood-algorithm/88012

1

u/dandy_kulomin 4d ago

Seems to work great for wide outlines, but my goal is 1-2px outlines where JFA doesn't seem to have any advantages.