From 4238629ebf9aced0df7fc9eeaa55d1d4713bb43b Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 8 Aug 2011 17:00:55 +0000 Subject: [PATCH] Further work on basic ViewDependentShadowMaps, adding implementation of RTT camera and texgen setup. --- include/osg/Polytope | 3 +- include/osgShadow/ViewDependentShadowMap | 43 +- src/osgShadow/ViewDependentShadowMap.cpp | 534 +++++++++++++++++++---- 3 files changed, 493 insertions(+), 87 deletions(-) diff --git a/include/osg/Polytope b/include/osg/Polytope index f70cb5234..4fc570d1d 100644 --- a/include/osg/Polytope +++ b/include/osg/Polytope @@ -144,7 +144,8 @@ class OSG_EXPORT Polytope itr->flip(); } } - + + inline bool empty() const { return _planeList.empty(); } inline PlaneList& getPlaneList() { return _planeList; } diff --git a/include/osgShadow/ViewDependentShadowMap b/include/osgShadow/ViewDependentShadowMap index ff7402558..3b7e936cb 100644 --- a/include/osgShadow/ViewDependentShadowMap +++ b/include/osgShadow/ViewDependentShadowMap @@ -76,15 +76,52 @@ class OSGSHADOW_EXPORT ViewDependentShadowMap : public ShadowTechnique ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv); + struct OSGSHADOW_EXPORT Frustum + { + Frustum(osgUtil::CullVisitor* cv); - typedef std::pair< osg::ref_ptr, osg::ref_ptr > PositionedLight; + osg::Matrixd projectionMatrix; + osg::Matrixd modelViewMatrix; + + typedef std::vector Vertices; + Vertices corners; + + typedef std::vector Indices; + typedef std::vector Faces; + Faces faces; + + typedef std::vector Edges; + Edges edges; + + osg::Vec3d centerNearPlane; + osg::Vec3d centerFarPlane; + osg::Vec3d center; + osg::Vec3d frustumCenterLine; + }; + + struct OSGSHADOW_EXPORT PositionedLight + { + PositionedLight(osg::RefMatrix* lm, const osg::Light* l, const osg::Matrixd& modelViewMatrix); + + osg::ref_ptr lightMatrix; + osg::ref_ptr light; + + osg::Vec4d lightPos; + osg::Vec3d lightPos3; + osg::Vec3d lightDir; + bool directionalLight; + + typedef std::vector ActiveTextureUnits; + ActiveTextureUnits textureUnits; + }; + typedef std::list< PositionedLight > PositionedLightList; virtual bool selectActiveLights(osgUtil::CullVisitor* cv, PositionedLightList& pll) const; - virtual osg::Polytope computeLightViewFrustumPolytope(osgUtil::CullVisitor* cv, PositionedLight& positionedLight); + virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, PositionedLight& positionedLight); - virtual bool computeShadowCameraSettings(PositionedLight& positionedLight, osg::Polytope& polytope, osg::Camera* camera); + virtual bool computeShadowCameraSettings(Frustum& frustum, PositionedLight& positionedLight, osg::Camera* camera); virtual bool assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen); diff --git a/src/osgShadow/ViewDependentShadowMap.cpp b/src/osgShadow/ViewDependentShadowMap.cpp index be95071ca..b3fc7543b 100644 --- a/src/osgShadow/ViewDependentShadowMap.cpp +++ b/src/osgShadow/ViewDependentShadowMap.cpp @@ -17,6 +17,97 @@ using namespace osgShadow; +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_NOTICE<<"VDSMCameraCullCallback::operator()(osg::Node* "<getProjectionCullingStack().back(); + + cs.setFrustum(_polytope); + + cv->pushCullingSet(); + } + + if (_vdsm->getShadowedScene()) + { + _vdsm->getShadowedScene()->osg::Group::traverse(*nv); + } + + if (!_polytope.empty()) + { + OSG_NOTICE<<"Popping custom Polytope"<popCullingSet(); + } + + if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + osg::Matrixd projection = *(cv->getProjectionMatrix()); + + OSG_NOTICE<<"RTT Projection matrix "<setProjectionMatrix(projection); + } + +} + /////////////////////////////////////////////////////////////////////////////////////////////// // // ViewDependentShadowMap @@ -76,16 +167,23 @@ void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) { OSG_NOTICE<<"ViewDependentShadowMap::cull(osg::CullVisitor&"<<&cv<<")"< stateset = new osg::StateSet; // 1. Traverse main scene graph - cullShadowReceivingScene(&cv, stateset); + cullShadowReceivingScene(&cv, stateset.get()); // 2. select active light sources // create a list of light sources + their matrices to place them PositionedLightList pll; selectActiveLights(&cv, pll); + unsigned int pos_x = 0; + unsigned int baseTextureUnit = 1; + unsigned int textureUnit = baseTextureUnit; + unsigned int numValidShadows = 0; + + Frustum frustum(&cv); + for(PositionedLightList::iterator itr = pll.begin(); itr != pll.end(); ++itr) @@ -97,31 +195,120 @@ void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) // 4. For each light/shadow map { - osg::Camera* camera = 0; - osg::TexGen* texgen = 0; - unsigned int textureUnit = 0; + osg::ref_ptr camera; + + { + camera = new osg::Camera; + + camera->ref(); + + camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); + + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + //camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + + // set viewport + //camera->setViewport(0,0,_textureSize.x(),_textureSize.y()); + camera->setViewport(pos_x,0,400,400); + + pos_x += 440; + + // set the camera to render before the main camera. + //camera->setRenderOrder(osg::Camera::PRE_RENDER); + camera->setRenderOrder(osg::Camera::POST_RENDER); + + // tell the camera to use OpenGL frame buffer object where supported. + //camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + //camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW); + + // attach the texture and use it as the color buffer. + // camera->attach(osg::Camera::DEPTH_BUFFER, texture.get()); + } + + osg::ref_ptr texgen = new osg::TexGen; // 4.1 compute light space polytope // - osg::Polytope polytope = computeLightViewFrustumPolytope(&cv, pl); + 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 // - cullShadowCastingScene(&cv, camera); + cullShadowCastingScene(&cv, camera.get()); // 4.4 compute main scene graph TexGen + uniform settings + setup state // - assignTexGenSettings(&cv, camera, textureUnit, texgen); + assignTexGenSettings(&cv, camera.get(), textureUnit, texgen); + + + // mark the light as one that has active shadows and requires shaders + pl.textureUnits.push_back(textureUnit); + + + // increment counters. + ++textureUnit; + ++numValidShadows ; } } + + if (numValidShadows>0) + { + OSG_NOTICE<<"Need to assign "<clear(); + } } @@ -132,32 +319,86 @@ bool ViewDependentShadowMap::selectActiveLights(osgUtil::CullVisitor* cv, Positi //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::iterator itr = aml.begin(); - itr != aml.end(); + for(osgUtil::PositionalStateContainer::AttrMatrixList::reverse_iterator itr = aml.rbegin(); + itr != aml.rend(); ++itr) { const osg::Light* light = dynamic_cast(itr->first.get()); if (light) { - pll.push_back(PositionedLight(itr->second, light)); + PositionedLightList::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_NOTICE<<"Light num "<getLightNum()<second, light, modelViewMatrix)); + } + else + { + OSG_NOTICE<<"Light num "<getLightNum()<<" already used, ignore 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_NOTICE<<" Directional light, lightPos="< Vertices; - Vertices corners(8); - corners[0].set(-1.0,-1.0,-1.0); corners[1].set(1.0,-1.0,-1.0); corners[2].set(1.0,-1.0,1.0); @@ -184,19 +422,24 @@ osg::Polytope ViewDependentShadowMap::computeLightViewFrustumPolytope(osgUtil::C osg::Matrixd clipToWorld; clipToWorld.invert(modelViewMatrix * projectionMatrix); - osg::Vec3d center( osg::Vec3d(0.0,0.0,0.0) * clipToWorld ); - + // transform frustum corners from clipspace to world coords, and compute center for(Vertices::iterator itr = corners.begin(); itr != corners.end(); ++itr) { *itr = (*itr) * clipToWorld; + OSG_NOTICE<<" corner "<<*itr< Indices; - typedef std::vector Faces; - Faces faces(6); + // compute center and the frustumCenterLine + centerNearPlane = (corners[0]+corners[1]+corners[5]+corners[4])*0.25; + centerFarPlane = (corners[3]+corners[2]+corners[6]+corners[7])*0.25; + center = (centerNearPlane+centerNearPlane)*0.5; + frustumCenterLine = centerFarPlane-centerNearPlane; + frustumCenterLine.normalize(); + + OSG_NOTICE<<" center "< Edges; - Edges edges(12); edges[0].push_back(0); edges[0].push_back(1); // corner points on edge edges[0].push_back(2); edges[0].push_back(4); // faces on edge @@ -267,42 +508,30 @@ osg::Polytope ViewDependentShadowMap::computeLightViewFrustumPolytope(osgUtil::C edges[11].push_back(7); edges[11].push_back(4); // corner points on edge edges[11].push_back(3); edges[11].push_back(0); // faces on edge +} +osg::Polytope ViewDependentShadowMap::computeLightViewFrustumPolytope(Frustum& frustum, PositionedLight& positionedLight) +{ + OSG_NOTICE<<"computeLightViewFrustumPolytope()"<getPosition(); - osg::Vec3d lightPos3(lightPos.x(), lightPos.y(), lightPos.z()); - osg::Vec3d lightDir; - bool directionalLight = (lightPos.w()== 0.0); - if (directionalLight) + if (positionedLight.directionalLight) { - lightDir.set(-lightPos.x(), -lightPos.y(), -lightPos.z()); - lightDir.normalize(); - OSG_NOTICE<<" Directional light, lightPos="< lightSide_z.length()) ? lightSide_y : lightSide_z; + lightSide.normalize(); - return false; + //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_NOTICE<<"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_NOTICE<<"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, osg::StateSet* stateset) const