Draw items "behind" a wall

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
fernandezvara
Prole
Posts: 2
Joined: Wed Oct 25, 2023 2:28 pm

Draw items "behind" a wall

Post by fernandezvara »

I'm programming a game that has walls and the player and other entities hide before them by using stencil. The thing is that I would like to show the items behind but with some opacity instead of its hide.

After looking at the documentation I was not able to find a sample to achieve something like this, only to hide all the contents.

Can someone point me in some direction to investigate more? or some sample?

I really appreciate any help you can provide.
Ross
Party member
Posts: 100
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Draw items "behind" a wall

Post by Ross »

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
e-6000_silhouette_mask_texture.png (8.28 KiB) Viewed 23420 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
screenshot_2023-10-27_10-03-54.png (360.5 KiB) Viewed 23420 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.
fernandezvara
Prole
Posts: 2
Joined: Wed Oct 25, 2023 2:28 pm

Re: Draw items "behind" a wall

Post by fernandezvara »

Thanks for the in-depth explanation, beyond my expectations. I will try the idea of modifying the current layer for stencil for this one.
RNavega
Party member
Posts: 355
Joined: Sun Aug 16, 2020 1:28 pm

Re: Draw items "behind" a wall

Post by RNavega »

fernandezvara wrote: Wed Oct 25, 2023 2:47 pm I'm programming a game that has walls and the player and other entities hide before them by using stencil. The thing is that I would like to show the items behind but with some opacity instead of its hide.
You can still do that with the stencil test, you just change the logic you use.
You clear the stencil buffer, then populate it with the objects that hide things, like placing 1 on their pixels. So the stencil buffer has 0 on open pixels and 1 on occupied pixels.

Then you set the stencil test to "equal" to 1 and draw all obscurable sprites with half transparency. Only parts that overlap occupied pixels will be drawn -- effectively drawing them on top of walls. Some games also draw a "silhouette", like fully opaque but with a flat color like turquoise or something like that.
Then you set the stencil test to "equal" to 0 and draw all obscurable sprites as fully opaque. Only the parts that are in open pixels, outside walls, will be drawn.
Post Reply

Who is online

Users browsing this forum: Semrush [Bot], whateverest and 6 guests