/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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 (3dhelp (at) gmail.com) */ /* ##################################################################################################### */ /* */ /* the pssm main idea is based on: */ /* */ /* Parallel-Split Shadow Maps for Large-scale Virtual Environments */ /* Fan Zhang Hanqiu Sun Leilei Xu Lee Kit Lun */ /* The Chinese University of Hong Kong */ /* */ /* Refer to our latest project webpage for "Parallel-Split Shadow Maps on Programmable GPUs" in GPU Gems */ /* */ /* ##################################################################################################### */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace osgShadow; // split scheme #define TEXTURE_RESOLUTION 1024 #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 ////////////////////////////////////////////////////////////////////////// // 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; sstr << "uniform float enableBaseTexture; " << std::endl; sstr << "uniform vec2 ambientBias;" << 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) : _textureUnitOffset(1), _debug_color_in_GLSL(false), _user_polgyonOffset_set(false), _resolution(TEXTURE_RESOLUTION), _setMaxFarDistance(1000.0), _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), _GLSL_shadow_filtered(true), _ambientBiasUniform(NULL), _ambientBias(0.1f,0.3f) { _displayTexturesGroupingNode = gr; _number_of_splits = icountplanes; _polgyonOffset.set(0.0f,0.0f); setFragmentShaderGenerator(new FragmentShaderGenerator()); setSplitCalculationMode(SPLIT_EXP); } ParallelSplitShadowMap::ParallelSplitShadowMap(const ParallelSplitShadowMap& copy, const osg::CopyOp& copyop): ShadowTechnique(copy,copyop), _displayTexturesGroupingNode(0), _textureUnitOffset(copy._textureUnitOffset), _number_of_splits(copy._number_of_splits), _debug_color_in_GLSL(copy._debug_color_in_GLSL), _polgyonOffset(copy._polgyonOffset), _user_polgyonOffset_set(copy._user_polgyonOffset_set), _resolution(copy._resolution), _setMaxFarDistance(copy._setMaxFarDistance), _isSetMaxFarDistance(copy._isSetMaxFarDistance), _split_min_near_dist(copy._split_min_near_dist), _move_vcam_behind_rcam_factor(copy._move_vcam_behind_rcam_factor), _userLight(copy._userLight), _FragmentShaderGenerator(copy._FragmentShaderGenerator), _GLSL_shadow_filtered(copy._GLSL_shadow_filtered), _SplitCalcMode(copy._SplitCalcMode), _ambientBiasUniform(NULL), _ambientBias(copy._ambientBias) { } void ParallelSplitShadowMap::resizeGLObjectBuffers(unsigned int maxSize) { for(PSSMShadowSplitTextureMap::iterator itr = _PSSMShadowSplitTextureMap.begin(); itr != _PSSMShadowSplitTextureMap.end(); ++itr) { itr->second.resizeGLObjectBuffers(maxSize); } } void ParallelSplitShadowMap::releaseGLObjects(osg::State* state) const { for(PSSMShadowSplitTextureMap::const_iterator itr = _PSSMShadowSplitTextureMap.begin(); itr != _PSSMShadowSplitTextureMap.end(); ++itr) { itr->second.releaseGLObjects(state); } } void ParallelSplitShadowMap::PSSMShadowSplitTexture::resizeGLObjectBuffers(unsigned int maxSize) { osg::resizeGLObjectBuffers(_camera, maxSize); osg::resizeGLObjectBuffers(_texture, maxSize); osg::resizeGLObjectBuffers(_stateset, maxSize); osg::resizeGLObjectBuffers(_debug_camera, maxSize); osg::resizeGLObjectBuffers(_debug_texture, maxSize); osg::resizeGLObjectBuffers(_debug_stateset, maxSize); } void ParallelSplitShadowMap::PSSMShadowSplitTexture::releaseGLObjects(osg::State* state) const { osg::releaseGLObjects(_camera, state); osg::releaseGLObjects(_texture, state); osg::releaseGLObjects(_stateset, state); osg::releaseGLObjects(_debug_camera, state); osg::releaseGLObjects(_debug_texture, state); osg::releaseGLObjects(_debug_stateset, state); } void ParallelSplitShadowMap::setAmbientBias(const osg::Vec2& ambientBias) { _ambientBias = ambientBias; if (_ambientBiasUniform ) _ambientBiasUniform->set(osg::Vec2f(_ambientBias.x(), _ambientBias.y())); } void ParallelSplitShadowMap::init() { if (!_shadowedScene) return; osg::ref_ptr sharedStateSet = new osg::StateSet; sharedStateSet->setDataVariance(osg::Object::DYNAMIC); unsigned int iCamerasMax=_number_of_splits; for (unsigned int iCameras=0;iCamerasaddUniform(shadowTextureSampler); //TODO: NOT YET SUPPORTED in the current version of the shader if ( ! _ambientBiasUniform ) { _ambientBiasUniform = new osg::Uniform("ambientBias",_ambientBias); pssmShadowSplitTexture._stateset->addUniform(_ambientBiasUniform); } std::stringstream strzShadow; strzShadow << "zShadow" << (pssmShadowSplitTexture._textureUnit-_textureUnitOffset); pssmShadowSplitTexture._farDistanceSplit = new osg::Uniform(strzShadow.str().c_str(),1.0f); pssmShadowSplitTexture._stateset->addUniform(pssmShadowSplitTexture._farDistanceSplit); osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0); pssmShadowSplitTexture._stateset->addUniform(baseTextureSampler); osg::Uniform* randomTextureSampler = new osg::Uniform("randomTexture",(int)(_textureUnitOffset+_number_of_splits)); pssmShadowSplitTexture._stateset->addUniform(randomTextureSampler); 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); } for (unsigned int textLoop(0);textLoop<_textureUnitOffset;textLoop++) { // fake texture for baseTexture, add a fake texture // we support by default at least one texture layer // without this fake texture we can not support // textured and not textured scene // TODO: at the moment the PSSM supports just one texture layer in the GLSL shader, multitexture are // not yet supported ! osg::Image* image = new osg::Image; // allocate the image data, noPixels x 1 x 1 with 4 rgba floats - equivalent 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::Vec4f color(1.0f,1.0f,1.0f,0.0f); *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->setBorderColor(osg::Vec4(1.0,1.0,1.0,1.0)); 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(textLoop,texture,osg::StateAttribute::ON); pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_1D,osg::StateAttribute::OFF); pssmShadowSplitTexture._stateset->setTextureMode(textLoop,GL_TEXTURE_2D,osg::StateAttribute::ON); pssmShadowSplitTexture._stateset->setTextureMode(textLoop,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(); #ifdef SHADOW_TEXTURE_GLSL PSSMShadowSplitTextureMap::iterator tm_itr=_PSSMShadowSplitTextureMap.begin(); #else // do traversal of shadow receiving scene which does need to be decorated by the shadow map for (PSSMShadowSplitTextureMap::iterator tm_itr=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++) #endif { PSSMShadowSplitTexture pssmShadowSplitTexture = tm_itr->second; cv.pushStateSet(pssmShadowSplitTexture._stateset.get()); ////////////////////////////////////////////////////////////////////////// // DEBUG 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); ////////////////////////////////////////////////////////////////////////// const osg::Light* selectLight = 0; /// light pos and light direction osg::Vec4 lightpos; osg::Vec3 lightDirection; if ( ! _userLight ) { // try to find a light in the scene 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; }else{ // take the user light as light source lightpos = _userLight->getPosition(); lightDirection = _userLight->getDirection(); selectLight = _userLight.get(); } if (selectLight) { // do traversal of shadow receiving 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) // calculateLightInitialPosition(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::Vec3d const_pointFarTR( 1.0, 1.0, 1.0); const osg::Vec3d const_pointFarBR( 1.0, -1.0, 1.0); const osg::Vec3d const_pointFarTL( -1.0, 1.0, 1.0); const osg::Vec3d const_pointFarBL( -1.0, -1.0, 1.0); const osg::Vec3d const_pointNearTR( 1.0, 1.0, -1.0); const osg::Vec3d const_pointNearBR( 1.0, -1.0, -1.0); const osg::Vec3d const_pointNearTL( -1.0, 1.0, -1.0); const osg::Vec3d const_pointNearBL( -1.0, -1.0, -1.0); ////////////////////////////////////////////////////////////////////////// void ParallelSplitShadowMap::calculateFrustumCorners(PSSMShadowSplitTexture &pssmShadowSplitTexture, osg::Vec3d *frustumCorners) { // get user cameras double fovy,aspectRatio,camNear,camFar; pssmShadowSplitTexture._cameraProj.getPerspective(fovy,aspectRatio,camNear,camFar); // force to max far distance to show shadow, for some scene it can be solve performance problems. if ((_isSetMaxFarDistance) && (_setMaxFarDistance < camFar)) camFar = _setMaxFarDistance; // build camera matrix with some offsets (the user view camera) osg::Matrixd viewMat; osg::Vec3d camEye,camCenter,camUp; pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp); osg::Vec3d viewDir = camCenter - camEye; //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); ////////////////////////////////////////////////////////////////////////// /// CALCULATE SPLIT double maxFar = camFar; // double minNear = camNear; double camNearFar_Dist = maxFar - camNear; 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 { // Exponential split scheme: // // Ci = (n - f)*(i/numsplits)^(bias+1) + n; // static double fSplitSchemeBias[2]={0.25f,0.66f}; fSplitSchemeBias[1]=Clamp(fSplitSchemeBias[1],0.0,3.0); double* pSplitDistances =new double[_number_of_splits+1]; for(int i=0;i<(int)_number_of_splits;i++) { double fIDM=(double)(i)/(double)(_number_of_splits); pSplitDistances[i]=camNearFar_Dist*(pow(fIDM,fSplitSchemeBias[1]+1))+camNear; } // make sure border values are right pSplitDistances[0]=camNear; pSplitDistances[_number_of_splits]=camFar; camNear = pSplitDistances[pssmShadowSplitTexture._splitID]; camFar = pSplitDistances[pssmShadowSplitTexture._splitID+1]; delete[] pSplitDistances; } pssmShadowSplitTexture._split_far = camFar; ////////////////////////////////////////////////////////////////////////// /// TRANSFORM frustum corners (Optimized for Orthogonal) osg::Matrixd projMat; projMat.makePerspective(fovy,aspectRatio,camNear,camFar); osg::Matrixd projViewMat(viewMat*projMat); osg::Matrixd 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; //std::cout << "camFar : "< dist_z_from_light ) zNear = dist_z_from_light; } // update near - far plane pssmShadowSplitTexture._lightNear = max(zNear - _split_min_near_dist - 0.01,0.01); pssmShadowSplitTexture._lightFar = zFar; } void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners) { // 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(viewDir^camUp); // we force to have normalized vectors (camera's view) camUp.normalize(); viewDir.normalize(); 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(frustumCorners[i] - pssmShadowSplitTexture._frustumSplitCenter); double lright(diffCorner*right); double lTop(diffCorner*top); if ( lright > maxRight ) maxRight = lright; if ( lTop > maxTop ) maxTop = lTop; 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(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 }