OGRECave / ogre-next

aka ogre v2 - scene-oriented, flexible 3D C++ engine
https://ogrecave.github.io/ogre-next/api/latest
Other
1.03k stars 227 forks source link

Shadow artifacts with point light and transparency #454

Closed jwwalker closed 2 months ago

jwwalker commented 2 months ago

System Information

Detailed description

When using a point light and an object that uses transparent mode, you see shadows where you shouldn't.

To reproduce, start with the ShadowMapFromCode sample, but add the line

#include <OgreHlmsPbsDatablock.h>

and replace this method:

    void ShadowMapFromCodeGameState::createScene01()
    {
        Ogre::SceneManager *sceneManager = mGraphicsSystem->getSceneManager();

        Ogre::v1::MeshPtr planeMeshV1 = Ogre::v1::MeshManager::getSingleton().createPlane(
            "Plane v1", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
            Ogre::Plane( Ogre::Vector3::UNIT_Y, 1.0f ), 50.0f, 50.0f, 1, 1, true, 1, 4.0f, 4.0f,
            Ogre::Vector3::UNIT_Z, Ogre::v1::HardwareBuffer::HBU_STATIC,
            Ogre::v1::HardwareBuffer::HBU_STATIC );

        Ogre::MeshPtr planeMesh = Ogre::MeshManager::getSingleton().createByImportingV1(
            "Plane", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, planeMeshV1.get(), true,
            true, true );

        {
            Ogre::Item *item = sceneManager->createItem( planeMesh, Ogre::SCENE_DYNAMIC );
            Ogre::SceneNode *sceneNode = sceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
                                             ->createChildSceneNode( Ogre::SCENE_DYNAMIC );
            sceneNode->setPosition( 0, -1, 0 );
            sceneNode->attachObject( item );
        }

        float armsLength = 2.5f;

        for( int i = 0; i < 4; ++i )
        {
            for( int j = 0; j < 4; ++j )
            {
                Ogre::Item *item = sceneManager->createItem(
                    "Cube_d.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
                    Ogre::SCENE_DYNAMIC );
                Ogre::SubItem* subItem = item->getSubItem( 0 );
                Ogre::HlmsDatablock* block = subItem->getDatablock();
                Ogre::HlmsPbsDatablock* pbsBlock = dynamic_cast<Ogre::HlmsPbsDatablock*>( block );
                if (pbsBlock != nullptr)
                {
                    pbsBlock->setTransparency( 1.0f );
                }

                const size_t idx = static_cast<size_t>( i * 4 + j );

                mSceneNode[idx] = sceneManager->getRootSceneNode( Ogre::SCENE_DYNAMIC )
                                      ->createChildSceneNode( Ogre::SCENE_DYNAMIC );

                mSceneNode[idx]->setPosition( ( float( i ) - 1.5f ) * armsLength, 2.0f,
                                              ( float( j ) - 1.5f ) * armsLength );
                mSceneNode[idx]->setScale( 0.65f, 0.65f, 0.65f );

                mSceneNode[idx]->roll( Ogre::Radian( (Ogre::Real)idx ) );

                mSceneNode[idx]->attachObject( item );
            }
        }

        Ogre::SceneNode *rootNode = sceneManager->getRootSceneNode();

        Ogre::Light *light = sceneManager->createLight();
        Ogre::SceneNode *lightNode = rootNode->createChildSceneNode();
        lightNode->attachObject( light );
        light->setPowerScale( 1.0f );
        light->setType( Ogre::Light::LT_DIRECTIONAL );
        light->setDirection( Ogre::Vector3( -1, -1, -1 ).normalisedCopy() );
        light->setVisible( false );

        mLightNodes[0] = lightNode;

        light = sceneManager->createLight();
        lightNode = rootNode->createChildSceneNode();
        lightNode->attachObject( light );
        light->setDiffuseColour( 0.8f, 0.4f, 0.2f );  // Warm
        light->setSpecularColour( 0.8f, 0.4f, 0.2f );
        light->setPowerScale( Ogre::Math::PI );
        light->setType( Ogre::Light::LT_SPOTLIGHT );
        lightNode->setPosition( -10.0f, 10.0f, 10.0f );
        light->setDirection( Ogre::Vector3( 1, -1, -1 ).normalisedCopy() );
        light->setAttenuationBasedOnRadius( 10.0f, 0.01f );
        light->setVisible( false );

        mLightNodes[1] = lightNode;

        light = sceneManager->createLight();
        lightNode = rootNode->createChildSceneNode();
        lightNode->attachObject( light );
        light->setDiffuseColour( 0.2f, 0.4f, 0.8f );  // Cold
        light->setSpecularColour( 0.2f, 0.4f, 0.8f );
        light->setPowerScale( Ogre::Math::PI );
        light->setType( Ogre::Light::LT_SPOTLIGHT );
        lightNode->setPosition( 10.0f, 10.0f, -10.0f );
        light->setDirection( Ogre::Vector3( -1, -1, 1 ).normalisedCopy() );
        light->setAttenuationBasedOnRadius( 10.0f, 0.01f );
        light->setVisible( false );

        mLightNodes[2] = lightNode;

#ifdef USE_STATIC_BRANCHING_FOR_SHADOWMAP_LIGHTS
        light = sceneManager->createLight();
        lightNode = rootNode->createChildSceneNode();
        lightNode->attachObject( light );
        light->setDiffuseColour( 0.8f, 0.0f, 0.0f );  // Red
        light->setSpecularColour( 0.8f, 0.0f, 0.0f );
        light->setPowerScale( Ogre::Math::PI );
        light->setType( Ogre::Light::LT_POINT );
        lightNode->setPosition( -10.0f, -10.0f, 10.0f );
        // light->setDirection( Ogre::Vector3( 1, -1, -1 ).normalisedCopy() );
        light->setAttenuationBasedOnRadius( 10.0f, 0.01f );
        light->setVisible( false );

        mLightNodes[3] = lightNode;

        light = sceneManager->createLight();
        lightNode = rootNode->createChildSceneNode();
        lightNode->attachObject( light );
        light->setDiffuseColour( 1.0f, 0.8f, 0.0f );  // Green
        light->setSpecularColour( 0.0f, 0.8f, 0.0f );
        light->setPowerScale( Ogre::Math::PI );
        light->setType( Ogre::Light::LT_POINT );
        lightNode->setPosition( -10.0f, 0.0f, 10.0f );
        // light->setDirection( Ogre::Vector3( -1, -1, 1 ).normalisedCopy() );
        light->setAttenuationBasedOnRadius( 10.0f, 0.01f );

        mLightNodes[4] = lightNode;
#endif

        mCameraController = new CameraController( mGraphicsSystem, false );

        createShadowMapDebugOverlays();

#if !OGRE_NO_JSON
        // For ESM, setup the filter settings (radius and gaussian deviation).
        // It controls how blurry the shadows will look.
        Ogre::HlmsManager *hlmsManager = Ogre::Root::getSingleton().getHlmsManager();
        Ogre::HlmsCompute *hlmsCompute = hlmsManager->getComputeHlms();

        Ogre::uint8 kernelRadius = 8;
        float gaussianDeviationFactor = 0.5f;
        Ogre::uint16 K = 80;
        Ogre::HlmsComputeJob *job = 0;

        // Setup compute shader filter (faster for large kernels; but
        // beware of mobile hardware where compute shaders are slow)
        // For reference large kernels means kernelRadius > 2 (approx)
        job = hlmsCompute->findComputeJob( "ESM/GaussianLogFilterH" );
        MiscUtils::setGaussianLogFilterParams( job, kernelRadius, gaussianDeviationFactor, K );
        job = hlmsCompute->findComputeJob( "ESM/GaussianLogFilterV" );
        MiscUtils::setGaussianLogFilterParams( job, kernelRadius, gaussianDeviationFactor, K );

        // Setup pixel shader filter (faster for small kernels, also to use as a fallback
        // on GPUs that don't support compute shaders, or where compute shaders are slow).
        MiscUtils::setGaussianLogFilterParams( "ESM/GaussianLogFilterH", kernelRadius,
                                               gaussianDeviationFactor, K );
        MiscUtils::setGaussianLogFilterParams( "ESM/GaussianLogFilterV", kernelRadius,
                                               gaussianDeviationFactor, K );
#endif

        TutorialGameState::createScene01();
    }

In this modified version, I have turned off all the lights except one point light, whose position I have moved, and I have called setTransparency on the cubes.

Here's a screen shot. Given that the light is to the left of the cubes, there is no reason that the front left cube should have shadows on it. And if you comment out the setTransparency line, it won't.

Screenshot 2024-07-25 at 9 27 17 AM
jwwalker commented 2 months ago

I can reproduce the bug under Windows with both the Vulkan and Direct3D render systems.

jwwalker commented 2 months ago

Further experimentation shows that if you have an opaque mesh casting a shadow on a translucent object, it looks OK. Whereas if a translucent object casts a shadow, it may cast a shadow on itself as well as on something else.

darksylinc commented 2 months ago

Fixed!

Thanks for the report!