I did finally figure out a solution - actually a few different solutions. I did not figure out the actual cause of the artifact from a graphics programming perspective - but I did find some solutions.
As I previously stated in my question, it appeared that the artifact was only occurring on the pre-computed shadow volumes of the worldspawn static geometry (which is basically geometry that the engine knows is never going to move so it pre-calculates ahead-of-time the shadow volumes and other things with a command entered in the console called "dmap"). I did not figure out why it was only on the shadows of the static worldspawn geometry and not on any of the ASE or LWO models.
Now, the thing that I noticed was that there is actually a plethora of parameters that can be used with the dmap command - one of these parameters is called "shadowOpt" - which must stand for shadow optimization level. This parameter sets an enum - there appear to be a few different shadow optimization levels:
typedef enum {
SO_NONE, // 0 // NOTE: I haven't tried this one yet - should test this one.
SO_MERGE_SURFACES, // 1 // NOTE: this was the original default one - it causes some artifacts - the ones I have been trying to fix.
SO_CULL_OCCLUDED, // 2 // NOTE: this one works the best - takes a bit longer - but it has alot of unnecessary print statements that could probably be removed.
SO_CLIP_OCCLUDERS, // 3 // NOTE: I haven't tried this one yet - but it is not used anywhere.
SO_CLIP_SILS, // 4 // NOTE: I haven't tried this one yet - should test this one.
SO_SIL_OPTIMIZE // 5 // NOTE: this one doesn't seem to work well at all - and it takes an extrememly long amount of time - was probably an expirimental version.
} shadowOptLevel_t;
I have had success with option 2 - "SO_CULL_OCCLUDED". It fixes all the artifacts - it takes a bit longer to run - but I believe a lot of this time is spent printing large amounts of information to the console - these prints could probably be reduced or done away with.
One the of the places that gave me some clues was the comment here in tr_stencilshadow.cpp:
// if we are running from dmap, perform the (very) expensive shadow optimizations
// to remove internal sil edges and optimize the caps
if ( callOptimizer ) {
Now, the problem with only doing this "extra" shadow optimization during "dmap" is that if any of these lights are ever moved (which is always possible depending on the type of project you are doing) - it will then default back to the "un-optimized" real time shadow volume creation process (for the moved light) and the artifacts will reappear for that light. So the only way to guarantee that these artifacts will not appear is to always run the very expensive optimization process for these static worldspawn shadows. It is in fact very expensive so this would be an absolute last resort if you can't figure out a proper graphics solution. (if you do, make sure to post your solution here.)
I would recommend for anyone creating large maps for the vanilla Doom 3 engine - and using worldspawn geometry - that they create a cvar that they can change depending on their needs for the real-time creation of the optimized shadow volumes. I called my cvar r_useExpensiveShadowOptimizations - which appears to be an oxymoron. For example:
// if we are running from dmap, perform the (very) expensive shadow optimizations
// to remove internal sil edges and optimize the caps
if ( callOptimizer || r_useExpensiveShadowOptimizations.GetBool() ) {
I also recommend that depending on how large your maps are(and assuming the lights will not move), that you increase the static shadow volume optimization level with the "shadowOpt" parameter for dmap.
So basically all of the things you need to have a large map and not have shadow artifacts are there for you, you just need to decide which ones you will need to use. Doing it in real time is extremely expensive and should only be done as a last resort if you can't find a proper graphics solution. Doing it in DMAP makes perfect sense as it solves the problem and only takes a few more seconds for the map to compile.
float
). You've easily used up 5 of those. en.wikipedia.org/wiki/… \$\endgroup\$