diff --git a/include/osgShadow/ShadowSettings b/include/osgShadow/ShadowSettings index cea3ee161..f0be97d5a 100644 --- a/include/osgShadow/ShadowSettings +++ b/include/osgShadow/ShadowSettings @@ -72,6 +72,20 @@ class OSGSHADOW_EXPORT ShadowSettings : public osg::Object void setPerspectiveShadowMapCutOffAngle(double angle) { _perspectiveShadowMapCutOffAngle = angle; } double getPerspectiveShadowMapCutOffAngle() const { return _perspectiveShadowMapCutOffAngle; } + + void setNumShadowMapsPerLight(unsigned int numShadowMaps) { _numShadowMapsPerLight = numShadowMaps; } + unsigned int getNumShadowMapsPerLight() const { return _numShadowMapsPerLight; } + + enum MultipleShadowMapHint + { + PARALLEL_SPLIT, + CASCADED + }; + + void setMultipleShadowMapHint(MultipleShadowMapHint hint) { _multipleShadowMapHint = hint; } + MultipleShadowMapHint getMultipleShadowMapHint() const { return _multipleShadowMapHint; } + + enum ShaderHint { NO_SHADERS, @@ -97,6 +111,10 @@ class OSGSHADOW_EXPORT ShadowSettings : public osg::Object double _minimumShadowMapNearFarRatio; ShadowMapProjectionHint _shadowMapProjectionHint; double _perspectiveShadowMapCutOffAngle; + + unsigned int _numShadowMapsPerLight; + MultipleShadowMapHint _multipleShadowMapHint; + ShaderHint _shaderHint; bool _debugDraw; diff --git a/include/osgShadow/ViewDependentShadowMap b/include/osgShadow/ViewDependentShadowMap index d8d11a8ec..2f3f3c9c0 100644 --- a/include/osgShadow/ViewDependentShadowMap +++ b/include/osgShadow/ViewDependentShadowMap @@ -159,7 +159,7 @@ class OSGSHADOW_EXPORT ViewDependentShadowMap : public ShadowTechnique virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight); - virtual bool computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Camera* camera); + virtual bool computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix); virtual bool adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& positionedLight, osg::Camera* camera); diff --git a/src/osgShadow/ShadowSettings.cpp b/src/osgShadow/ShadowSettings.cpp index 6634181db..cf9c9b8db 100644 --- a/src/osgShadow/ShadowSettings.cpp +++ b/src/osgShadow/ShadowSettings.cpp @@ -23,7 +23,9 @@ ShadowSettings::ShadowSettings(): _minimumShadowMapNearFarRatio(0.05), _shadowMapProjectionHint(PERSPECTIVE_SHADOW_MAP), _perspectiveShadowMapCutOffAngle(2.0), - _shaderHint(NO_SHADERS), + _numShadowMapsPerLight(1), + _multipleShadowMapHint(PARALLEL_SPLIT), + _shaderHint(NO_SHADERS), // _shaderHint(PROVIDE_FRAGMENT_SHADER), _debugDraw(false) { @@ -38,6 +40,8 @@ ShadowSettings::ShadowSettings(const ShadowSettings& ss, const osg::CopyOp& copy _minimumShadowMapNearFarRatio(ss._minimumShadowMapNearFarRatio), _shadowMapProjectionHint(ss._shadowMapProjectionHint), _perspectiveShadowMapCutOffAngle(ss._perspectiveShadowMapCutOffAngle), + _numShadowMapsPerLight(ss._numShadowMapsPerLight), + _multipleShadowMapHint(ss._multipleShadowMapHint), _shaderHint(ss._shaderHint), _debugDraw(ss._debugDraw) { diff --git a/src/osgShadow/ViewDependentShadowMap.cpp b/src/osgShadow/ViewDependentShadowMap.cpp index a799eb00b..6860ad862 100644 --- a/src/osgShadow/ViewDependentShadowMap.cpp +++ b/src/osgShadow/ViewDependentShadowMap.cpp @@ -14,8 +14,11 @@ #include #include #include +#include #include +#include + using namespace osgShadow; ////////////////////////////////////////////////////////////////// @@ -37,16 +40,34 @@ static const char fragmentShaderSource_withBaseTexture[] = static const char fragmentShaderSource_withBaseTexture[] = "uniform sampler2D baseTexture; \n" "uniform int baseTextureUnit; \n" - "uniform sampler2DShadow shadowTexture; \n" - "uniform int shadowTextureUnit; \n" + "uniform sampler2DShadow shadowTexture0; \n" + "uniform int shadowTextureUnit0; \n" " \n" "void main(void) \n" "{ \n" " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" " vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy ); \n" - " color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture, gl_TexCoord[shadowTextureUnit] ).r ); \n" + " color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r ); \n" " gl_FragColor = color; \n" "} \n"; + +static const char fragmentShaderSource_withBaseTexture_twoShadowMaps[] = + "uniform sampler2D baseTexture; \n" + "uniform int baseTextureUnit; \n" + "uniform sampler2DShadow shadowTexture0; \n" + "uniform int shadowTextureUnit0; \n" + "uniform sampler2DShadow shadowTexture1; \n" + "uniform int shadowTextureUnit1; \n" + " \n" + "void main(void) \n" + "{ \n" + " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" + " vec4 color = texture2D( baseTexture, gl_TexCoord[baseTextureUnit].xy ); \n" + " float shadow0 = shadow2DProj( shadowTexture0, gl_TexCoord[shadowTextureUnit0] ).r; \n" + " float shadow1 = shadow2DProj( shadowTexture1, gl_TexCoord[shadowTextureUnit1] ).r; \n" + " color *= mix( colorAmbientEmissive, gl_Color, shadow0*shadow1 ); \n" + " gl_FragColor = color; \n" + "} \n"; #endif template @@ -226,6 +247,109 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) } } + +class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack +{ +public: + ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) + { + pushViewport(viewport); + pushProjectionMatrix(new osg::RefMatrix(projectionMatrix)); + pushModelViewMatrix(new osg::RefMatrix(viewMatrix),osg::Transform::ABSOLUTE_RF); + } + + void apply(osg::Node& node) + { + if (isCulled(node)) return; + + traverse(node); + } + + void apply(osg::Geode& node) + { + if (isCulled(node)) return; + + for(unsigned int i=0; igetBound()); + } + } + } + + void apply(osg::Billboard&) + { + OSG_INFO<<"Warning Billboards not yet supported"< matrix = new osg::RefMatrix(*getModelViewMatrix()); + transform.computeLocalToWorldMatrix(*matrix,this); + pushModelViewMatrix(matrix.get(), transform.getReferenceFrame()); + + traverse(transform); + + popModelViewMatrix(); + } + + void apply(osg::Camera&) + { + // camera nodes won't affect a shadow map so their subgraphs should be ignored + return; + } + + void updateBound(const osg::BoundingBox& bb) + { + if (!bb.valid()) return; + + const osg::Matrix& matrix = *getModelViewMatrix() * *getProjectionMatrix(); + + update(bb.corner(0) * matrix); + update(bb.corner(1) * matrix); + update(bb.corner(2) * matrix); + update(bb.corner(3) * matrix); + update(bb.corner(4) * matrix); + update(bb.corner(5) * matrix); + update(bb.corner(6) * matrix); + update(bb.corner(7) * matrix); + } + + void update(const osg::Vec3& v) + { + if (v.z()<0.0f) + { + //OSG_NOTICE<<"discarding("<1.0f) x=1.0f; + float y = v.y(); + if (y<-1.0f) y=-1.0f; + if (y>1.0f) y=1.0f; + + _bb.expandBy(osg::Vec3(x,y,v.z())); + } + + osg::BoundingBox _bb; +}; + /////////////////////////////////////////////////////////////////////////////////////////////// // // LightData @@ -672,7 +796,14 @@ void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) ShadowDataList& sdl = vdd->getShadowDataList(); ShadowDataList previous_sdl; previous_sdl.swap(sdl); - + + unsigned int numShadowMapsPerLight = settings->getNumShadowMapsPerLight(); + if (numShadowMapsPerLight>2) + { + OSG_NOTICE<<"numShadowMapsPerLight of "<getLightDataList(); for(LightDataList::iterator itr = pll.begin(); itr != pll.end(); @@ -683,7 +814,114 @@ void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) LightData& pl = **itr; + // 3.1 compute light space polytope + // + osg::Polytope polytope = computeLightViewFrustumPolytope(frustum, pl); + + // if polytope is empty then no rendering. + if (polytope.empty()) + { + OSG_NOTICE<<"Polytope empty no shadow to render"<1 && _shadowedScene->getCastsShadowTraversalMask()!=0xffffffff) + { + // osg::ElapsedTime timer; + + osg::ref_ptr viewport = new osg::Viewport(0,0,2048,2048); + ComputeLightSpaceBounds clsb(viewport.get(), projectionMatrix, viewMatrix); + clsb.setTraversalMask(_shadowedScene->getCastsShadowTraversalMask()); + + osg::Matrixd invertModelView; + invertModelView.invert(viewMatrix); + osg::Polytope local_polytope(polytope); + local_polytope.transformProvidingInverse(invertModelView); + + osg::CullingSet& cs = clsb.getProjectionCullingStack().back(); + cs.setFrustum(local_polytope); + + clsb.pushCullingSet(); + + _shadowedScene->accept(clsb); + + // OSG_NOTICE<<"Extents of LightSpace "<getViewMatrix()); - polytope.transformProvidingInverse(invertModelView); + osg::Polytope local_polytope(polytope); + local_polytope.transformProvidingInverse(invertModelView); - osg::ref_ptr vdsmCallback = new VDSMCameraCullCallback(this, polytope); + + if (numShadowMapsPerLight>1) + { + // compute the start and end range in non-dimensional coords +#if 0 + double r_start = (sm_i==0) ? -1.0 : (double(sm_i)/double(numShadowMapsPerLight)*2.0-1.0); + double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : (double(sm_i+1)/double(numShadowMapsPerLight)*2.0-1.0); +#endif + + // hardwired for 2 splits + double r_start = (sm_i==0) ? -1.0 : splitPoint; + double r_end = (sm_i+1==numShadowMapsPerLight) ? 1.0 : splitPoint; + + // for all by the last shadowmap shift the r_end so that it overlaps slightly with the next shadowmap + // to prevent a seam showing through between the shadowmaps + if (sm_i+10) + { + // not the first shadowmap so insert a polytope to clip the scene from before r_start + + // plane in clip space coords + osg::Plane plane(0.0,1.0,0.0,-r_start); + + // transform into eye coords + plane.transformProvidingInverse(projectionMatrix); + local_polytope.getPlaneList().push_back(plane); + + //OSG_NOTICE<<"Adding r_start plane "< shadowTextureSampler = new osg::Uniform("shadowTexture",(int)(settings->getBaseShadowTextureUnit())); - _uniforms.push_back(shadowTextureSampler.get()); + for(unsigned int sm_i=0; sm_igetNumShadowMapsPerLight(); ++sm_i) + { + { + std::stringstream sstr; + sstr<<"shadowTexture"< shadowTextureSampler = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i)); + _uniforms.push_back(shadowTextureSampler.get()); + } - osg::ref_ptr shadowTextureUnit = new osg::Uniform("shadowTextureUnit",(int)(settings->getBaseShadowTextureUnit())); - _uniforms.push_back(shadowTextureUnit.get()); + { + std::stringstream sstr; + sstr<<"shadowTextureUnit"< shadowTextureUnit = new osg::Uniform(sstr.str().c_str(),(int)(settings->getBaseShadowTextureUnit()+sm_i)); + _uniforms.push_back(shadowTextureUnit.get()); + } + } switch(settings->getShaderHint()) { @@ -903,10 +1198,18 @@ void ViewDependentShadowMap::createShaders() case(ShadowSettings::PROVIDE_VERTEX_AND_FRAGMENT_SHADER): case(ShadowSettings::PROVIDE_FRAGMENT_SHADER): { - //osg::ref_ptr fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture); - osg::ref_ptr fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture); _program = new osg::Program; - _program->addShader(fragment_shader.get()); + + //osg::ref_ptr fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_noBaseTexture); + if (settings->getNumShadowMapsPerLight()==2) + { + _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture_twoShadowMaps)); + } + else + { + _program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource_withBaseTexture)); + } + break; } } @@ -1050,7 +1353,7 @@ osg::Polytope ViewDependentShadowMap::computeLightViewFrustumPolytope(Frustum& f return lightVolumePolytope; } -bool ViewDependentShadowMap::computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Camera* camera) +bool ViewDependentShadowMap::computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) { OSG_INFO<<"standardShadowMapCameraSettings()"<setProjectionMatrixAsOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin); - camera->setViewMatrixAsLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center, lightUp); + projectionMatrix.makeOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin); + viewMatrix.makeLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center, lightUp); } } else @@ -1172,8 +1475,8 @@ bool ViewDependentShadowMap::computeShadowCameraSettings(Frustum& frustum, Light double fov = positionedLight.light->getSpotCutoff() * 2.0; if(fov < 180.0) // spotlight { - camera->setProjectionMatrixAsPerspective(fov, 1.0, zMin, zMax); - camera->setViewMatrixAsLookAt(positionedLight.lightPos3, + projectionMatrix.makePerspective(fov, 1.0, zMin, zMax); + viewMatrix.makeLookAt(positionedLight.lightPos3, positionedLight.lightPos3+positionedLight.lightDir, lightUp); } else @@ -1206,9 +1509,9 @@ bool ViewDependentShadowMap::computeShadowCameraSettings(Frustum& frustum, Light fov=fovMAX; } - camera->setProjectionMatrixAsPerspective(fov, 1.0, zMin, zMax); - camera->setViewMatrixAsLookAt(positionedLight.lightPos3, - positionedLight.lightPos3+positionedLight.lightDir, lightUp); + projectionMatrix.makePerspective(fov, 1.0, zMin, zMax); + viewMatrix.makeLookAt(positionedLight.lightPos3, + positionedLight.lightPos3+positionedLight.lightDir, lightUp); } } @@ -1797,7 +2100,7 @@ bool ViewDependentShadowMap::adjustPerspectiveShadowMapCameraSettings(osgUtil::R #endif osg::Vec3d eye_v = frustum.eye * light_v; - osg::Vec3d centerNearPlane_v = frustum.centerNearPlane * light_v; + //osg::Vec3d centerNearPlane_v = frustum.centerNearPlane * light_v; osg::Vec3d center_v = frustum.center * light_v; osg::Vec3d viewdir_v = center_v-eye_v; viewdir_v.normalize(); @@ -1822,8 +2125,8 @@ bool ViewDependentShadowMap::adjustPerspectiveShadowMapCameraSettings(osgUtil::R } #endif - osg::Vec3d centerNearPlane_ls = frustum.centerNearPlane * light_vp; - osg::Vec3d centerFarPlane_ls = frustum.centerFarPlane * light_vp; + //osg::Vec3d centerNearPlane_ls = frustum.centerNearPlane * light_vp; + //osg::Vec3d centerFarPlane_ls = frustum.centerFarPlane * light_vp; osg::Vec3d center_ls = frustum.center * light_vp; osg::Vec3d viewdir_ls = center_ls-eye_ls; viewdir_ls.normalize(); @@ -1948,7 +2251,7 @@ bool ViewDependentShadowMap::adjustPerspectiveShadowMapCameraSettings(osgUtil::R bool ViewDependentShadowMap::assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen) { - OSG_INFO<<"assignTexGenSettings()"<getName()<<")"<addUniform(itr->get()); }