View Issue Details

IDProjectCategoryView StatusLast Update
0005172The Dark ModCodingpublic15.05.2024 03:01
Reporterstgatilov Assigned Tostgatilov  
Status resolvedResolutionfixed 
Product VersionTDM 2.08 
Target VersionTDM 2.12Fixed in VersionTDM 2.12 
Summary0005172: Improve interaction culling: case when player-visible object is not visible from light
DescriptionThe engine does not cull away some shadows when it can.

Consider a typical scenario of two large rooms with one small open portal between them,
Suppose that an object is near the common wall between rooms, but far from the portal on this wall.
If the object is behind the wall, then player does not see it, and in most cases its shadow is culled away.

If the object is in front of the wall but the light source is behind the wall, then its shadow is also not visible.
In fact, in this case the light does not light the object at all.
The engine can detect this by seeing that light rays cannot pass into the object's area through the portal.
It seems that such interaction is not culled away, which is an opportunity for improvement.
Additional InformationExplained on dev forums:
TagsNo tags attached.


related to 0005084 resolvedstgatilov Interaction culling and Shadowhide WIP performance 
related to 0003818 resolvedstgatilov Parallel lights glitch 
related to 0005843 confirmedstgatilov Dmap computes wrong shadows on version 4 of NHAT 
related to 0006243 resolvedstgatilov Light volumes that should be culled bleeding into other vizleafs 
related to 0006306 resolvedstgatilov Deprecate parallel lights 
related to 0006321 resolvedstgatilov Weird textures issue w/SVN (TDM 2.12) 
related to 0006536 new Shadows2 texture broken 




13.12.2020 17:32

administrator   ~0013120

The reason such culling is not done is that traditional stencil shadows are not cast from front surfaces:

    // if we have a prelight model that includes all the shadows for the major world occluders,
    // we can limit the area references to those visible through the portals from the light center.
    // We can't do this in the normal case, because shadows are cast from back facing triangles, which
    // may be in areas not directly visible to the light projection center.
    if ( light->parms.prelightModel && r_useLightPortalFlow.GetBool() && light->lightShader->LightCastsShadows() ) {
        light->world->FlowLightThroughPortals( light );
    } else {
        // push these points down the BSP tree into areas
        light->world->PushFrustumIntoTree( NULL, light, light->inverseBaseLightProject, bounds_zeroOneCube );

Suppose there is a wall between areaA and areaB, with light in areaA.
The light sees the front side of the wall in areaA, which he won't cull away no matter what, but it does not drop shadows.
The back side of the wall is in areaB, which cast the shadows. But the light does not see it: it is occluded by the wall and not visible through portals.

Pretty sad.


22.12.2020 18:13

administrator   ~0013176

I think the line here must be crossed between closed (solid) and open (sheet) surfaces.

If an entity is composed of closed surfaces, then we can cull away its interaction if the entity is not visible from the light (shadow-casting one, of course).

The "if" above is not entirely right: it checks for prelightModel, which is the precomputed shadow for all world-area surfaces. In reality, area surfaces are not closed in most cases, thus it is not correct to prune them as suggested. However, most of the models are closed, and it is entirely OK to prune their interactions away, which could yield noticeable improvement in some cases.

So the idea is to store "closed" bit for surfaces and models, and do both branches here:
  FlowLightThroughPortals should decide which closed entities to consider for interactions
  PushFrustumIntoTree should decide which non-closed entities to consider for interactions
The problem is that this piece of code only decides which AREAs are visible by lights, not entities.
Moreover, it is done only when light position changes: it is not done every frame.

Perhaps it would make sense to run the same portal flow in idRenderWorldLocal::CreateLightDefInteractions.
Instead of simply taking all entities in all areas, go through portals and take only light-visible entities among closed ones.
Non-closed entities should be handled separately, e.g. by traversing areas -> entities.


26.03.2021 12:33

administrator   ~0013805

This forum thread shows that a lot of models are not closed enough:
(or we don't be able to detect it)


25.01.2023 12:43

reporter   ~0015883

I had logged a seperate tracker before it was suggested I like it here -



04.06.2023 11:57

administrator   ~0016011

I reread the above statement many times, and finally got to conclusion that "prelight" model is not something special.
The important part is to ensure that world models of all covered areas do cast shadows: whether these shadows are precomputed or dynamically generated does not matter.

So I extended this "light portal flow" optimization:
  r10392 Refactoring methods for creating area-entity/light refs.
  r10394 Use "light portal flow" code for all shadowing lights (to determine the areas light acts in).

Basically, the areas which are covered by light volume but are not reached by light flow are saved in a separate place.
When interactions are generated, we do special treatment for these areas, adding interactions only with special world-area entities.
The new code is under "r_useLightPortalFlow 2" cvar.

This helps a lot with number of lights and shadows in the view on a few maps I tested.
(Note: this info is displayed by r_showDefs cvar, but restarting/reloading save is necessary after a change in portal flow cvar).

Still, it does not help when light rays hit the area with entity but don't hit the entity itself.
Like the B2 case from the original investigation:


06.06.2023 08:00

administrator   ~0016013

Last edited: 16.12.2023 17:32

Light portal flow is more useful now:
  r10396 Use results of FlowLightThroughPortals / "light portal flow" to cull entities against it.

So now we avoid creation of light-entity interaction if we see that entity is in different area and light beams which hit this area surely don't hit entity.
This finally covers the case B2 from the original discussion.
In a sense, noshadowing lights might be more expensive now because they pass through walls, while shadowing lights don't =)

The optimization is enabled under cvar r_useLightPortalFlowCulling.
In the missions I tested, it took negligible time, so I hope there will be no problem with this new culling.


18.06.2023 17:28

administrator   ~0016022

After light portal flow was enabled for dynamic lights (svn rev 10394), a bug was noticed:
Basically, a light which has shadow-casting material and noshadows spawnarg in the map uses portal flow while it does not cast shadows from worls areas.

This is fixed now:
  r10414 Fixed bug with noshadows lights using light portal flow.


22.06.2023 06:25

administrator   ~0016029

Last edited: 24.06.2023 17:56

Due to the fact that parallel lights have totally broken behavior regarding areas and portals (0003818), I had to hack-disable the new optimizations for parallel lights.
See discussion here:

Note that the new parallelSky lights do use the new optimizations (0005121).

The actual commits:
  r10425 Hack parallel lights to work exactly as before, all light culling improvements disabled for them.


12.10.2023 19:21

administrator   ~0016126

Last edited: 26.11.2023 13:13

As usual, some maps are built incorrectly, and now they don't work:

In this case, doorway is composed or world brushes and entities.
The world brushes partly use caulk for uncertain reason, which means that they don't properly separate the areas.
Then entity is put on top of the caulk so that player does not see the absence of the surfaces where caulk is.

The missing shadow must be cast from the backface which is removed cue to caulk.
The entity does not cast shadow anymore because it is fully occluded by shadows from world geometry (well, it must be).

As far as I understand, this case won't work even in 2.11 if you replace moveable candle which generates light entity as attachment with a simple nonmoveable light.
In this case Doom 3 engine will generate and use "optimized shadows" from world geometry, and will skip casting shadows from the entity.
image.png (1,184,170 bytes)
image-2.png (813,962 bytes)


26.11.2023 13:25

administrator   ~0016189

Provided a workaround:
  r10518 Added workaround for stencil shadow leaks behind caulk brushes.

It adds two spawnargs:
* "forceShadowBehindOpaque" "1" on an entity means it will always cast shadows from any light, even if it is fully behind wall/opaque brushes.
* "forceAllShadowsBehindOpaque" "1" on worldspawn causes all entities to get forceShadowBehindOpaque flag automatically.

So when we find this bad "caulk brushes underneath a model" pattern, we can apply the spawnarg to this model to fix the issue.
But if there are too many such cases on an old mission, we can disable the optimization globally on everything in the mission.

Note that applying the workaround does NOT require re-dmapping, which is very important for fixing old missions.


26.11.2023 16:57

reporter   ~0016192

thank you.


01.01.2024 11:51

administrator   ~0016308

Last edited: 03.01.2024 14:09

Another pack of major changes:
  r10607 Avoid using light portal flow for some "ill-behaved" lights.
  r10608 Use Doom 3 style light frustum for all light culling, never use BFG-style frustum.
  r10609 Extended listRenderLightDefs and lightRenderEntityDefs commands.
  r10610 A bit more precise report on visibility in listRenderXyzDefs.
  r10611 Restored light culling optimizations based on area connectivity and blocked portals.

It was detected (0006321) that some lights are broken by trying to run light portal flow culling for them.
Or maybe it is better to say they are inherently broken by themselves (ill-defined) and enabling the culling changes their behavior.
More information here:


11.02.2024 12:08

administrator   ~0016505

Some minor fixes for the workaround:
  r10639. Save/restore renderEntity flag forceShadowBehindOpaque.
  r10642. Set forceShadowBehindOpaque on all doors with visportal connected.

Issue History

Date Modified Username Field Change
09.03.2020 04:31 stgatilov New Issue
09.03.2020 04:31 stgatilov Status new => assigned
09.03.2020 04:31 stgatilov Assigned To => stgatilov
09.03.2020 04:32 stgatilov Relationship added related to 0005084
21.03.2020 17:43 stgatilov Target Version => TDM 2.09
05.12.2020 12:35 stgatilov Target Version TDM 2.09 => TDM 2.10
13.12.2020 17:32 stgatilov Note Added: 0013120
22.12.2020 18:13 stgatilov Note Added: 0013176
26.03.2021 12:33 stgatilov Note Added: 0013805
22.11.2021 14:04 stgatilov Target Version TDM 2.10 => TDM 2.11
15.11.2022 04:04 nbohr1more Target Version TDM 2.11 => TDM 2.12
25.01.2023 12:40 Bikerdude Relationship added related to 0006243
25.01.2023 12:43 Bikerdude Note Added: 0015883
04.06.2023 11:57 stgatilov Note Added: 0016011
06.06.2023 08:00 stgatilov Note Added: 0016013
06.06.2023 08:00 stgatilov Status assigned => resolved
06.06.2023 08:00 stgatilov Resolution open => fixed
06.06.2023 08:00 stgatilov Fixed in Version => TDM 2.12
18.06.2023 17:28 stgatilov Note Added: 0016022
22.06.2023 06:22 stgatilov Relationship added related to 0003818
22.06.2023 06:25 stgatilov Note Added: 0016029
22.06.2023 06:33 stgatilov Relationship added related to 0006306
24.06.2023 17:56 stgatilov Note Edited: 0016029
12.10.2023 19:21 stgatilov Note Added: 0016126
12.10.2023 19:21 stgatilov File Added: image.png
12.10.2023 19:21 stgatilov File Added: image-2.png
26.11.2023 13:13 stgatilov Note Edited: 0016126
26.11.2023 13:25 stgatilov Note Added: 0016189
26.11.2023 16:57 Bikerdude Note Added: 0016192
16.12.2023 17:32 stgatilov Note Edited: 0016013
24.12.2023 10:11 stgatilov Relationship added related to 0006321
01.01.2024 11:51 stgatilov Note Added: 0016308
01.01.2024 11:52 stgatilov Status resolved => feedback
01.01.2024 11:52 stgatilov Resolution fixed => reopened
03.01.2024 14:09 stgatilov Note Edited: 0016308
28.01.2024 02:47 nbohr1more Relationship added related to 0005843
11.02.2024 12:08 stgatilov Note Added: 0016505
25.02.2024 02:07 nbohr1more Status feedback => resolved
25.02.2024 02:07 nbohr1more Resolution reopened => fixed
15.05.2024 03:01 nbohr1more Relationship added related to 0006536