diff --git a/include/osgShadow/ParallelSplitShadowMap b/include/osgShadow/ParallelSplitShadowMap index 38b4804e1..93d59ebeb 100644 --- a/include/osgShadow/ParallelSplitShadowMap +++ b/include/osgShadow/ParallelSplitShadowMap @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -77,24 +78,45 @@ class OSGSHADOW_EXPORT ParallelSplitShadowMap : public ShadowTechnique /** Set min near distance for splits */ inline void setMinNearDistanceForSplits(double nd){ _split_min_near_dist=nd; } - /** use linear split (default: linear) */ - inline void useLinearSplit(bool flag) { _linearSplit = flag;} - /** 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; } - - + /** Set the values for the ambient bias the shader will use.*/ void setAmbientBias(const osg::Vec2& ambientBias ); - + + /** + * you can overwrite the fragment shader if you like to modify it yourself, own fragment shader can be used + */ + class 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; } + + protected : virtual ~ParallelSplitShadowMap() {} - std::string generateGLSL_FragmentShader_BaseTex(bool debug, unsigned int splitCount); struct PSSMShadowSplitTexture { // RTT @@ -105,7 +127,7 @@ class OSGSHADOW_EXPORT ParallelSplitShadowMap : public ShadowTechnique unsigned int _textureUnit; - osg::ref_ptr _depth; + double _split_far; osg::ref_ptr _debug_camera; osg::ref_ptr _debug_texture; @@ -127,8 +149,8 @@ class OSGSHADOW_EXPORT ParallelSplitShadowMap : public ShadowTechnique unsigned int _resolution; osg::Uniform* _farDistanceSplit; - }; + typedef std::map PSSMShadowSplitTextureMap; PSSMShadowSplitTextureMap _PSSMShadowSplitTextureMap; @@ -158,11 +180,12 @@ class OSGSHADOW_EXPORT ParallelSplitShadowMap : public ShadowTechnique double _split_min_near_dist; double _move_vcam_behind_rcam_factor; + + osg::ref_ptr _userLight; + osg::ref_ptr _FragmentShaderGenerator; - bool _linearSplit; - - osg::Light* _userLight; - + bool _GLSL_shadow_filtered; + SplitCalcMode _SplitCalcMode; osg::Uniform* _ambientBiasUniform; osg::Vec2d _ambientBias; diff --git a/src/osgShadow/ParallelSplitShadowMap.cpp b/src/osgShadow/ParallelSplitShadowMap.cpp index 4f07306c8..6219999f2 100644 --- a/src/osgShadow/ParallelSplitShadowMap.cpp +++ b/src/osgShadow/ParallelSplitShadowMap.cpp @@ -53,83 +53,101 @@ using namespace osgShadow; #define TEXTURE_RESOLUTION 1024 -#define LINEAR_SPLIT false - -//#define SMOOTH_SHADOW //experimental - -#define ZNEAR_MIN_FROM_LIGHT_SOURCE 2.0 + + +#define ZNEAR_MIN_FROM_LIGHT_SOURCE 5.0 #define MOVE_VIRTUAL_CAMERA_BEHIND_REAL_CAMERA_FACTOR 0.0 //#define SHOW_SHADOW_TEXTURE_DEBUG // DEPTH instead of color for debug information texture display in a rectangle - - //#define SHADOW_TEXTURE_DEBUG // COLOR instead of DEPTH #ifndef SHADOW_TEXTURE_DEBUG #define SHADOW_TEXTURE_GLSL #endif - -std::string ParallelSplitShadowMap::generateGLSL_FragmentShader_BaseTex(bool debug, unsigned int splitCount) { +////////////////////////////////////////////////////////////////////////// +// FragmentShaderGenerator +std::string ParallelSplitShadowMap::FragmentShaderGenerator::generateGLSL_FragmentShader_BaseTex( + bool debug, + unsigned int splitCount, + double textureRes, + bool filtered, + unsigned int nbrSplits, + unsigned int textureOffset +) { std::stringstream sstr; /// base texture sstr << "uniform sampler2D baseTexture; " << std::endl; - #ifdef SMOOTH_SHADOW - sstr << "uniform sampler2D randomTexture; " << std::endl; - #endif sstr << "uniform float enableBaseTexture; " << std::endl; sstr << "uniform vec2 ambientBias;" << std::endl; - for (unsigned int i=0;i<_number_of_splits;i++) { + for (unsigned int i=0;i SHADOW filter; " << std::endl; + /// build shadow factor value v sstr << " float v = clamp("; - for (unsigned int i=0;i<_number_of_splits;i++) { - sstr << "term" << i; - if ( i+1 < _number_of_splits ){ + for (unsigned int i=0;i inline Type Clamp(Type A, Type Min, Type Max) { if(A inline Type Clamp(Type A, Type Min, Type Max) { #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) - +////////////////////////////////////////////////////////////////////////// ParallelSplitShadowMap::ParallelSplitShadowMap(osg::Geode** gr, int icountplanes) : -_textureUnitOffset(1), -_debug_color_in_GLSL(false), -_user_polgyonOffset_set(false), -_resolution(TEXTURE_RESOLUTION), -_isSetMaxFarDistance(false), -_split_min_near_dist(ZNEAR_MIN_FROM_LIGHT_SOURCE), -_linearSplit(LINEAR_SPLIT), -_move_vcam_behind_rcam_factor(MOVE_VIRTUAL_CAMERA_BEHIND_REAL_CAMERA_FACTOR), -_userLight(NULL), -_ambientBias(0.1,0.3), -_ambientBiasUniform(NULL) + _textureUnitOffset(1), + _debug_color_in_GLSL(false), + _user_polgyonOffset_set(false), + _resolution(TEXTURE_RESOLUTION), + _isSetMaxFarDistance(false), + _split_min_near_dist(ZNEAR_MIN_FROM_LIGHT_SOURCE), + _move_vcam_behind_rcam_factor(MOVE_VIRTUAL_CAMERA_BEHIND_REAL_CAMERA_FACTOR), + _userLight(NULL), + _ambientBias(0.1,0.3), + _ambientBiasUniform(NULL), + _GLSL_shadow_filtered(true) { _displayTexturesGroupingNode = gr; _number_of_splits = icountplanes; - _polgyonOffset.set(0.01f,0.01f); -} + _polgyonOffset.set(1.0f,1.0f); + setFragmentShaderGenerator(new FragmentShaderGenerator()); + setSplitCalculationMode(SPLIT_EXP); + } ParallelSplitShadowMap::ParallelSplitShadowMap(const ParallelSplitShadowMap& copy, const osg::CopyOp& copyop): ShadowTechnique(copy,copyop), @@ -205,13 +244,15 @@ _user_polgyonOffset_set(copy._user_polgyonOffset_set), _resolution(copy._resolution), _isSetMaxFarDistance(copy._isSetMaxFarDistance), _split_min_near_dist(copy._split_min_near_dist), -_linearSplit(copy._linearSplit), +_SplitCalcMode(copy._SplitCalcMode), _number_of_splits(copy._number_of_splits), _polgyonOffset(copy._polgyonOffset), _setMaxFarDistance(copy._setMaxFarDistance), _move_vcam_behind_rcam_factor(copy._move_vcam_behind_rcam_factor), _userLight(copy._userLight), -_ambientBias(copy._ambientBias) +_ambientBias(copy._ambientBias), +_GLSL_shadow_filtered(copy._GLSL_shadow_filtered), +_FragmentShaderGenerator(copy._FragmentShaderGenerator) { } @@ -303,24 +344,25 @@ void ParallelSplitShadowMap::init(){ osg::StateSet* stateset = pssmShadowSplitTexture._camera->getOrCreateStateSet(); - pssmShadowSplitTexture._depth = new osg::Depth; - stateset->setAttribute(pssmShadowSplitTexture._depth.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - + ////////////////////////////////////////////////////////////////////////// float factor = _polgyonOffset.x(); float units = _polgyonOffset.y(); - osg::ref_ptr polygon_offset = new osg::PolygonOffset; - polygon_offset->setFactor(factor); + osg::ref_ptr polygon_offset = new osg::PolygonOffset; + polygon_offset->setFactor(factor); polygon_offset->setUnits(units); stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); ////////////////////////////////////////////////////////////////////////// - osg::ref_ptr cull_face = new osg::CullFace; - cull_face->setMode(osg::CullFace::FRONT); - stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + if ( ! _GLSL_shadow_filtered ) { + // if not glsl filtering enabled then we should force front face culling to reduce the number of shadow artefacts. + osg::ref_ptr cull_face = new osg::CullFace; + cull_face->setMode(osg::CullFace::FRONT); + stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } ////////////////////////////////////////////////////////////////////////// osg::ShadeModel* shademodel = dynamic_cast(stateset->getAttribute(osg::StateAttribute::SHADEMODEL)); @@ -354,7 +396,15 @@ void ParallelSplitShadowMap::init(){ ////////////////////////////////////////////////////////////////////////// // GLSL PROGRAMS - osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, generateGLSL_FragmentShader_BaseTex(_displayTexturesGroupingNode!=NULL,iCameras).c_str()); + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, + _FragmentShaderGenerator->generateGLSL_FragmentShader_BaseTex( + _debug_color_in_GLSL, + iCameras, + pssmShadowSplitTexture._resolution, + _GLSL_shadow_filtered, + _number_of_splits, + _textureUnitOffset + ).c_str()); program->addShader(fragment_shader); @@ -422,43 +472,6 @@ void ParallelSplitShadowMap::init(){ pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_2D,osg::StateAttribute::ON); pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_3D,osg::StateAttribute::OFF); } - - - - #ifdef SMOOTH_SHADOW - { - // texture for randomTexture (for smoothing shadow edges) - osg::Image* image = new osg::Image; - // allocate the image data, noPixels x noPixels x 1 with 4 rgba floats - equivalent to a Vec4! - int noPixels = 128; - image->allocateImage(noPixels,noPixels,1,GL_RGBA,GL_FLOAT); - image->setInternalTextureFormat(GL_RGBA); - // fill in the image data. - osg::Vec4* dataPtr = (osg::Vec4*)image->data(); - for (int s=0;ssetWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER); - texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER); - texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); - texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); - texture->setImage(image); - // add fake texture - pssmShadowSplitTexture._stateset->setTextureAttribute(_textureUnitOffset+_number_of_splits,texture,osg::StateAttribute::ON); - pssmShadowSplitTexture._stateset->setTextureMode(_textureUnitOffset+_number_of_splits,GL_TEXTURE_1D,osg::StateAttribute::OFF); - pssmShadowSplitTexture._stateset->setTextureMode(_textureUnitOffset+_number_of_splits,GL_TEXTURE_2D,osg::StateAttribute::ON); - pssmShadowSplitTexture._stateset->setTextureMode(_textureUnitOffset+_number_of_splits,GL_TEXTURE_3D,osg::StateAttribute::OFF); - } - #endif #endif @@ -591,7 +604,7 @@ void ParallelSplitShadowMap::cull(osgUtil::CullVisitor& cv){ // take the user light as light source lightpos = _userLight->getPosition(); lightDirection = _userLight->getDirection(); - selectLight = _userLight; + selectLight = _userLight.get(); } if (selectLight) @@ -699,14 +712,14 @@ void ParallelSplitShadowMap::cleanSceneGraph(){ // // //unit box representing frustum in clip space -const osg::Vec3f const_pointFarBR(1.0f, -1.0f, 1.0f); -const osg::Vec3f const_pointNearBR(1.0f, -1.0f, -1.0f); -const osg::Vec3f const_pointNearTR(1.0f, 1.0f, -1.0f); const osg::Vec3f const_pointFarTR(1.0f, 1.0f, 1.0f); +const osg::Vec3f const_pointFarBR(1.0f, -1.0f, 1.0f); const osg::Vec3f const_pointFarTL(-1.0f, 1.0f, 1.0f); const osg::Vec3f const_pointFarBL(-1.0f, -1.0f, 1.0f); -const osg::Vec3f const_pointNearBL(-1.0f, -1.0f, -1.0f); +const osg::Vec3f const_pointNearTR(1.0f, 1.0f, -1.0f); +const osg::Vec3f const_pointNearBR(1.0f, -1.0f, -1.0f); const osg::Vec3f const_pointNearTL(-1.0f, 1.0f, -1.0f); +const osg::Vec3f const_pointNearBL(-1.0f, -1.0f, -1.0f); ////////////////////////////////////////////////////////////////////////// @@ -731,7 +744,7 @@ void ParallelSplitShadowMap::calculateFrustumCorners( osg::Vec3d camEye,camCenter,camUp; pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp); osg::Vec3d viewDir = camCenter - camEye; - viewDir.normalize(); + //viewDir.normalize(); //we can assume that viewDir is still normalized in the viewMatrix camEye = camEye - viewDir * _move_vcam_behind_rcam_factor; camFar += _move_vcam_behind_rcam_factor * viewDir.length(); viewMat.makeLookAt(camEye,camCenter,camUp); @@ -743,7 +756,7 @@ void ParallelSplitShadowMap::calculateFrustumCorners( double maxFar = camFar; double minNear = camNear; double camNearFar_Dist = maxFar - camNear; - if ( _linearSplit ) { + if ( _SplitCalcMode == SPLIT_LINEAR ) { camFar = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID+1))/((double)(_number_of_splits)); camNear = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID))/((double)(_number_of_splits)); } else { @@ -768,26 +781,20 @@ void ParallelSplitShadowMap::calculateFrustumCorners( delete[] pSplitDistances; } - - //pssmShadowSplitTexture._depth->setRange((camNear - minNear) / camNearFar_Dist,(camFar - minNear)/ camNearFar_Dist); - pssmShadowSplitTexture._depth->setRange(0.0,1.0); - pssmShadowSplitTexture._depth->setFunction(osg::Depth::LEQUAL); - - #ifdef SHADOW_TEXTURE_GLSL - float fVal = (float)((camFar-minNear)/camNearFar_Dist); - //std::cout << pssmShadowSplitTexture._farDistanceSplit->getName() << " " << fVal << std::endl; - pssmShadowSplitTexture._farDistanceSplit->set(fVal); - #endif + + + pssmShadowSplitTexture._split_far = camFar; + + ////////////////////////////////////////////////////////////////////////// /// TRANSFORM frustum corners (Optimized for Orthogonal) - osg::Matrixf invProjViewMat; osg::Matrixf projMat; projMat.makePerspective(fovy,aspectRatio,camNear,camFar); - osg::Matrixf projViewMat(viewMat*projMat); + osg::Matrixf invProjViewMat; invProjViewMat.invert(projViewMat); //transform frustum vertices to world space @@ -798,8 +805,7 @@ void ParallelSplitShadowMap::calculateFrustumCorners( frustumCorners[4] = const_pointFarTL * invProjViewMat; frustumCorners[5] = const_pointFarBL * invProjViewMat; frustumCorners[6] = const_pointNearBL* invProjViewMat; - frustumCorners[7] = const_pointNearTL* invProjViewMat; - + frustumCorners[7] = const_pointNearTL* invProjViewMat; //std::cout << "camFar : "< dist_z_from_light ) zNear = dist_z_from_light; } // update near - far plane - pssmShadowSplitTexture._lightNear = zNear - _split_min_near_dist + 0.01; + pssmShadowSplitTexture._lightNear = max(zNear - _split_min_near_dist - 0.01,0.01); pssmShadowSplitTexture._lightFar = zFar; } @@ -870,42 +874,52 @@ void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowS // calculate the camera's coordinate system osg::Vec3d camEye,camCenter,camUp; pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp); + osg::Vec3d viewDir(camCenter-camEye); + osg::Vec3d camRight(camUp^viewDir); - osg::Vec3d viewDir(camCenter-camEye); + // we force to have normalized vectors (camera's view) + camUp.normalize(); viewDir.normalize(); - - osg::Vec3d camLeft(camUp ^ viewDir); - camLeft.normalize(); - - osg::Vec3d top(pssmShadowSplitTexture._lightDirection ^ camLeft); - if(top.length2() < 0.5) top = pssmShadowSplitTexture._lightDirection ^ camUp; - - osg::Vec3d left(top ^ pssmShadowSplitTexture._lightDirection); - - - // calculate the camera's frustum left,right,bottom,top parameters - double maxLeft(-DBL_MAX),maxTop(-DBL_MAX); - double minLeft(DBL_MAX),minTop(DBL_MAX); + camRight.normalize(); + + // use quaternion -> numerical more robust + osg::Quat qRot; + qRot.makeRotate(viewDir,pssmShadowSplitTexture._lightDirection); + osg::Vec3d top = qRot * camUp; + osg::Vec3d right = qRot * camRight; + + // calculate the camera's frustum right,right,bottom,top parameters + double maxRight(-DBL_MAX),maxTop(-DBL_MAX); + double minRight(DBL_MAX),minTop(DBL_MAX); for(int i(0); i < 8; i++) - { + { + osg::Vec3d diffCorner(pssmShadowSplitTexture._lightCameraSource - frustumCorners[i]); - double lLeft(diffCorner*left); + double lright(diffCorner*right); double lTop(diffCorner*top); - if ( lLeft > maxLeft ) maxLeft = lLeft; + if ( lright > maxRight ) maxRight = lright; if ( lTop > maxTop ) maxTop = lTop; - if ( lLeft < minLeft ) minLeft = lLeft; + if ( lright < minRight ) minRight = lright; if ( lTop < minTop ) minTop = lTop; - } - + } // make the camera view matrix pssmShadowSplitTexture._camera->setViewMatrixAsLookAt(pssmShadowSplitTexture._lightCameraSource,pssmShadowSplitTexture._lightCameraTarget,top); // use ortho projection for light (directional light only supported) - pssmShadowSplitTexture._camera->setProjectionMatrixAsOrtho(minLeft,maxLeft,minTop,maxTop,pssmShadowSplitTexture._lightNear,pssmShadowSplitTexture._lightFar); + pssmShadowSplitTexture._camera->setProjectionMatrixAsOrtho(minRight,maxRight,minTop,maxTop,pssmShadowSplitTexture._lightNear,pssmShadowSplitTexture._lightFar); + + + #ifdef SHADOW_TEXTURE_GLSL + // get user cameras + osg::Vec3d vProjCamFraValue = (camEye + viewDir * pssmShadowSplitTexture._split_far) * (pssmShadowSplitTexture._cameraView * pssmShadowSplitTexture._cameraProj); + pssmShadowSplitTexture._farDistanceSplit->set((float)vProjCamFraValue.z()); + #endif + + }