I have an idea for a method of faking 3D shadows in 2D, but I'm struggling to implement it due to my lack of experience. I want to achieve this by manipulating sprites with a custom shader.
TL;DR
I need a way for a CanvasItem shader to get the colour values of a sprite after a light has affected it, not before. CanvasGroups don't work for me. How could I achieve this?
My Plan
Each sprite to be shaded is actually constructed from two: the sprite when it's completely lit, and the sprite when it's in shade. It uses its channels in a special way to combine these and other information. Colours aren't stored directly but indexed to a palette.
R — Index values referencing a palette for when the sprite is Lit
G — Index values referencing a palette for when the sprite is in Shade
B — Height value for each pixel
A — Alpha
I have exaggerated the values for of the Lit, Shade and Height components to be more visible. They'd really be very dark.
A shader will use a palette texture (as a uniform) to colour in the sprite considering which parts are lit (it will use R) or in shade (it will use G). It will determine this by looking at the blue channel (height).
I want the fragment shader to look at the blue component and if it is zero or less the the green channel is used instead of the red channel.
How shadows are achieved:
Box with a ground shadow. Sun is from top left.
The shade the box casts can be thought of as a volume, a triangular prism.
Heightmap of this shade volume in blue channel (values exaggerated)
Shade heightmap blend mode set to subtract over character heightmap. Where this results in the players blue channel equalling zero there should be shade.
Sprite shader considers the blue channel and shades sprite accordingly.
Except I can't figure out how to have the sprite shader consider the Light2D properly. The fragment shader can't consider the effects of lights, because it's just acting on the sprite texture. I know there is a light function as well as the fragment function for CanvasItem shaders, but idk how to use this. I tried a CanvasGroup over the sprite which I then applied a shader to, but CanvasGroups don't have a texture accessible to shaders, leading to a white box.