Optimizing a stylized material from 3 passes to a single pass

Godot Version

4.6

Question

I’d like to pick someone’s brain for ideas on how this could be done. By my current knowledge - it is impossible.

Here’s how it looks:

It’s a classic lambert with a single directional light source and AO. There’s a cach though. Diffuse, ambient and AO intensities are all mapped to their respective ramped lambertian gradients (including cast shadows). The main motivations behind this is to confine ambient light (and AO) to dark parts only, thus preventing it from “contaminating” the illuminated parts.

I have this implemented as a 3 pass shader in Godot 4.3. Here’s how it’s currently working:

pass 1 - unshaded opaque white silhouette:

pass 2 - AO, confined to pixels that don’t see the light, overlaid over the previous pass:

pass 3 - lambert, the final pixel color is calculated entirely in the light() function and multiplied over the previous passes:

I also have it implemented as a single pass GLSL shader in OpenGL. This is easy to do when the fragment shader has the direct access to shadow maps and AO texture. However, Godot is not that flexible and although it looks like it should be simple, it’s an unfortunate combination of requirements that apparently can’t be squeezed into a single pass.

Any ideas on how Godot could be tricked into doing it in a single shader pass? Can be in 4.7 if some new rendering feature I’m not aware of appeared there.

1 Like