Take a look at this thread:
viewtopic.php?p=246776
The way I ended up doing it:
Step 1: Draw your objects as a black & white mask to a render texture, in back-to-front order. Overlapping objects are white, silhouette-able objects are black.
You can output to multiple render textures at once from a shader, so I could do this in one pass with normal rendering.
- e-6000_silhouette_mask_texture.png (8.28 KiB) Viewed 23418 times
Step 2: Redraw all silhouette-able objects with a custom shader. In this shader, read from the mask texture and only draw the silhouette for pixels that are white in the mask.
The final result:
- screenshot_2023-10-27_10-03-54.png (360.5 KiB) Viewed 23418 times
My overall drawing process code:
Code: Select all
love.graphics.setCanvas(maskCanvas)
love.graphics.clear(0, 0, 0, 1) -- Silhouette shader requires a black background, not transparent.
love.graphics.setCanvas(canvas, maskCanvas)
love.graphics.setShader(maskShader)
layer:draw() -- Draw all objects.
love.graphics.setCanvas(canvas)
love.graphics.setShader(silhouetteShader)
layer:drawSilhouettes() -- Draw silhouettes on top.
love.graphics.setShader()
The main fragment shader that draws objects normally and their black or white mask at the same time:
Code: Select all
uniform Image MainTex;
extern float isSilhouetteable = 0.0;
void effect() {
// Draw regular image color to main canvas.
vec4 texturecolor = Texel(MainTex, VaryingTexCoord.xy);
love_Canvases[0] = texturecolor * VaryingColor;
// Draw black or white silhouette to mask canvas.
float s = 1 - isSilhouetteable;
vec4 maskColor = vec4(s, s, s, texturecolor.a);
love_Canvases[1] = maskColor;
}
You need to use shader:send() to set the "isSilhouetteable" setting to either 0 or 1 for each object. I have my object drawing code set up to only send this when it actually needs to change, so it only breaks the batch when necessary.
And the silhouette shader (for drawing the "behind" bits on top):
Code: Select all
extern Image mask;
extern vec2 maskSize;
extern vec4 silColor = vec4(0, 1, 0, 0.5);
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) {
vec4 maskCol = Texel(mask, screen_coords/maskSize);
vec4 texCol = Texel(tex, texture_coords);
return vec4(silColor.rgb, silColor.a * texCol.a * maskCol.r);
}
Of course this is only one method. There are other ways to do it. If you only have
one object that you want to show behind things or a limited number or location of "walls", then it's much easier. I wanted something that could handle any number of movable objects silhouetted behind anything anywhere, haha.
Note: My drawing setup is probably different from yours. I'm not using any stencils. I just sort my objects by their base Y position and draw them back to front so they all overlap correctly.