diff --git a/examples/osgshadow/osgshadow.cpp b/examples/osgshadow/osgshadow.cpp index 1f4b6b28c..2c5050352 100644 --- a/examples/osgshadow/osgshadow.cpp +++ b/examples/osgshadow/osgshadow.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -494,6 +495,9 @@ int main(int argc, char** argv) arguments.getApplicationUsage()->addCommandLineOption("--ssm", "Select SoftShadowMap implementation."); arguments.getApplicationUsage()->addCommandLineOption("--sm", "Select ShadowMap implementation."); // arguments.getApplicationUsage()->addCommandLineOption("--pssm", "Select ParallelSplitShadowMap implementation."); + arguments.getApplicationUsage()->addCommandLineOption("--pssm", "Select ParallelSplitShadowMap implementation.");//ADEGLI + arguments.getApplicationUsage()->addCommandLineOption("--mapcount", "ParallelSplitShadowMap texture count.");//ADEGLI + arguments.getApplicationUsage()->addCommandLineOption("-1", "Use test model one."); arguments.getApplicationUsage()->addCommandLineOption("-2", "Use test model two."); arguments.getApplicationUsage()->addCommandLineOption("-3", "Use test model three."); @@ -613,13 +617,13 @@ int main(int argc, char** argv) osg::ref_ptr st = new osgShadow::ShadowTexture; shadowedScene->setShadowTechnique(st.get()); } -#if 0 else if (arguments.read("--pssm")) { - osg::ref_ptr pssm = new osgShadow::ParallelSplitShadowMap; + int mapcount = 3; + while (arguments.read("--mapcount", mapcount)); + osg::ref_ptr pssm = new osgShadow::ParallelSplitShadowMap(NULL,mapcount); shadowedScene->setShadowTechnique(pssm.get()); } -#endif else if (arguments.read("--ssm")) { osg::ref_ptr sm = new osgShadow::SoftShadowMap; @@ -687,6 +691,10 @@ int main(int argc, char** argv) lightpos.set(sinf(t),cosf(t),1.0f,0.0f); } ls->getLight()->setPosition(lightpos); + + osg::Vec3f lightDir(-lightpos.x(),-lightpos.y(),-lightpos.z()); + lightDir.normalize(); + ls->getLight()->setDirection(lightDir); } viewer.frame(); diff --git a/include/osgShadow/ParallelSplitShadowMap b/include/osgShadow/ParallelSplitShadowMap new file mode 100644 index 000000000..91657d36a --- /dev/null +++ b/include/osgShadow/ParallelSplitShadowMap @@ -0,0 +1,107 @@ +/* OpenSceneGraph example, osgshadow. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +/* ParallelSplitShadowMap written by Adrian Egli */ + +#ifndef OSGSHADOW_ParallelSplitShadowMap +#define OSGSHADOW_ParallelSplitShadowMap 1 + +#include +#include + +#include + +namespace osgShadow { +class 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(); + + protected : + + virtual ~ParallelSplitShadowMap() {} + + + struct PSSMShadowSplitTexture { + // RTT + osg::ref_ptr _camera; + osg::ref_ptr _texgen; + osg::ref_ptr _texture; + osg::ref_ptr _stateset; + unsigned int _textureUnit; + osg::Vec2d _ambientBias; + + + osg::ref_ptr _debug_camera; + osg::ref_ptr _debug_texture; + osg::ref_ptr _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 PSSMShadowSplitTextureMap; + PSSMShadowSplitTextureMap _PSSMShadowSplitTextureMap; + + + private: + void calculateFrustumCorners(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners); + void calculateLightInitalPosition(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; + +}; +} +#endif diff --git a/src/osgShadow/CMakeLists.txt b/src/osgShadow/CMakeLists.txt index 8b847942d..8d5d18096 100644 --- a/src/osgShadow/CMakeLists.txt +++ b/src/osgShadow/CMakeLists.txt @@ -16,6 +16,7 @@ SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/ShadowVolume ${HEADER_PATH}/ShadowedScene ${HEADER_PATH}/SoftShadowMap + ${HEADER_PATH}/ParallelSplitShadowMap ${HEADER_PATH}/Version ) @@ -30,6 +31,7 @@ ADD_LIBRARY(${LIB_NAME} ShadowVolume.cpp ShadowedScene.cpp SoftShadowMap.cpp + ParallelSplitShadowMap.cpp Version.cpp ) diff --git a/src/osgShadow/ParallelSplitShadowMap.cpp b/src/osgShadow/ParallelSplitShadowMap.cpp new file mode 100644 index 000000000..44adc3ced --- /dev/null +++ b/src/osgShadow/ParallelSplitShadowMap.cpp @@ -0,0 +1,714 @@ +/* OpenSceneGraph example, osgshadow. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +/* ParallelSplitShadowMap written by Adrian Egli */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgShadow; + +// split scheme +unsigned int NUM_SPLITS = 1; +#define TEXTURE_RESOLUTION 1024 +//#define ADAPTIVE_TEXTURE_RESOLUTION + + +#define LINEAR_SPLIT false + + +#define ZNEAR_MIN_FROM_LIGHT_SOURCE 1.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 + + //#define SHADOW_TEXTURE_GLSL_DEBUG + +#endif + + +std::string generateGLSL_FragmentShader_BaseTex(unsigned int splitCount) { + std::stringstream sstr; + + /// base texture + sstr << "uniform sampler2D baseTexture; " << std::endl; + sstr << "uniform float enableBaseTexture; " << std::endl; + + for (unsigned int i=0;i inline Type Clamp(Type A, Type Min, Type Max) { + if(AMax) return Max; + return A; +} + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + + +ParallelSplitShadowMap::ParallelSplitShadowMap(osg::Geode** gr, int icountplanes){ + _displayTexturesGroupingNode = gr; + NUM_SPLITS = icountplanes; + _textureUnitOffset = 1; +} + +ParallelSplitShadowMap::ParallelSplitShadowMap(const ParallelSplitShadowMap& copy, const osg::CopyOp& copyop): + ShadowTechnique(copy,copyop), + _textureUnitOffset(copy._textureUnitOffset) +{ +} + + +void ParallelSplitShadowMap::init(){ + if (!_shadowedScene) return; + + osg::StateSet* sharedStateSet = new osg::StateSet; + + unsigned int iCamerasMax=NUM_SPLITS; + for (unsigned int iCameras=0;iCamerasaddUniform(shadowTextureSampler); + + std::stringstream strAB; strAB << "ambientBias" << (pssmShadowSplitTexture._textureUnit-1); + osg::Uniform* ambientBias = new osg::Uniform(strAB.str().c_str(),pssmShadowSplitTexture._ambientBias); + pssmShadowSplitTexture._stateset->addUniform(ambientBias); + + + std::stringstream strzShadow; strzShadow << "zShadow" << (pssmShadowSplitTexture._textureUnit-1); + pssmShadowSplitTexture._farDistanceSplit = new osg::Uniform(strzShadow.str().c_str(),(float)1.0); + pssmShadowSplitTexture._stateset->addUniform(pssmShadowSplitTexture._farDistanceSplit); + + osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0); + pssmShadowSplitTexture._stateset->addUniform(baseTextureSampler); + + if ( _textureUnitOffset > 0 ) { + osg::Uniform* enableBaseTexture = new osg::Uniform("enableBaseTexture",1.0f); + pssmShadowSplitTexture._stateset->addUniform(enableBaseTexture); + } else { + osg::Uniform* enableBaseTexture = new osg::Uniform("enableBaseTexture",0.0f); + pssmShadowSplitTexture._stateset->addUniform(enableBaseTexture); + } + + + // fake texture for baseTexture + osg::Image* image = new osg::Image; + // allocate the image data, noPixels x 1 x 1 with 4 rgba floats - equivilant to a Vec4! + int noPixels = 1; + image->allocateImage(noPixels,1,1,GL_RGBA,GL_FLOAT); + image->setInternalTextureFormat(GL_RGBA); + // fill in the image data. + osg::Vec4* dataPtr = (osg::Vec4*)image->data(); + osg::Vec4 color(0,0,0,0); + *dataPtr = color; + // make fake texture + osg::Texture2D* texture = new osg::Texture2D; + texture->setWrap(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(0,texture,osg::StateAttribute::ON); + pssmShadowSplitTexture._stateset->setTextureMode(0,GL_TEXTURE_1D,osg::StateAttribute::OFF); + pssmShadowSplitTexture._stateset->setTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::ON); + pssmShadowSplitTexture._stateset->setTextureMode(0,GL_TEXTURE_3D,osg::StateAttribute::OFF); + + + #endif + + + ////////////////////////////////////////////////////////////////////////// + // DEBUG + if ( _displayTexturesGroupingNode ) { + { + pssmShadowSplitTexture._debug_textureUnit = 1; + pssmShadowSplitTexture._debug_texture = new osg::Texture2D; + pssmShadowSplitTexture._debug_texture->setTextureSize(TEXTURE_RESOLUTION, TEXTURE_RESOLUTION); + #ifdef SHOW_SHADOW_TEXTURE_DEBUG + pssmShadowSplitTexture._debug_texture->setInternalFormat(GL_DEPTH_COMPONENT); + pssmShadowSplitTexture._debug_texture->setShadowTextureMode(osg::Texture2D::LUMINANCE); + #else + pssmShadowSplitTexture._debug_texture->setInternalFormat(GL_RGBA); + #endif + pssmShadowSplitTexture._debug_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + pssmShadowSplitTexture._debug_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + // create the camera + pssmShadowSplitTexture._debug_camera = new osg::Camera; + pssmShadowSplitTexture._debug_camera->setCullCallback(new CameraCullCallback(this)); + pssmShadowSplitTexture._debug_camera->setClearMask(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); + pssmShadowSplitTexture._debug_camera->setClearColor(osg::Vec4(1.0,1.0,1.0,1.0)); + pssmShadowSplitTexture._debug_camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + + // set viewport + pssmShadowSplitTexture._debug_camera->setViewport(0,0,TEXTURE_RESOLUTION,TEXTURE_RESOLUTION); + // set the camera to render before the main camera. + pssmShadowSplitTexture._debug_camera->setRenderOrder(osg::Camera::PRE_RENDER); + // tell the camera to use OpenGL frame buffer object where supported. + pssmShadowSplitTexture._debug_camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + // attach the texture and use it as the color buffer. + #ifdef SHOW_SHADOW_TEXTURE_DEBUG + pssmShadowSplitTexture._debug_camera->attach(osg::Camera::DEPTH_BUFFER, pssmShadowSplitTexture._debug_texture.get()); + #else + pssmShadowSplitTexture._debug_camera->attach(osg::Camera::COLOR_BUFFER, pssmShadowSplitTexture._debug_texture.get()); + #endif + osg::StateSet* stateset = pssmShadowSplitTexture._debug_camera->getOrCreateStateSet(); + + pssmShadowSplitTexture._debug_stateset = new osg::StateSet; + pssmShadowSplitTexture._debug_stateset->setTextureAttributeAndModes(pssmShadowSplitTexture._debug_textureUnit,pssmShadowSplitTexture._debug_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON); + pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON); + pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON); + pssmShadowSplitTexture._debug_stateset->setTextureMode(pssmShadowSplitTexture._debug_textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON); + } + + osg::Geode* geode = _displayTexturesGroupingNode[iCameras]; + geode->getOrCreateStateSet()->setTextureAttributeAndModes(0,pssmShadowSplitTexture._debug_texture.get(),osg::StateAttribute::ON); + + } + ////////////////////////////////////////////////////////////////////////// + + _PSSMShadowSplitTextureMap.insert(PSSMShadowSplitTextureMap::value_type(iCameras,pssmShadowSplitTexture)); + + + } + + + _dirty = false; +} + +void ParallelSplitShadowMap::update(osg::NodeVisitor& nv){ + getShadowedScene()->osg::Group::traverse(nv); +} + +void ParallelSplitShadowMap::cull(osgUtil::CullVisitor& cv){ + // record the traversal mask on entry so we can reapply it later. + unsigned int traversalMask = cv.getTraversalMask(); + osgUtil::RenderStage* orig_rs = cv.getRenderStage(); + + // do traversal of shadow recieving scene which does need to be decorated by the shadow map + for (PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++) + { + PSSMShadowSplitTexture pssmShadowSplitTexture = it->second; + cv.pushStateSet(pssmShadowSplitTexture._stateset.get()); + + ////////////////////////////////////////////////////////////////////////// + // DEGUBG + if ( _displayTexturesGroupingNode ) { + cv.pushStateSet(pssmShadowSplitTexture._debug_stateset.get()); + } + ////////////////////////////////////////////////////////////////////////// + + _shadowedScene->osg::Group::traverse(cv); + + cv.popStateSet(); + + } + + // need to compute view frustum for RTT camera. + // get the bounds of the model. + osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); + cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask()); + _shadowedScene->osg::Group::traverse(cbbv); + osg::BoundingBox bb = cbbv.getBoundingBox(); + + const osg::Light* selectLight = 0; + osg::Vec4 lightpos; + osg::Vec3 lightDirection; + + osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList(); + for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin(); + itr != aml.end(); + ++itr) + { + const osg::Light* light = dynamic_cast(itr->first.get()); + if (light) + { + osg::RefMatrix* matrix = itr->second.get(); + if (matrix) lightpos = light->getPosition() * (*matrix); + else lightpos = light->getPosition(); + if (matrix) lightDirection = light->getDirection() * (*matrix); + else lightDirection = light->getDirection(); + + selectLight = light; + } + } + + osg::Matrix eyeToWorld; + eyeToWorld.invert(*cv.getModelViewMatrix()); + + lightpos = lightpos * eyeToWorld; + lightDirection = lightDirection * eyeToWorld; + + if (selectLight) + { + + // do traversal of shadow recieving scene which does need to be decorated by the shadow map + unsigned int iMaxSplit = _PSSMShadowSplitTextureMap.size(); + + for (PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++) + { + PSSMShadowSplitTexture pssmShadowSplitTexture = it->second; + + ////////////////////////////////////////////////////////////////////////// + // SETUP pssmShadowSplitTexture for rendering + // + lightDirection.normalize(); + pssmShadowSplitTexture._lightDirection = lightDirection; + pssmShadowSplitTexture._cameraView = cv.getRenderInfo().getView()->getCamera()->getViewMatrix(); + pssmShadowSplitTexture._cameraProj = cv.getRenderInfo().getView()->getCamera()->getProjectionMatrix(); + + ////////////////////////////////////////////////////////////////////////// + // CALCULATE + + + + // Calculate corner points of frustum split + // + // To avoid edge problems, scale the frustum so + // that it's at least a few pixels larger + // + osg::Vec3d pCorners[8]; + calculateFrustumCorners(pssmShadowSplitTexture,pCorners); + + // Init Light (Directional Light) + // + calculateLightInitalPosition(pssmShadowSplitTexture,pCorners); + + // Calculate near and far for light view + // + calculateLightNearFarFormFrustum(pssmShadowSplitTexture,pCorners); + + // Calculate view and projection matrices + // + calculateLightViewProjectionFormFrustum(pssmShadowSplitTexture,pCorners); + + ////////////////////////////////////////////////////////////////////////// + // set up shadow rendering camera + pssmShadowSplitTexture._camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + + ////////////////////////////////////////////////////////////////////////// + // DEBUG + if ( _displayTexturesGroupingNode ) { + pssmShadowSplitTexture._debug_camera->setViewMatrix(pssmShadowSplitTexture._camera->getViewMatrix()); + pssmShadowSplitTexture._debug_camera->setProjectionMatrix(pssmShadowSplitTexture._camera->getProjectionMatrix()); + pssmShadowSplitTexture._debug_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + } + + ////////////////////////////////////////////////////////////////////////// + // compute the matrix which takes a vertex from local coords into tex coords + // will use this later to specify osg::TexGen.. + + osg::Matrix MVPT = pssmShadowSplitTexture._camera->getViewMatrix() * + pssmShadowSplitTexture._camera->getProjectionMatrix() * + osg::Matrix::translate(1.0,1.0,1.0) * + osg::Matrix::scale(0.5,0.5,0.5); + + pssmShadowSplitTexture._texgen->setMode(osg::TexGen::EYE_LINEAR); + pssmShadowSplitTexture._texgen->setPlanesFromMatrix(MVPT); + ////////////////////////////////////////////////////////////////////////// + + + ////////////////////////////////////////////////////////////////////////// + cv.setTraversalMask( traversalMask & getShadowedScene()->getCastsShadowTraversalMask() ); + + // do RTT camera traversal + pssmShadowSplitTexture._camera->accept(cv); + + ////////////////////////////////////////////////////////////////////////// + // DEBUG + if ( _displayTexturesGroupingNode ) { + pssmShadowSplitTexture._debug_camera->accept(cv); + } + + + orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(pssmShadowSplitTexture._textureUnit, cv.getModelViewMatrix(), pssmShadowSplitTexture._texgen.get()); + + + } + } // if light + + + + // reapply the original traversal mask + cv.setTraversalMask( traversalMask ); +} + +void ParallelSplitShadowMap::cleanSceneGraph(){ + +} + + + +// Computes corner points of a frustum +// +// +//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_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_pointNearTL(-1.0f, 1.0f, -1.0f); +// + +void ParallelSplitShadowMap::calculateFrustumCorners( + PSSMShadowSplitTexture &pssmShadowSplitTexture, + osg::Vec3d *frustumCorners +) { + double fovy,aspectRatio,camNear,camFar; + pssmShadowSplitTexture._cameraProj.getPerspective(fovy,aspectRatio,camNear,camFar); + + ////////////////////////////////////////////////////////////////////////// + /// CALCULATE SPLIT + double maxFar = camFar; + double camNearFar_Dist = maxFar - camNear; + bool linear = LINEAR_SPLIT; + if ( linear ) { + camFar = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID+1))/((double)(NUM_SPLITS)); + camNear = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID))/((double)(NUM_SPLITS)); + } else { + + // Exponential split scheme: + // + // Ci = (n - f)*(i/numsplits)^(bias+1) + n; + // + static float fSplitSchemeBias[2]={0.25f,0.66f}; + fSplitSchemeBias[1]=Clamp(fSplitSchemeBias[1],0.0f,3.0f); + float* pSplitDistances =new float[NUM_SPLITS+1]; + + for(int i=0;i<(int)NUM_SPLITS;i++) { + float fIDM=i/(float)NUM_SPLITS; + pSplitDistances[i]=(camFar-camNear)*(powf(fIDM,fSplitSchemeBias[1]+1))+camNear; + } + // make sure border values are right + pSplitDistances[0]=camNear; + pSplitDistances[NUM_SPLITS]=camFar; + + camNear = pSplitDistances[pssmShadowSplitTexture._splitID]; + camFar = pSplitDistances[pssmShadowSplitTexture._splitID+1]; + + delete[] pSplitDistances; + + } + + + + #ifdef SHADOW_TEXTURE_GLSL + pssmShadowSplitTexture._farDistanceSplit->set((float)((maxFar-camNear)/camNearFar_Dist)); + #endif + + ////////////////////////////////////////////////////////////////////////// + /// TRANSFORM frustum corners (Optimized for Orthogonal) + osg::Matrix projMat; + projMat.makePerspective(fovy,aspectRatio,camNear,camFar); + + osg::Matrix projViewMat = pssmShadowSplitTexture._cameraView*projMat; + osg::Matrix invProjViewMat; + invProjViewMat.invert(projViewMat); + + //transform frustum vertices to world space + frustumCorners[0] = const_pointFarBR * invProjViewMat; + frustumCorners[1] = const_pointNearBR* invProjViewMat; + frustumCorners[2] = const_pointNearTR* invProjViewMat; + frustumCorners[3] = const_pointFarTR * invProjViewMat; + frustumCorners[4] = const_pointFarTL * invProjViewMat; + frustumCorners[5] = const_pointFarBL * invProjViewMat; + frustumCorners[6] = const_pointNearBL* invProjViewMat; + frustumCorners[7] = const_pointNearTL* invProjViewMat; + +} + +////////////////////////////////////////////////////////////////////////// +// +// compute directional light inital postion; +void ParallelSplitShadowMap::calculateLightInitalPosition(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners){ + pssmShadowSplitTexture._frustumSplitCenter = frustumCorners[0]; + for(int i=1;i<8;i++) { + pssmShadowSplitTexture._frustumSplitCenter +=frustumCorners[i]; + } + pssmShadowSplitTexture._frustumSplitCenter /= 8.0; + pssmShadowSplitTexture._lightCameraSource = pssmShadowSplitTexture._frustumSplitCenter; +} + +void ParallelSplitShadowMap::calculateLightNearFarFormFrustum( + PSSMShadowSplitTexture &pssmShadowSplitTexture, + osg::Vec3d *frustumCorners +) { + + //calculate near, far + double zNear=-1; + double zFar =-1; + + + + + // force zNear > 0.0 + // set 2.0m distance to the nearest point + int count = 0; + while (zNear <= ZNEAR_MIN_FROM_LIGHT_SOURCE && count++ < 10) { + zNear= DBL_MAX; + zFar =-DBL_MAX; + for(int i=0;i<8;i++) { + double dist_z_from_light = pssmShadowSplitTexture._lightDirection*(frustumCorners[i] - pssmShadowSplitTexture._lightCameraSource); + if ( zNear > dist_z_from_light ) zNear = dist_z_from_light; + if ( zFar < dist_z_from_light ) zFar = dist_z_from_light; + } + + if ( zNear <= ZNEAR_MIN_FROM_LIGHT_SOURCE ){ + osg::Vec3 dUpdate = - pssmShadowSplitTexture._lightDirection*(fabs(zNear)+ZNEAR_MIN_FROM_LIGHT_SOURCE); + pssmShadowSplitTexture._lightCameraSource = pssmShadowSplitTexture._lightCameraSource + dUpdate; + } + + } + + + pssmShadowSplitTexture._lightCameraTarget = pssmShadowSplitTexture._lightCameraSource + pssmShadowSplitTexture._lightDirection*zFar; + pssmShadowSplitTexture._lightNear = zNear; + pssmShadowSplitTexture._lightFar = zFar; +} + +void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners) { + ////////////////////////////////////////////////////////////////////////// + + // light dir + osg::Vec3d lightDir = pssmShadowSplitTexture._lightDirection; + + osg::Vec3d up(0,1,0); + osg::Vec3d left; + osg::Vec3d top; + + left = up ^ lightDir; + top = lightDir ^ left; + + + double maxLeft,maxTop; + double minLeft,minTop; + + osg::Vec3d fCenter = pssmShadowSplitTexture._frustumSplitCenter; + + maxLeft = maxTop = -DBL_MAX; + minLeft = minTop = DBL_MAX; + for(int i = 0; i < 8; i++) + { + osg::Vec3d diffCorner = fCenter - frustumCorners[i]; + double lLeft = (diffCorner*left) * 1.5; // scale, removes edges problem, faster for calculation + double lTop = (diffCorner*top) * 1.5; // scale, removes edges problem, faster for calculation + + if ( lLeft > maxLeft ) maxLeft = lLeft; + if ( lTop > maxTop ) maxTop = lTop ; + + if ( lLeft < minLeft ) minLeft = lLeft; + if ( lTop < minTop ) minTop = lTop ; + } + + osg::Matrixd lightView; + lightView.makeLookAt(pssmShadowSplitTexture._lightCameraSource,pssmShadowSplitTexture._lightCameraTarget,up); + osg::Matrixd lightProj; + + double zNear = pssmShadowSplitTexture._lightNear; + double zFar = pssmShadowSplitTexture._lightFar; + + lightProj.makeOrtho(minLeft,maxLeft,minTop,maxTop,zNear,zFar); + + pssmShadowSplitTexture._camera->setViewMatrix(lightView); + pssmShadowSplitTexture._camera->setProjectionMatrix(lightProj); + + + +} + + + + + + + +