osg::BoundingBox MinimalShadowMap::ViewData::computeShadowReceivingCoarseBounds() { // Default slowest but most precise ShadowReceivingCoarseBoundAccuracy accuracy = DEFAULT_ACCURACY; MinimalShadowMap * msm = dynamic_cast< MinimalShadowMap* >( _st.get() ); if( msm ) accuracy = msm->getShadowReceivingCoarseBoundAccuracy(); if( accuracy == MinimalShadowMap::EMPTY_BOX ) { // One may skip coarse scene bounds computation if light is infinite. // Empty box will be intersected with view frustum so in the end // view frustum will be used as bounds approximation. // But if light is nondirectional and bounds come out too large // they may bring the effect of almost 180 deg perspective set // up for shadow camera. Such projection will significantly impact // precision of further math. return osg::BoundingBox(); } if( accuracy == MinimalShadowMap::BOUNDING_SPHERE ) { // faster but less precise rough scene bound computation // however if compute near far is active it may bring quite good result osg::Camera * camera = _cv->getRenderStage()->getCamera(); osg::Matrix m = camera->getViewMatrix() * _clampedProjection; ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( m ), m ); osg::BoundingSphere bs =_st->getShadowedScene()->getBound(); osg::BoundingBox bb; bb.expandBy( bs ); osg::Polytope box; box.setToBoundingBox( bb ); frustum.cut( box ); // approximate sphere with octahedron. Ie first cut by box then // additionaly cut with the same box rotated 45, 45, 45 deg. box.transform( // rotate box around its center osg::Matrix::translate( -bs.center() ) * osg::Matrix::rotate( osg::PI_4, 0, 0, 1 ) * osg::Matrix::rotate( osg::PI_4, 1, 1, 0 ) * osg::Matrix::translate( bs.center() ) ); frustum.cut( box ); return frustum.computeBoundingBox( ); } if( accuracy == MinimalShadowMap::BOUNDING_BOX ) // Default { // more precise method but slower method // bound visitor traversal takes lot of time for complex scenes // (note that this adds to cull time) osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); cbbv.setTraversalMask(_st->getShadowedScene()->getCastsShadowTraversalMask()); _st->getShadowedScene()->osg::Group::traverse(cbbv); return cbbv.getBoundingBox(); } return osg::BoundingBox(); }
void MinimalShadowMap::ViewData::frameShadowCastingCamera ( const osg::Camera* cameraMain, osg::Camera* cameraShadow, int pass ) { osg::Matrix mvp = cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix(); ConvexPolyhedron polytope = _sceneReceivingShadowPolytope; std::vector<osg::Vec3d> points = _sceneReceivingShadowPolytopePoints; osg::BoundingBox bb = computeScenePolytopeBounds( mvp ); // projection was trimmed above, need to recompute mvp if( bb.valid() && *_minLightMarginPtr > 0 ) { // bb._max += osg::Vec3( 1, 1, 1 ); // bb._min -= osg::Vec3( 1, 1, 1 ); osg::Matrix transform = osg::Matrix::inverse( mvp ); // Code below was working only for directional lights ie when projection was ortho // osg::Vec3d normal = osg::Matrix::transform3x3( osg::Vec3d( 0,0,-1)., transfrom ); // So I replaced it with safer code working with spot lights as well osg::Vec3d normal = osg::Vec3d(0,0,-1) * transform - osg::Vec3d(0,0,1) * transform; normal.normalize(); _sceneReceivingShadowPolytope.extrude( normal * *_minLightMarginPtr ); // Zero pass does crude shadowed scene hull approximation. // Its important to cut it to coarse light frustum properly // at this stage. // If not cut and polytope extends beyond shadow projection clip // space (-1..1), it may get "twisted" by precisely adjusted shadow cam // projection in second pass. if ( pass == 0 && _frameShadowCastingCameraPasses > 1 ) { // Make sure extruded polytope does not extend beyond light frustum osg::Polytope lightFrustum; lightFrustum.setToUnitFrustum(); lightFrustum.transformProvidingInverse( mvp ); _sceneReceivingShadowPolytope.cut( lightFrustum ); } _sceneReceivingShadowPolytopePoints.clear(); _sceneReceivingShadowPolytope.getPoints ( _sceneReceivingShadowPolytopePoints ); bb = computeScenePolytopeBounds( mvp ); } setDebugPolytope( "extended", _sceneReceivingShadowPolytope, osg::Vec4( 1, 0.5, 0, 1 ), osg::Vec4( 1, 0.5, 0, 0.1 ) ); _sceneReceivingShadowPolytope = polytope; _sceneReceivingShadowPolytopePoints = points; // Warning: Trim light projection at near plane may remove shadowing // from objects outside of view space but still casting shadows into it. // I have not noticed this issue so I left mask at default: all bits set. if( bb.valid() ) trimProjection( cameraShadow->getProjectionMatrix(), bb, 1|2|4|8|16|32 ); ///// Debuging stuff ////////////////////////////////////////////////////////// setDebugPolytope( "scene", _sceneReceivingShadowPolytope, osg::Vec4(0,1,0,1) ); #if PRINT_SHADOW_TEXEL_TO_PIXEL_ERROR if( pass == 1 ) displayShadowTexelToPixelErrors ( cameraMain, cameraShadow, &_sceneReceivingShadowPolytope ); #endif if( pass == _frameShadowCastingCameraPasses - 1 ) { #if 1 { osg::Matrix mvp = cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix(); ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( mvp ), mvp ); setDebugPolytope( "shadowCamFrustum", frustum, osg::Vec4(0,0,1,1) ); } { osg::Matrix mvp = cameraMain->getViewMatrix() * cameraMain->getProjectionMatrix(); ConvexPolyhedron frustum; frustum.setToUnitFrustum(); frustum.transform( osg::Matrix::inverse( mvp ), mvp ); setDebugPolytope( "mainCamFrustum", frustum, osg::Vec4(1,1,1,1) ); } #endif std::string * filename = getDebugDump( ); if( filename && !filename->empty() ) { dump( *filename ); filename->clear(); } } }