/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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. */ #include #include #include #include using namespace osgShadow; ////////////////////////////////////////////////////////////////// // fragment shader // static const char fragmentShaderSource_withBaseTexture[] = "uniform sampler2D baseTexture; \n" "uniform sampler2DShadow shadowTexture; \n" " \n" "void main(void) \n" "{ \n" " vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n" " vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy ); \n" " color *= mix( colorAmbientEmissive, gl_Color, shadow2DProj( shadowTexture, gl_TexCoord[1] ).r ); \n" " gl_FragColor = color; \n" "} \n"; template class RenderLeafTraverser : public T { public: RenderLeafTraverser() { } void traverse(const osgUtil::RenderStage* rs) { traverse(static_cast(rs)); } void traverse(const osgUtil::RenderBin* renderBin) { const osgUtil::RenderBin::RenderBinList& rbl = renderBin->getRenderBinList(); for(osgUtil::RenderBin::RenderBinList::const_iterator itr = rbl.begin(); itr != rbl.end(); ++itr) { traverse(itr->second.get()); } const osgUtil::RenderBin::RenderLeafList& rll = renderBin->getRenderLeafList(); for(osgUtil::RenderBin::RenderLeafList::const_iterator itr = rll.begin(); itr != rll.end(); ++itr) { handle(*itr); } const osgUtil::RenderBin::StateGraphList& rgl = renderBin->getStateGraphList(); for(osgUtil::RenderBin::StateGraphList::const_iterator itr = rgl.begin(); itr != rgl.end(); ++itr) { traverse(*itr); } } void traverse(const osgUtil::StateGraph* stateGraph) { const osgUtil::StateGraph::ChildList& cl = stateGraph->_children; for(osgUtil::StateGraph::ChildList::const_iterator itr = cl.begin(); itr != cl.end(); ++itr) { traverse(itr->second.get()); } const osgUtil::StateGraph::LeafList& ll = stateGraph->_leaves; for(osgUtil::StateGraph::LeafList::const_iterator itr = ll.begin(); itr != ll.end(); ++itr) { handle(itr->get()); } } inline void handle(const osgUtil::RenderLeaf* renderLeaf) { this->operator()(renderLeaf); } }; /////////////////////////////////////////////////////////////////////////////////////////////// // // VDSMCameraCullCallback // class VDSMCameraCullCallback : public osg::NodeCallback { public: VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope); virtual void operator()(osg::Node*, osg::NodeVisitor* nv); osg::RefMatrix* getProjectionMatrix() { return _projectionMatrix.get(); } osgUtil::RenderStage* getRenderStage() { return _renderStage.get(); } protected: ViewDependentShadowMap* _vdsm; osg::ref_ptr _projectionMatrix; osg::ref_ptr _renderStage; osg::Polytope _polytope; }; VDSMCameraCullCallback::VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope): _vdsm(vdsm), _polytope(polytope) { } void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = dynamic_cast(nv); osg::Camera* camera = dynamic_cast(node); OSG_INFO<<"VDSMCameraCullCallback::operator()(osg::Node* "<getProjectionCullingStack().back(); cs.setFrustum(_polytope); cv->pushCullingSet(); } if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } if (!_polytope.empty()) { OSG_INFO<<"Popping custom Polytope"<popCullingSet(); } _renderStage = cv->getRenderStage(); if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { // make sure that the near plane is computed correctly. cv->computeNearPlane(); osg::Matrixd projection = *(cv->getProjectionMatrix()); OSG_INFO<<"RTT Projection matrix "<setProjectionMatrix(projection); _projectionMatrix = cv->getProjectionMatrix(); } } /////////////////////////////////////////////////////////////////////////////////////////////// // // LightData // ViewDependentShadowMap::LightData::LightData(ViewDependentShadowMap::ViewDependentData* vdd): _viewDependentData(vdd), directionalLight(false) { } void ViewDependentShadowMap::LightData::setLightData(osg::RefMatrix* lm, const osg::Light* l, const osg::Matrixd& modelViewMatrix) { lightMatrix = lm; light = l; lightPos = light->getPosition(); directionalLight = (light->getPosition().w()== 0.0); if (directionalLight) { lightPos3.set(0.0, 0.0, 0.0); // directional light has no destinct position lightDir.set(-lightPos.x(), -lightPos.y(), -lightPos.z()); lightDir.normalize(); OSG_INFO<<" Directional light, lightPos="<getCalculatedNearPlane(); osgUtil::CullVisitor::value_type zFar = cv->getCalculatedFarPlane(); cv->clampProjectionMatrix(projectionMatrix,zNear,zFar); OSG_INFO<<"zNear = "< lock(_viewDependentDataMapMutex); ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv); if (itr!=_viewDependentDataMap.end()) return itr->second.get(); osg::ref_ptr vdd = createViewDependentData(cv); _viewDependentDataMap[cv] = vdd; return vdd.release(); } void ViewDependentShadowMap::update(osg::NodeVisitor& nv) { OSG_INFO<<"ViewDependentShadowMap::update(osg::NodeVisitor& "<<&nv<<")"<osg::Group::traverse(nv); } void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) { OSG_INFO<osg::Group::traverse(cv); return; } OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()< decoratorStateGraph = cv.getCurrentStateGraph(); cullShadowReceivingScene(&cv); cv.popStateSet(); // make sure that the near plane is computed correctly so that any projection matrix computations // are all done correctly. cv.computeNearPlane(); // return compute near far mode back to it's original settings cv.setComputeNearFarMode(cachedNearFarMode); // 2. select active light sources // create a list of light sources + their matrices to place them selectActiveLights(&cv, vdd); unsigned int pos_x = 0; unsigned int baseTextureUnit = 0; unsigned int textureUnit = baseTextureUnit+1; unsigned int numValidShadows = 0; Frustum frustum(&cv); ShadowDataList& sdl = vdd->getShadowDataList(); ShadowDataList previous_sdl; previous_sdl.swap(sdl); LightDataList& pll = vdd->getLightDataList(); for(LightDataList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures LightData& pl = **itr; // 4. For each light/shadow map { osg::ref_ptr sd; if (previous_sdl.empty()) { OSG_INFO<<"Create new ShadowData"< camera = sd->_camera; if (_debugDraw) { camera->getViewport()->x() = pos_x; pos_x += camera->getViewport()->width() + 40; } osg::ref_ptr texgen = sd->_texgen; // 4.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"<getViewMatrix()); polytope.transformProvidingInverse(invertModelView); osg::ref_ptr vdsmCallback = new VDSMCameraCullCallback(this, polytope); camera->setCullCallback(vdsmCallback.get()); // 4.3 traverse RTT camera // cv.pushStateSet(_shadowCastingStateSet.get()); cullShadowCastingScene(&cv, camera.get()); cv.popStateSet(); if (_shadowMapProjectionHint==PERSPECTIVE_SHADOW_MAP) { adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get()); if (vdsmCallback->getProjectionMatrix()) { vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix()); } } // 4.4 compute main scene graph TexGen + uniform settings + setup state // assignTexGenSettings(&cv, camera.get(), textureUnit, texgen.get()); // mark the light as one that has active shadows and requires shaders pl.textureUnits.push_back(textureUnit); // pass on shadow data to ShadowDataList sd->_textureUnit = textureUnit; sdl.push_back(sd); // increment counters. ++textureUnit; ++numValidShadows ; } } if (numValidShadows>0) { decoratorStateGraph->setStateSet(selectStateSetForRenderingShadow(*vdd)); } // OSG_NOTICE<<"End of shadow setup Projection matrix "<<*cv.getProjectionMatrix()<getLightDataList(); LightDataList previous_ldl; previous_ldl.swap(pll); //MR testing giving a specific light osgUtil::RenderStage * rs = cv->getRenderStage(); osg::Matrixd modelViewMatrix = *(cv->getModelViewMatrix()); osgUtil::PositionalStateContainer::AttrMatrixList& aml = rs->getPositionalStateContainer()->getAttrMatrixList(); for(osgUtil::PositionalStateContainer::AttrMatrixList::reverse_iterator itr = aml.rbegin(); itr != aml.rend(); ++itr) { const osg::Light* light = dynamic_cast(itr->first.get()); if (light) { LightDataList::iterator pll_itr = pll.begin(); for(; pll_itr != pll.end(); ++pll_itr) { if ((*pll_itr)->light->getLightNum()==light->getLightNum()) break; } if (pll_itr==pll.end()) { OSG_INFO<<"Light num "<getLightNum()<setLightData(itr->second.get(), light, modelViewMatrix); pll.push_back(ld); } else { OSG_INFO<<"Light num "<getLightNum()<<" already used, ignore light"<setAttribute( new osg::CullFace( osg::CullFace::FRONT ), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); // make sure GL_CULL_FACE is off by default // we assume that if object has cull face attribute set to back // it will also set cull face mode ON so no need for override _shadowCastingStateSet->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); } #if 1 float factor = 1.1; float units = 4.0; #else float factor = -1.1; float units = -4.0; #endif _polygonOffset = new osg::PolygonOffset(factor, units); _shadowCastingStateSet->setAttribute(_polygonOffset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); _uniforms.clear(); osg::ref_ptr baseTextureSampler = new osg::Uniform("baseTexture",(int)_baseTextureUnit); _uniforms.push_back(baseTextureSampler.get()); osg::ref_ptr shadowTextureSampler = new osg::Uniform("shadowTexture",(int)_shadowTextureUnit); _uniforms.push_back(shadowTextureSampler.get()); //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::Polytope ViewDependentShadowMap::computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight) { OSG_INFO<<"computeLightViewFrustumPolytope()"< lightSide_z.length()) ? lightSide_y : lightSide_z; lightSide.normalize(); #else osg::Vec3d lightSide = positionedLight.lightDir ^ frustum.frustumCenterLine; lightSide.normalize(); #endif osg::Vec3d lightUp = lightSide ^ positionedLight.lightDir; OSG_INFO<<"positionedLight.lightDir="<setProjectionMatrixAsOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin); camera->setViewMatrixAsLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center, lightUp); } } else { double zMax=-DBL_MAX; OSG_INFO<<"lightDir = "<=0.0 && d1>=0.0) { // OSG_NOTICE<<" Edge completely inside"<first; osg::Vec3d& v1 = itr->second; osg::Vec3d intersection = v0 - (v1-v0)*(d0/(d1-d0)); // OSG_NOTICE<<" Edge across clip plane, v0="<_modelview.get()!=previous_modelview) { previous_modelview = renderLeaf->_modelview.get(); light_mvp.mult(*renderLeaf->_modelview, light_vp); //OSG_NOTICE<<"Computing new light_mvp "<_drawable->getBound(); if (bb.valid()) { handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMin())); handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMin())); handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMin())); handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMin())); handle(osg::Vec3d(bb.xMin(),bb.yMin(),bb.zMax())); handle(osg::Vec3d(bb.xMax(),bb.yMin(),bb.zMax())); handle(osg::Vec3d(bb.xMin(),bb.yMax(),bb.zMax())); handle(osg::Vec3d(bb.xMax(),bb.yMax(),bb.zMax())); } else { //OSG_NOTICE<<"bb invalid"<n) { x_ratio = delta.x()/delta.y(); z_ratio = delta.z()/delta.y(); } else { x_ratio = delta.x()/n; z_ratio = delta.z()/n; } if (x_ratiomax_x_ratio) max_x_ratio = x_ratio; if (z_ratiomax_z_ratio) max_z_ratio = z_ratio; // clip to the light space if (ls.x()clip_max_x) ls.x()=clip_max_x; if (ls.y()clip_max_y) ls.y()=clip_max_y; if (ls.z()clip_max_z) ls.z()=clip_max_z; // compute the xyz range. if (ls.x()max_x) max_x=ls.x(); if (ls.y()max_y) max_y=ls.y(); if (ls.z()max_z) max_z=ls.z(); } osg::Matrixd light_vp; osg::Vec3d eye_ls; double n; osg::Matrixd light_mvp; osg::RefMatrix* previous_modelview; unsigned int numLeaves; double clip_min_x, clip_max_x; double clip_min_y, clip_max_y; double clip_min_z, clip_max_z; double clip_min_x_ratio, clip_max_x_ratio; double clip_min_z_ratio, clip_max_z_ratio; double min_x_ratio, max_x_ratio; double min_z_ratio, max_z_ratio; double min_x, max_x; double min_y, max_y; double min_z, max_z; }; bool ViewDependentShadowMap::adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& positionedLight, osg::Camera* camera) { //frustum.projectionMatrix; //frustum.modelViewMatrix; osg::Matrixd light_p = camera->getProjectionMatrix(); osg::Matrixd light_v = camera->getViewMatrix(); osg::Matrixd light_vp = light_v * light_p; osg::Vec3d lightdir(0.0,0.0,-1.0); // check whether this light space projection is perspective or orthographic. bool orthographicLightSpaceProjection = light_p(0,3)==0.0 && light_p(1,3)==0.0 && light_p(2,3)==0.0; if (!orthographicLightSpaceProjection) { OSG_INFO<<"perspective light space projection not yet supported."<setProjectionMatrix(light_p); //return true; } osg::Vec3d eye_v = frustum.eye * 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(); double dotProduct_v = lightdir * viewdir_v; double gamma_v = acos(dotProduct_v); double standardShadowMapToleranceAngle = 2.0; if (gamma_vosg::DegreesToRadians(180-standardShadowMapToleranceAngle)) { // OSG_NOTICE<<"Light and view vectors near parrallel - use standard shadow map."< rli; rli.set(osg::Matrix::inverse(frustum.modelViewMatrix) * light_vp, virtual_eye, n); rli.traverse(renderStage); #if 0 OSG_NOTICE<<"Time for RenderLeafTraverser "<setMode(osg::TexGen::EYE_LINEAR); // compute the matrix which takes a vertex from local coords into tex coords // We actually use two matrices one used to define texgen // and second that will be used as modelview when appling to OpenGL texgen->setPlanesFromMatrix( camera->getProjectionMatrix() * osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5f,0.5f,0.5f) ); // Place texgen with modelview which removes big offsets (making it float friendly) osg::ref_ptr refMatrix = new osg::RefMatrix( camera->getInverseViewMatrix() * (*(cv->getModelViewMatrix())) ); cv->getRenderStage()->getPositionalStateContainer()->addPositionedTextureAttribute( textureUnit, refMatrix.get(), texgen ); return true; } void ViewDependentShadowMap::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const { OSG_INFO<<"cullShadowReceivingScene()"<getTraversalMask(); cv->setTraversalMask( traversalMask & _shadowedScene->getReceivesShadowTraversalMask() ); _shadowedScene->osg::Group::traverse(*cv); cv->setTraversalMask( traversalMask ); return; } void ViewDependentShadowMap::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const { OSG_INFO<<"cullShadowCastingScene()"<getTraversalMask(); cv->setTraversalMask( traversalMask & _shadowedScene->getCastsShadowTraversalMask() ); if (camera) camera->accept(*cv); cv->setTraversalMask( traversalMask ); return; } osg::StateSet* ViewDependentShadowMap::selectStateSetForRenderingShadow(ViewDependentData& vdd) const { OSG_INFO<<" selectStateSetForRenderingShadow() "< stateset = vdd.getStateSet(); vdd.getStateSet()->clear(); //vdd.getStateSet()->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); for(Uniforms::const_iterator itr=_uniforms.begin(); itr!=_uniforms.end(); ++itr) { stateset->addUniform(itr->get()); } stateset->setAttribute(_program.get()); LightDataList& pll = vdd.getLightDataList(); for(LightDataList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures LightData& pl = (**itr); // if no texture units have been activated for this light then no shadow state required. if (pl.textureUnits.empty()) continue; for(LightData::ActiveTextureUnits::iterator atu_itr = pl.textureUnits.begin(); atu_itr != pl.textureUnits.end(); ++atu_itr) { OSG_INFO<<" Need to assign state for "<<*atu_itr<setTextureAttributeAndModes(sd._textureUnit, sd._texture.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON); stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON); stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON); stateset->setTextureMode(sd._textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON); } return vdd.getStateSet(); }