[2.0] Multi-pass Lighting with ShadowNode is not working

Problems building or running the engine, queries about how to use features etc.
Post Reply
everlight
Gnoblar
Posts: 1
Joined: Sat Apr 09, 2016 4:43 am

[2.0] Multi-pass Lighting with ShadowNode is not working

Post by everlight »

Hi, I'm working on porting my project form 1.9 to 2.0.
I'm using deferred-lighting on opaque object, and multi-pass lighting on transparent object.

Multi-pass lighting is applied by 4 passes below: (In detail, passes aren't loaded by material script. customized script is loaded by custom parser, and passes that be similar to below are generated automatically.)

Code: Select all

// ambient pass
pass 
{
	diffuse 0 0 0
	specular 0 0 0 0
	illumination_stage ambient
}

// point light pass
pass 
{
	ambient 0 0 0
	emissive 0 0 0
	scene_blend add
	illumination_stage per_light
	iteration once_per_light point
}

// spotlight light pass
pass 
{
	ambient 0 0 0
	emissive 0 0 0
	scene_blend add
	illumination_stage per_light
	iteration once_per_light spotlight
}

// directional light pass
pass 
{
	ambient 0 0 0
	emissive 0 0 0
	scene_blend add
	illumination_stage per_light
	iteration once_per_light directional
}

In this situation, the problems are occurred in 'SceneManager::renderSingleObject' fuction.

Code: Select all

if (doLightIteration)
{
    ...

    const LightList* pLightListToUse;
    // Start counting from the start light
    size_t lightIndex = pass->getStartLight();
    size_t depthInc = 0;

    while (lightsLeft > 0)
    {
        // Determine light list to use
        if (iteratePerLight)
        {
            localLightList.resize(pass->getLightCountPerIteration());

            LightList::iterator destit = localLightList.begin();
            for (; destit != localLightList.end()
                    && lightIndex < rendLightList.size(); 
                ++lightIndex, --lightsLeft)
            {
                Light const * currLight = rendLightList[lightIndex].light;

                // Check whether we need to filter this one out
                if ((pass->getRunOnlyForOneLightType() && 
                    pass->getOnlyLightType() != currLight->getType()) ||
                    (pass->getLightMask() & currLight->getLightMask()) == 0)
                {
                    // Skip
                    continue;
                }

                *destit = rendLightList[lightIndex];
                ++destit;
            }
                    
			...
            pLightListToUse = &localLightList;
            ...
        }
        else // !iterate per light
        {
			...
        }

        fireRenderSingleObject(rend, pass, mAutoParamDataSource, pLightListToUse, mSuppressRenderStateChanges);

        // Do we need to update GPU program parameters?
        if (pass->isProgrammable())
        {
            if( mCurrentShadowNode )
            {
                pLightListToUse = mCurrentShadowNode->setShadowMapsToPass( rend, pass,
                                                                            mAutoParamDataSource,
                                                                            pass->getStartLight() );
            }

            useLightsGpuProgram(pass, pLightListToUse);
        }
'LightList' pointed by 'pLightListToUse' has only one light in each light-iteration, and 'lightIndex' has light index value for next iteration.
This situation is suitable for multi-pass lighting.

But, if scene-manager has a shadow-node, the problems are occurred.


Image Image Image
<Light1 / Light2 / Light1+Light2>

Firstly, light properties on shader are not updated while light-iteration, even if there are multiple lights of various properties. It is like that there is only light.

'pLightListToUse' pointer is overwritten by return value of 'CompositorShadowNode::setShadowMapsToPass' function.
The function returns list of all lights that be queried by renderable, and its results are always same while light-iteration.
Because 'pLightListToUse' is not changed in light-iteration, 'mGpuParamsDirty |= GPV_LIGHTS' statement in 'useLightsGpuProgram' function is exceuted once while light iteration.

When I add 'mGpuParamsDirty |= GPV_LIGHTS' statement to begin of light iteration, the problem is fixed.

Image
<Light1+Light2(Fixed)>





Image Image Image
<Shadow1 / Shadow2 / Shadow1+Shadow2>

Secondly, shadow-map texture references assigned by 'CompositorShadowNode::setShadowMapsToPass' function are not consecutive but shared.
When there is only one light, shadow is very good. But, when there are two or more lights casting shadow, shadow is only one rendered.

Actually, although shadow-map textures are rendered on each light and shadow-mapping are performed on each pass while light-iteration, it is wrong to match texture-unit and shadow-map textures.
When I examine this situation by PIX, I found that two spotlight passes have same shadow-map texture pointer.

It is also caused due to 'CompositorShadowNode::setShadowMapsToPass' function. This function sets all shadow-map textures to texture units in current pass regardless of progress of light-iteration.
Thus, its results are always same while light-iteration, and each pass shares same shadow-map texture.

I have not found without modifying 'CompositorShadowNode::setShadowMapsToPass' function to solve this problem.

Image
<Shadow1+Shadow2(Rendered by 1.9 w/ my some fixes)>



I guess that 'CompositorShadowNode::setShadowMapsToPass' function doesn't consider light-iteration.
I have check 2.1 source, but didn't find the changes associated with this problem.
Is there a good way to solve this problem?

Or, is there a reason why multi-pass lighting can't be recommended?
I picked up multi-pass lighting, because shader invalidation using RTSS is very slow. (1-pass-multi-light lighting needs shader invalidation when light-list affecting the object is changed.)

In 2.1, is shader invalidation using HLMS fast enough to not care?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5299
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1279
Contact:

Re: [2.0] Multi-pass Lighting with ShadowNode is not working

Post by dark_sylinc »

Hi!

Indeed multi-pass lighting has been a PITA feature to maintain. It would require a ton of debugging and I don't know if it could be fully fixed. And most importantly this task would have to sit at the bottom of my todo list :roll:
everlight wrote:In 2.1, is shader invalidation using HLMS fast enough to not care?
Yes, and no. Yes, you can solve the problem quite easily in 2.1, no you can't "just forget entirely".
In Ogre 2.1 the first time it sees the object is affected by one light it will compile one shader (which results in a hiccup, D3D11 in particular can take seconds). When it sees the object is affected by two lights for the first time, it will compile another shader (hence another hiccup).
However, when it sees the object is affected by one light again; the shader was cached and no hiccups happen again.

To solve this problem there are two solutions:
  • Pre-render a couple of frames before actually showing content, to give the Hlms a chance to "see" all objects and compile its shaders. Sort of a warm up. Meanwhile display a "Loading..." screen to the user covering everything. Dirty, but gets the job done.
  • Saving the microcode cache to disk. With the cache on disk, the next time you run your application the cache is loaded from disk and no hiccups will occur. The cache from D3D11 can be moved to different systems, which means save the cache once in your PC, then distribute it everywhere. The cache from GL is only specific to that machine, which means in this case you would have run your app once in the user's machine, then save the cache (i.e. you could do this during installation process; or you could use the "Loading..." screen trick and the first time being run will take longer than the next times).
Both solutions are simple to implement (particularly the microcode cache), however I can't say "just forget about the problem" because that wouldn't be true.

Note that these tricks might work with the RTSS too. I'm not familiar with how the RTSS works. But if the RTSS doesn't delete old compilation results, the microcode cache ought to work. Make sure you've called setSaveMicrocodesToCache( true ) to enable the cache.
Post Reply