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
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
What would be the best approach for this? I want to avoid using one rendertarget for each room.
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
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?