questions about shadow node setup Topic is solved

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


jwwalker
Goblin
Posts: 248
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 18

questions about shadow node setup

Post by jwwalker »

I'm looking at Sample_ShadowMapFromCode, specifically the function createPcfShadowNode(). I understand some of it, but have some questions:
  1. What is the correspondence between parts of the shadow node parameters and the actual lights? For instance, is the "first" light the first one created, or the first one seen in some traversal of the scene graph, or what?
  2. What happens if the shadow node is set up for more lights than you actually have?
  3. Is there a reason why shadowParam.resolution[3] is set but shadowParam.atlasStart[3] is not set?
  4. Are there rules to how you lay out the atlas, how big it can be, and when you should use a new atlasId?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: questions about shadow node setup

Post by dark_sylinc »

jwwalker wrote: Tue Nov 09, 2021 12:53 am What is the correspondence between parts of the shadow node parameters and the actual lights? For instance, is the "first" light the first one created, or the first one seen in some traversal of the scene graph, or what?
Lights are sorted by distance to camera; hence closest light is light 0, 2nd closest is light 1, and so on.

When it comes to directional lights though, IIRC the order of creation is what matters (or it may be random, I can't recall), since directional lights don't have a "distance" to camera and are all-encompassing.

All (shadow-casting) directional lights will therefore come first before any point/spot lights. Note that our PSSM implementation only supports one directional light.

The exception is if you're using setLightFixedToShadowMap; where you explicitly assign a light to a given shadow map idx.
jwwalker wrote: Tue Nov 09, 2021 12:53 am [*]What happens if the shadow node is set up for more lights than you actually have?
The GPU VRAM is wasted.
Because the atlas is usually cleared every frame (i.e. except for static lights) the BW and time clearing region of an unused space is also wasted.

But the shader will be compiled to not use those lights, so that performance won't be wasted.
(note: this optimization of ours may cause performance problems of a different kind though)
jwwalker wrote: Tue Nov 09, 2021 12:53 am [*]Is there a reason why shadowParam.resolution[3] is set but shadowParam.atlasStart[3] is not set?
If you mean createPcfShadowNode, numPssmSplits = 3u therefore shadowParam.resolution[3] is not actually used.
If numPssmSplits = 4 then you're right, shadowParam.atlasStart[3] would have to be set otherwise it would overlap with other splits.
jwwalker wrote: Tue Nov 09, 2021 12:53 am [*]Are there rules to how you lay out the atlas, how big it can be, and when you should use a new atlasId?
How big? GPU texture limits applies (RenderSystemCapabilities::getMaximumResolution2D) but every modern GPU nowadays supports up to 16384x16384 so that's really a non-issue. At that resolution performance and memory (16384 * 16384 * 4 bytes for D32 = 1GB of VRAM) are a bigger concern.

I suppose that at 16kx16k floating point precision problems might cause problems though (it's the main reason we never saw resolutions past 16k: aside from the enormous memory consumption; there's only so many fractions that can be represented by 32-bit floating point AND their subfractions for bilinear interpolation between pixels).

How to layout the atlas? If you manage to lay them in a way that the final texture is a power of 2, then excellent. Other than that, just avoid overlap.

I placed a few asserts on stuff that may cause trouble but nobody ran into; the only one I can find right now is:

Code: Select all

assert( (smCamera.scenePassesViewportSize[firstBitSet].x < Real( 0.0 ) ||
         smCamera.scenePassesViewportSize[firstBitSet].y < Real( 0.0 ) ||
         smCamera.scenePassesViewportSize[firstBitSet] == vpSize) &&
        "Two scene passes to the same shadow map have different viewport sizes! "
        "Ogre cannot determine how to prevent jittering. Maybe you meant assign "
        "assign each light types to different passes but you assigned more than "
        "one light type (or the wrong one) to the same pass?" );
and when you should use a new atlasId
In rare cases you may find that adding splitting in two atlas makes it easier to make a texture power of 2; and through profiling you verified it actually matters to performance.

Other than that; the main reason to use a different atlasId is because you want different update frequencies. I.e. if you're manually controlling shadows via setLightFixedToShadowMap; you don't want to (e.g.) mix shadows that are updated every frame with ones that are updated on even/odd frames, or with shadows that are only updated on demand; because target clears applies to the whole texture, not to a subregion.
jwwalker
Goblin
Posts: 248
Joined: Thu Aug 12, 2021 10:06 pm
Location: San Diego, CA, USA
x 18

Re: questions about shadow node setup

Post by jwwalker »

Thanks, that's helpful. One more thing. Is there a significant cost to having a shadow node continue to exist after you stop using it? If so, how do you clean it up? The fact that ShadowNodeHelper::createShadowNodeWithSettings takes a name of type String while CompositorManager2::removeShadowNodeDefinition takes an IdString makes me suspect that they are not quite talking about the same thing.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5436
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1343

Re: questions about shadow node setup

Post by dark_sylinc »

The shadow node definition is only a few kb if memory. It makes little sense to keep adding and removing it (in fact I am not familiar with the performance characteristics of that; because shadow node defs are usually defined once; and then only change/recreated when graphics settings are altered)

What's more costly is the shadow node instance(*), which is instantiated with the workspace when you call addWorkspace.

Shadow node instantiation (and destruction) can be expensive because the API may take time to create/destroy the requested textures

(*) Most of the compositor is designed like that: you have a class that acts as the definition, and another class which is instantiation. All instantiations share the same definition, e.g.

Code: Select all

class CompositorStuffDefinition
{
// shared settings
};

class CompositorStuff
{
 CompositorStuffDefinition *mDefinition; // Pointer to shared settings
 
  TextureGpu *mTextures[...]; // Textures needed by CompositorStuff
 // Unique settings
};