/* -*-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"; /////////////////////////////////////////////////////////////////////////////////////////////// // // VDSMCameraCullCallback // class VDSMCameraCullCallback : public osg::NodeCallback { public: VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope); virtual void operator()(osg::Node*, osg::NodeVisitor* nv); protected: ViewDependentShadowMap* _vdsm; 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(); } if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { osg::Matrixd projection = *(cv->getProjectionMatrix()); OSG_INFO<<"RTT Projection matrix "<setProjectionMatrix(projection); } } /////////////////////////////////////////////////////////////////////////////////////////////// // // 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; } // 1. Traverse main scene graph cv.pushStateSet( _shadowRecievingPlaceholderStateSet.get() ); osg::ref_ptr decoratorStateGraph = cv.getCurrentStateGraph(); cullShadowReceivingScene(&cv); cv.popStateSet(); // 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); camera->setCullCallback(new VDSMCameraCullCallback(this, polytope)); // 4.3 traverse RTT camera // cv.pushStateSet(_shadowCastingStateSet.get()); cullShadowCastingScene(&cv, camera.get()); cv.popStateSet(); // 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) { OSG_INFO<<"Need to assign "<setStateSet(selectStateSetForRenderingShadow(*vdd)); } } bool ViewDependentShadowMap::selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const { OSG_INFO<<"selectActiveLights"<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"< cull_face = new osg::CullFace; cull_face->setMode(osg::CullFace::FRONT); _shadowCastingStateSet->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); #endif #if 1 float factor = 1.1; float units = 4.0; _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); #endif _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(); //osg::Vec3d lightSide = positionedLight.lightDir ^ frustum.frustumCenterLine; lightSide.normalize(); osg::Vec3d lightUp = lightSide ^ positionedLight.lightDir; if (positionedLight.directionalLight) { double xMin=0.0, xMax=0.0; double yMin=0.0, yMax=0.0; double zMin=0.0, zMax=0.0; for(Frustum::Vertices::iterator itr = frustum.corners.begin(); itr != frustum.corners.end(); ++itr) { osg::Vec3d cornerDelta(*itr - frustum.center); osg::Vec3d cornerInLightCoords(cornerDelta*lightSide, cornerDelta*lightUp, cornerDelta*positionedLight.lightDir); xMin = osg::minimum( xMin, cornerInLightCoords.x()); xMax = osg::maximum( xMax, cornerInLightCoords.x()); yMin = osg::minimum( yMin, cornerInLightCoords.y()); yMax = osg::maximum( yMax, cornerInLightCoords.y()); zMin = osg::minimum( zMin, cornerInLightCoords.z()); zMax = osg::maximum( zMax, cornerInLightCoords.z()); } OSG_INFO<<"before bs xMin="<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 = "<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(); }