ParallelSplitShadowMap 8.68 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

/* ParallelSplitShadowMap written by Adrian Egli
 *
 * this version has still a bug in mutli-thread application (flickering problem)
 * to avoid the flickering problem try osgShadow --pssm --SingleThreaded your_scene.ive
 *
 * The Parallel Split Shadow Map only supports directional light for simulating the shadow.
 * It's one of the most robust algorithm for huge terrain sun light's shadow simulation, if
 * you need to shadow a terrain, or another huge scene, you should use Parallel Split Shadow Map
 * or at least test it against your scene. Have fun.
 *
 */

#ifndef OSGSHADOW_ParallelSplitShadowMap
#define OSGSHADOW_ParallelSplitShadowMap 1

#include <osg/Camera>
#include <osg/Material>
#include <osg/Depth>
#include <osg/ClipPlane>

#include <osgShadow/ShadowTechnique>

namespace osgShadow {

class OSGSHADOW_EXPORT ParallelSplitShadowMap :  public ShadowTechnique
{
    public:
        ParallelSplitShadowMap(osg::Geode** debugGroup=NULL, int icountplanes=3);

        ParallelSplitShadowMap(const ParallelSplitShadowMap& es, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

        META_Object(osgShadow, ParallelSplitShadowMap);


        /** Initialize the ShadowedScene and local cached data structures.*/
        virtual void init();

        /** Run the update traversal of the ShadowedScene and update any loca chached data structures.*/
        virtual void update(osg::NodeVisitor& nv);

        /** Run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/
        virtual void cull(osgUtil::CullVisitor& cv);

        /** Clean scene graph from any shadow technique specific nodes, state and drawables.*/
        virtual void cleanSceneGraph();

        /** Switch on the debug coloring in GLSL (only the first 3 texture/splits showed for visualisation */
        inline void setDebugColorOn() { _debug_color_in_GLSL = true; }

        /** Set the polygon offset osg::Vec2f(factor,unit) */
        inline void setPolygonOffset(const osg::Vec2f& p) { _polgyonOffset = p;_user_polgyonOffset_set=true;}

        /** Get the polygon offset osg::Vec2f(factor,unit) */
        inline const osg::Vec2f& getPolygonOffset() const { return _polgyonOffset;}

        /** Set the texture resolution */
        inline void setTextureResolution(unsigned int resolution) { _resolution = resolution; }
        
        /** Get the texture resolution */
        inline unsigned int getTextureResolution() const { return _resolution; }

        /** Set the max far distance */
        inline void setMaxFarDistance(double farDist) { _setMaxFarDistance = farDist; _isSetMaxFarDistance = true; }

        /** Get the max far distance */
        inline double getMaxFarDistance() const { return _setMaxFarDistance; }

        /** Set the factor for moving the virtual camera behind the real camera*/
        inline void setMoveVCamBehindRCamFactor(double distFactor ) { _move_vcam_behind_rcam_factor = distFactor; }

        /** Get the factor for moving the virtual camera behind the real camera*/
        inline double getMoveVCamBehindRCamFactor() const { return _move_vcam_behind_rcam_factor; }

        /** Set min near distance for splits */
        inline void setMinNearDistanceForSplits(double nd){ _split_min_near_dist=nd; }
        
        /** Get min near distance for splits */
        inline double getMinNearDistanceForSplits() const { return _split_min_near_dist; }

        /** set a user defined light for shadow simulation (sun light, ... )
         *    when this light get passed to pssm, the scene's light are no longer collected
         *    and simulated. just this user passed light, it needs to be a directional light.
         */
        inline void setUserLight(osg::Light* light) { _userLight = light; }
        
        /** get the user defined light for shadow simulation */
        inline const osg::Light* getUserLight() const { return _userLight.get(); }

        /** Set the values for the ambient bias the shader will use.*/
        void setAmbientBias(const osg::Vec2& ambientBias );
        
        /** Get the values for the ambient bias the shader will use.*/
        const osg::Vec2& getAmbientBias() const { return _ambientBias; }

        /**
         * you can overwrite the fragment shader if you like to modify it yourself, own fragment shader can be used
         */
        class OSGSHADOW_EXPORT FragmentShaderGenerator : public osg::Referenced {
            public:
                /**
                 * generate the GLSL fragement shader
                 */
                virtual std::string generateGLSL_FragmentShader_BaseTex(bool debug, unsigned int splitCount,double textureRes, bool filtered, unsigned int nbrSplits,unsigned int textureOffset);
        };

        /** set fragment shader generator */
        inline void setFragmentShaderGenerator(FragmentShaderGenerator* fsw) { _FragmentShaderGenerator = fsw;}

        /** enable / disable shadow filtering */
        inline void enableShadowGLSLFiltering(bool filtering = true) {  _GLSL_shadow_filtered = filtering; }

        enum SplitCalcMode {
            SPLIT_LINEAR,
            SPLIT_EXP
        };

        /** set split calculation mode */
        inline void setSplitCalculationMode(SplitCalcMode scm=SPLIT_EXP) { _SplitCalcMode = scm; }
        
        /** get split calculation mode */
        inline SplitCalcMode getSplitCalculationMode() const { return _SplitCalcMode; }


    protected :

        virtual ~ParallelSplitShadowMap() {}


        struct PSSMShadowSplitTexture {
            // RTT
            osg::ref_ptr<osg::Camera>       _camera;
            osg::ref_ptr<osg::TexGen>       _texgen;
            osg::ref_ptr<osg::Texture2D>    _texture;
            osg::ref_ptr<osg::StateSet>     _stateset;
            unsigned int                    _textureUnit;


            double                            _split_far;

            osg::ref_ptr<osg::Camera>       _debug_camera;
            osg::ref_ptr<osg::Texture2D>    _debug_texture;
            osg::ref_ptr<osg::StateSet>     _debug_stateset;
            unsigned int                    _debug_textureUnit;

            // Light (SUN)
            osg::Vec3d                        _lightCameraSource;
            osg::Vec3d                        _lightCameraTarget;
            osg::Vec3d                        _frustumSplitCenter;
            osg::Vec3d                        _lightDirection;
            double                            _lightNear;
            double                            _lightFar;

            osg::Matrix                       _cameraView;
            osg::Matrix                       _cameraProj;

            unsigned int                      _splitID;
            unsigned int                      _resolution;

            osg::Uniform*                    _farDistanceSplit;
        };

        typedef std::map<unsigned int,PSSMShadowSplitTexture> PSSMShadowSplitTextureMap;
        PSSMShadowSplitTextureMap _PSSMShadowSplitTextureMap;


    private:
        void calculateFrustumCorners(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners);
        void calculateLightInitialPosition(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners);
        void calculateLightNearFarFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners);
        void calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners);

        osg::Geode** _displayTexturesGroupingNode;

        unsigned int _textureUnitOffset;

        unsigned int _number_of_splits;

        bool _debug_color_in_GLSL;

        osg::Vec2 _polgyonOffset;
        bool _user_polgyonOffset_set;

        unsigned int _resolution;

        double _setMaxFarDistance;
        bool _isSetMaxFarDistance;

        double _split_min_near_dist;

        double _move_vcam_behind_rcam_factor;

        osg::ref_ptr<osg::Light> _userLight;
        osg::ref_ptr<FragmentShaderGenerator> _FragmentShaderGenerator;

        bool            _GLSL_shadow_filtered;
        SplitCalcMode   _SplitCalcMode;

        osg::Uniform*   _ambientBiasUniform;
        osg::Vec2       _ambientBias;

};
}
#endif