Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Мужик с Чашкой

Faking Dynamic Shadows

Recommended Posts

While working upon my mod "Zero Brightness", I searched the web to find the means to easily implement proper dynamic shadows into the GZDoom. Sadly, I only found the topic with the relevant question and the Graf himself answering, that GZDoom literally cannot do anything than 2D shadowmaps.
After some time I came to realization, that even if GZDoom don't have the tools to draw proper shadowmaps, it still has all the tools to fake it!

My hypothesis was based on two main points:

  • GZDoom supports dynamic pixel pointlights.
  • GZDoom supports calculations of traces from one point to another with ability to spawn pointlights on one end of the path (basically how bullet puff lighting works)

 

The approximate algorithm of the light emission would look like this:

  • Establish the light emitter as an object.
  • Shoot tracers from it in a equal grid pattern.
  • When tracer meets the object to collide with (wall, floor, ceiling, monster) - spawn a point light in place of collision. The size of pointlight must correlate to the distance from the emitter (the further it is, the bigger the pointlight must be).
  • If light emitter shoots rays once and pointlights have infinite time of existence - then it can be established as a baked lighting. If light emitter constantly shoots new rays and pointlights have limited life time (at the moment when a pointlight is removed, it instantly replaced by a new pointlight) - then lighting will be fully dynamic.

Graph

 

To test my hypothesis I have used a simple Decoration, that shoots custom bullets with random spread in certain degrees. These bullets had no damage, decals or anything else but a path that ends with a permanent pointlight with fixed size in the place of collision.

The experiment resulted in success.

image.png.1d3b5d8a39be4c8b3a5367cd4468b53b.png

 

The light emitter drew a proper soft shadow of the space ahead of it.

The number of pointlights was 4096 - which was equal to a 64x64 pixels shadowmap texture resolution. 
Both GZDoom and my PC handled it surprisingly well in FullHD resolution, despite the lack of optimization in the algorithm (light was constantly updating every tic possible no matter which area was visible; rays were arranged randomly and not in grid). This make this mean of achieving true dynamic shadows or even CSM (Cascaded Shadow Maps) in GZDoom a possibility even with use of primitive Decorate code. Sadly I have neither time nor mental resources to comprehend the ZScript to push the algorithm even further, but I don't see the reason why the more qualified and talented programmers wouldn't be able to optimize or improve it.

 

However, this method has one downside: since "shadowmap" is created not from the depth buffer, but from a collision buffer of sorts, it cannot see alpha channels as transparent and any object that has collision will be drawn on shadowmap as a big dark square. I saw how Relighting Doom has managed to implement sprite shadows from monsters to be drawn on walls, so there is always an option to make light pass contact only with map geometry, while shadows from objects will be drawn as sprite projections. Other problem that comes from the collision buffer shadowmap is improper shadowing of the midtextures like chainfences or cage bars, since they are either completely block the light or allow it all to come through without stopping it.

 

Here is the video how my primitive implementation works in action. Ignore the blood, since every ray is basically a bullet trace, but pay attention to how both player and demon are casting a shadow onto the wall:

 


How it all looks in Decorate/Gldefs code:

Spoiler

//THIS SECTION COMES FROM DECORATE.txt

ACTOR TestLight 1509
{
	Height 20
	Monster
	+STANDSTILL
	+NOBLOCKMAP
	+NOGRAVITY
	+LOOKALLAROUND
	-SHOOTABLE
	States
	{
	Spawn:
		TNT1 A 5 A_LookEx (LOF_NOSOUNDCHECK, 0, 0, 0, 0, "ContFire")
		Loop

	ContFire:
                                   TNT1 A 1 A_CustomBulletAttack(20, 20, 256, 0, "TestLightPuff", 2048, CBAF_AIMFACING|CBAF_NORANDOM|CBAF_NOPITCH|CBAF_NORANDOMPUFFZ)
		Loop
	See:
		TNT1 A -1
		Stop
  }
}

ACTOR TestLightPuff
{
  +NOBLOCKMAP
  +NOGRAVITY
  RenderStyle Translucent
  Alpha 0.0
  States
  {
  Spawn:
    PUFF A 16
    // Intentional fall-through
  Melee:
    TNT1 A 1
    Stop
  }
}


//THIS SECTION COMES FROM GLDEFS.txt

// LIGHTTEST
pointlight TESTLIGHTPUFFLIGHT
{
    color 0.2 0.2 0.2
    size 8
    offset 0 0 0
	attenuate 0
}

object TestLightPuff
{
    frame PUFFA { light TESTLIGHTPUFFLIGHT }
}

 

 

 

P.S. I'm too inept to go to ZDoom forums so I drop this piece of info here. Maybe some day it will make it to some really smart programmer to turn this concept into a full fleshed implementation of dynamic lighting.

Edited by Мужик с Чашкой

Share this post


Link to post

Don't feat zdoom forums, there are frendlies there ;)

 

By looking at the code, I think it could be made even better in zscript  by making a custom fuction and not using A_Custombulletattack(), which has unwanted effect on player. I think by using linetraces,  but I'm not skilled in this area, so I can't give advices.

Share this post


Link to post
1 hour ago, ramon.dexter said:

Don't feat zdoom forums, there are frendlies there ;)

 

By looking at the code, I think it could be made even better in zscript  by making a custom fuction and not using A_Custombulletattack(), which has unwanted effect on player. I think by using linetraces,  but I'm not skilled in this area, so I can't give advices.


Yeah it would be. I have a clear picture how the algorithm should look like, but I don't know how to make it using zscript since I literally have a caveman level of understanding that programming language. There is a "Flashlight++" mod which basically does what I want: tracing a ray to the nearest collision plane using Vector3 action, spawning a pointlight there and scaling it relatively to the distance from the emitter (player), and also allows emitting rays with preset offset from the initial vector coordinates. We only need someone to copy that code, change the emitter origin from the player dummy to the separate object and then force it to emit more than one ray in correct grid.

Share this post


Link to post
11 hours ago, Мужик с Чашкой said:

While working upon my mod "Zero Brightness", I searched the web to find the means to easily implement proper dynamic shadows into the GZDoom. Sadly, I only found the topic with the relevant question and the Graf himself answering, that GZDoom literally cannot do anything than 2D shadowmaps.
After some time I came to realization, that even if GZDoom don't have the tools to draw proper shadowmaps, it still has all the tools to fake it!

My hypothesis was based on two main points:

  • GZDoom supports dynamic pixel pointlights.
  • GZDoom supports calculations of traces from one point to another with ability to spawn pointlights on one end of the path (basically how bullet puff lighting works)

 

The approximate algorithm of the light emission would look like this:

  • Establish the light emitter as an object.
  • Shoot tracers from it in a equal grid pattern.
  • When tracer meets the object to collide with (wall, floor, ceiling, monster) - spawn a point light in place of collision. The size of pointlight must correlate to the distance from the emitter (the further it is, the bigger the pointlight must be).
  • If light emitter shoots rays once and pointlights have infinite time of existence - then it can be established as a baked lighting. If light emitter constantly shoots new rays and pointlights have limited life time (at the moment when a pointlight is removed, it instantly replaced by a new pointlight) - then lighting will be fully dynamic.

Graph

 

To test my hypothesis I have used a simple Decoration, that shoots custom bullets with random spread in certain degrees. These bullets had no damage, decals or anything else but a path that ends with a permanent pointlight with fixed size in the place of collision.

The experiment resulted in success.

image.png.1d3b5d8a39be4c8b3a5367cd4468b53b.png

 

The light emitter drew a proper soft shadow of the space ahead of it.

The number of pointlights was 4096 - which was equal to a 64x64 pixels shadowmap texture resolution. 
Both GZDoom and my PC handled it surprisingly well in FullHD resolution, despite the lack of optimization in the algorithm (light was constantly updating every tic possible no matter which area was visible; rays were arranged randomly and not in grid). This make this mean of achieving true dynamic shadows or even CSM (Cascaded Shadow Maps) in GZDoom a possibility even with use of primitive Decorate code. Sadly I have neither time nor mental resources to comprehend the ZScript to push the algorithm even further, but I don't see the reason why the more qualified and talented programmers wouldn't be able to optimize or improve it.

 

However, this method has one downside: since "shadowmap" is created not from the depth buffer, but from a collision buffer of sorts, it cannot see alpha channels as transparent and any object that has collision will be drawn on shadowmap as a big dark square. I saw how Relighting Doom has managed to implement sprite shadows from monsters to be drawn on walls, so there is always an option to make light pass contact only with map geometry, while shadows from objects will be drawn as sprite projections. Other problem that comes from the collision buffer shadowmap is improper shadowing of the midtextures like chainfences or cage bars, since they are either completely block the light or allow it all to come through without stopping it.

 

Here is the video how my primitive implementation works in action. Ignore the blood, since every ray is basically a bullet trace, but pay attention to how both player and demon are casting a shadow onto the wall:

 


How it all looks in Decorate/Gldefs code:

  Reveal hidden contents


//THIS SECTION COMES FROM DECORATE.txt

ACTOR TestLight 1509
{
	Height 20
	Monster
	+STANDSTILL
	+NOBLOCKMAP
	+NOGRAVITY
	+LOOKALLAROUND
	-SHOOTABLE
	States
	{
	Spawn:
		TNT1 A 5 A_LookEx (LOF_NOSOUNDCHECK, 0, 0, 0, 0, "ContFire")
		Loop

	ContFire:
                                   TNT1 A 1 A_CustomBulletAttack(20, 20, 256, 0, "TestLightPuff", 2048, CBAF_AIMFACING|CBAF_NORANDOM|CBAF_NOPITCH|CBAF_NORANDOMPUFFZ)
		Loop
	See:
		TNT1 A -1
		Stop
  }
}

ACTOR TestLightPuff
{
  +NOBLOCKMAP
  +NOGRAVITY
  RenderStyle Translucent
  Alpha 0.0
  States
  {
  Spawn:
    PUFF A 16
    // Intentional fall-through
  Melee:
    TNT1 A 1
    Stop
  }
}


//THIS SECTION COMES FROM GLDEFS.txt

// LIGHTTEST
pointlight TESTLIGHTPUFFLIGHT
{
    color 0.2 0.2 0.2
    size 8
    offset 0 0 0
	attenuate 0
}

object TestLightPuff
{
    frame PUFFA { light TESTLIGHTPUFFLIGHT }
}

 

 

 

P.S. I'm too inept to go to ZDoom forums so I drop this piece of info here. Maybe some day it will make it to some really smart programmer to turn this concept into a full fleshed implementation of dynamic lighting.

 

Believe it or not, this has actually been done before in similar fashion, but it is called raytracing instead:

  • The Power of Light (by ibm5155) Jan 31, 2017 - A demoscene benchmark to show off dynamic lights and shadows, using tracers of rays, DECORATE + ACS ported to GZ, QZ, K8Vavoom and Vavoom
  • Concept of Raytracing Shadows (by ibm5155) Jul 03, 2016
    • ''At the end, it was required 4500 dynamic lights to simulate all those effects (it could reach 5K if I added the missing tracers near the door) What this effect does: add tracers all over the walls/ceilings/floors and use a jump if target in los to check if the lost soul is "emiting light over that tracer", if it's emiting light, do nothing, if not, Spawn some dark dynamic lights near it. The code isn't perfect since I'm not a pro with decorate (that's why it lags like hell on start, because many tracers didnt saw the lost soul)''.
  • Boris's Lighting Mode plugin achieves (static) shadows.

Share this post


Link to post
18 hours ago, Redneckerz said:

 

Believe it or not, this has actually been done before in similar fashion, but it is called raytracing instead:

  • The Power of Light (by ibm5155) Jan 31, 2017 - A demoscene benchmark to show off dynamic lights and shadows, using tracers of rays, DECORATE + ACS ported to GZ, QZ, K8Vavoom and Vavoom
  • Concept of Raytracing Shadows (by ibm5155) Jul 03, 2016
    • ''At the end, it was required 4500 dynamic lights to simulate all those effects (it could reach 5K if I added the missing tracers near the door) What this effect does: add tracers all over the walls/ceilings/floors and use a jump if target in los to check if the lost soul is "emiting light over that tracer", if it's emiting light, do nothing, if not, Spawn some dark dynamic lights near it. The code isn't perfect since I'm not a pro with decorate (that's why it lags like hell on start, because many tracers didnt saw the lost soul)''.
  • Boris's Lighting Mode plugin achieves (static) shadows.

I'am aware of the ibm5155 demoscene, but the tech is completely different: he achieves equal shadow resolution by covering the whole scene in prepositioned shadows which activate once they don't see the light source. It's a nice and inventive way of getting shadows, but:
1. It's not raytracing, since rays are not emitted, but constantly connected to the preprogrammed "light source", placed all over the map.
2. This way of making shadows requires serious preparation for each map and way too unoptimised since all shadowpoints constantly check if they see light source or not. I just put my light source on any map in any place and it's just works, cause I emit rays, not check line of sight.

 

Boris's lighting mode plugin is making linedef shadows which are not hot news since 1993. GZDoom already have inbuild 2D dynamic shadow mapping. Making it take floor and wall height into the account is another thing.

Share this post


Link to post

This is very cool stuff. I've been puzzling if any kind of directed light is possible in the GZDoom engine, since the dynamic lights work off the BSP and are flat. I assume your method does NOT work with a missile light e.g. imp fireball?

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×