Lighting issue using Rendertargets and blending

I am using a rendertarget for creating a shadow in closed rooms. This shadow is then subtracted through light sources, using a custom blending. So far this is producing the expected results. But this has a drawback.
In the image below, the outer lightsource is not affecting the shadow in the room
image

but since i am using one rendertarget for all rooms, if i place a torch in a room, this affects the lighting in the adjacent room, like so
image

What would be the best approach for this? I want to avoid using one rendertarget for each room.

Let me preface here with… I have no idea! :slight_smile:

With that out of the way, what if you were to create a mask for that room’s geometry and subtract that from the light source before you apply it? Basically, make the applied light, per room, bounded by the shape of the room.

Can you do that without another RenderTarget? I’m not sure :frowning:

Use stencil, unique mask per room (create permutation grid to prevent neighbors with same id if you have lot of rooms). When rendering light in room then set reference value of rendered light same as id of given room. Stencil pass on Equal.

I use a render target for drakness, a render target for each ligth, and a render target for the normal world, fully lit…

Draw the world as per usual… Draw the darkness on top, now everything is drawn, but covered in black.
Its dark all over…

Then you get a light. one at a time, but you can have any amount… You draw the lights own sprite, or radial gradient if you will, to its own render/target… Then you subtract anything that diminishes your light, such as shadows and or shading on objects inside the radius of that light… This leaves you with a fully rendered light, shadows, shading, and all… Which you then SUBTRACT from the darkness…

Your world will now be obscured by darkness, except for the holes punched by lights.
-You can simply adjust the alpha value of the darkness being drawn, to adjust ambient light.

Thanks for all the replies. I am currently trying the Stencil approach. I have tried this:

GraphicsDevice.SetRenderTarget(darknessInteriorTiles);
            GraphicsDevice.Clear(new Color(0, 0, 0, 0));
            int idx = (int)((shadowLevel - 0.5f) * 2.0f * 127.0f);
            BlendState bs2 = shadowLevel > 0.5f ? blend2s[idx] : blend2;

            foreach (var room in MapStore.ActiveMap.Sim.RoomFinder.Rooms)
            {
                var dsMask = new DepthStencilState();
                dsMask.StencilEnable = true;
                dsMask.StencilFunction = CompareFunction.Always;
                dsMask.StencilPass = StencilOperation.Replace;
                dsMask.ReferenceStencil = room.Stencil;
                dsMask.DepthBufferEnable = false;

                _spriteBatch.Begin(blendState: bs2, transformMatrix: cam.Transform, depthStencilState: dsMask);
                // create "darkness" with the specific day/night darkness intensity, but at least 0.5 i.e. 127 alpha
                foreach (var tile in MapStore.ActiveMap.StoreTile.Tree.GetNodesInside(cam.VisibleArea))
                {
                    if(room.Tiles.Contains(new Location(tile.Position.X / Tile.Width, tile.Position.Y / Tile.Height)))
                    {
                        tile.Render(_spriteBatch, cam);
                    }
                }
                _spriteBatch.End();
            }

            foreach (var room in MapStore.ActiveMap.Sim.RoomFinder.Rooms)
            {
                var dsPass = new DepthStencilState();
                dsPass.StencilEnable = true;
                dsPass.StencilFunction = CompareFunction.Equal;
                dsPass.StencilPass = StencilOperation.Keep;
                dsPass.ReferenceStencil = room.Stencil;
                dsPass.DepthBufferEnable = false;

                _spriteBatch.Begin(blendState: blend2_2, transformMatrix: cam.Transform, depthStencilState: dsPass);
                // light up previously darkened areas
                foreach (var light in room.Lights)
                {
                    light.RenderLight(_spriteBatch, cam);
                }
                _spriteBatch.End();
            }

but this doesn’t change anything. My idea was drawing the shadow of each room with a specific stencil id, different one for each room. Then draw the lights. For example light in room A has the same stencil Id as the previously drawn darkness for room A.
What am i missing here?

You could try to draw one light first, Clear the stencil buffer and repeat.
https://docs.monogame.net/api/Microsoft.Xna.Framework.Graphics.ClearOptions.html

You can get some other ideas by looking at the code of Penumbra.

I also have a small sample that takes a different approach. It draws 3D shadows, restricted to a 2D plane.

I have it working now. Problem was, the render target has to be created with stencil, like so

darknessInteriorTiles = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);
2 Likes