#include "shadow.hpp" #include <osgShadow/ShadowedScene> #include <osg/CullFace> #include <osg/Geode> #include <osg/io_utils> #include <osgDB/FileUtils> #include <osgDB/ReadFile> #include <components/settings/settings.hpp> namespace SceneUtil { using namespace osgShadow; std::string debugVertexShaderSource = "void main(void){gl_Position = gl_Vertex; gl_TexCoord[0]=gl_MultiTexCoord0;}"; std::string debugFragmentShaderSource = "uniform sampler2D texture; \n" " \n" "void main(void) \n" "{ \n" #if 1 " float f = texture2D( texture, gl_TexCoord[0].xy ).r; \n" " \n" " f = 256.0 * f; \n" " float fC = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fS = floor( f ) / 256.0; \n" " \n" " f = 256.0 * fract( f ); \n" " float fH = floor( f ) / 256.0; \n" " \n" " fS *= 0.5; \n" " fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); \n" " \n" " vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), \n" " abs( fC - 0.333333 ), \n" " abs( fC - 0.666667 ) ); \n" " \n" " rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); \n" " \n" " float fMax = max( max( rgb.r, rgb.g ), rgb.b ); \n" " fMax = 1.0 / fMax; \n" " \n" " vec3 color = fMax * rgb; \n" " \n" " gl_FragColor = vec4( fS + fH * color, 1 ); \n" #else " gl_FragColor = texture2D(texture, gl_TexCoord[0].xy); \n" #endif "} \n"; void MWShadow::setupShadowSettings(osg::ref_ptr<osgShadow::ShadowSettings> settings, int castsShadowMask) { if (!Settings::Manager::getBool("enable shadows", "Shadows")) return; settings->setLightNum(0); settings->setCastsShadowTraversalMask(castsShadowMask); settings->setReceivesShadowTraversalMask(~0u); int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows"); settings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight); settings->setBaseShadowTextureUnit(8 - numberOfShadowMapsPerLight); settings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows")); if (Settings::Manager::getBool("compute tight scene bounds", "Shadows")) settings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows"); settings->setTextureSize(osg::Vec2s(mapres, mapres)); } void MWShadow::disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateset) { int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows"); int baseShadowTextureUnit = 8 - numberOfShadowMapsPerLight; osg::ref_ptr<osg::Image> fakeShadowMapImage = new osg::Image(); fakeShadowMapImage->allocateImage(1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT); *(float*)fakeShadowMapImage->data() = std::numeric_limits<float>::infinity(); osg::ref_ptr<osg::Texture> fakeShadowMapTexture = new osg::Texture2D(fakeShadowMapImage); fakeShadowMapTexture->setShadowComparison(true); fakeShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS); for (int i = baseShadowTextureUnit; i < baseShadowTextureUnit + numberOfShadowMapsPerLight; ++i) stateset->setTextureAttributeAndModes(i, fakeShadowMapTexture, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED); } MWShadow::MWShadow() : enableShadows(Settings::Manager::getBool("enable shadows", "Shadows")), numberOfShadowMapsPerLight(Settings::Manager::getInt("number of shadow maps", "Shadows")), baseShadowTextureUnit(8 - numberOfShadowMapsPerLight), debugHud(Settings::Manager::getBool("enable debug hud", "Shadows")), debugProgram(new osg::Program), debugTextureUnit(0) { if (debugHud) { osg::ref_ptr<osg::Shader> vertexShader = new osg::Shader(osg::Shader::VERTEX, debugVertexShaderSource); debugProgram->addShader(vertexShader); osg::ref_ptr<osg::Shader> fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, debugFragmentShaderSource); debugProgram->addShader(fragmentShader); for (int i = 0; i < numberOfShadowMapsPerLight; ++i) { std::cout << i << std::endl; debugCameras.push_back(new osg::Camera); debugCameras[i]->setViewport(200 * i, 0, 200, 200); debugCameras[i]->setRenderOrder(osg::Camera::POST_RENDER); debugCameras[i]->setClearColor(osg::Vec4(1.0, 1.0, 0.0, 1.0)); debugGeometry.push_back(osg::createTexturedQuadGeometry(osg::Vec3(-1, -1, 0), osg::Vec3(2, 0, 0), osg::Vec3(0, 2, 0))); debugGeometry[i]->setCullingActive(false); debugCameras[i]->addChild(debugGeometry[i]); osg::ref_ptr<osg::StateSet> stateSet = debugGeometry[i]->getOrCreateStateSet(); stateSet->setAttributeAndModes(debugProgram, osg::StateAttribute::ON); osg::ref_ptr<osg::Uniform> textureUniform = new osg::Uniform("texture", debugTextureUnit); //textureUniform->setType(osg::Uniform::SAMPLER_2D); stateSet->addUniform(textureUniform.get()); } } } 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<osg::RefMatrix> _projectionMatrix; osg::ref_ptr<osgUtil::RenderStage> _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<osgUtil::CullVisitor*>(nv); osg::Camera* camera = dynamic_cast<osg::Camera*>(node); OSG_INFO << "VDSMCameraCullCallback::operator()(osg::Node* " << camera << ", osg::NodeVisitor* " << cv << ")" << std::endl; #if 1 if (!_polytope.empty()) { OSG_INFO << "Pushing custom Polytope" << std::endl; osg::CullingSet& cs = cv->getProjectionCullingStack().back(); cs.setFrustum(_polytope); cv->pushCullingSet(); } #endif if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } #if 1 if (!_polytope.empty()) { OSG_INFO << "Popping custom Polytope" << std::endl; cv->popCullingSet(); } #endif _renderStage = cv->getCurrentRenderBin()->getStage(); OSG_INFO << "VDSM second : _renderStage = " << _renderStage << std::endl; 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 " << projection << std::endl; osg::Matrix::value_type left, right, bottom, top, zNear, zFar; osg::Matrix::value_type epsilon = 1e-6; if (fabs(projection(0, 3))<epsilon && fabs(projection(1, 3))<epsilon && fabs(projection(2, 3))<epsilon) { projection.getOrtho(left, right, bottom, top, zNear, zFar); OSG_INFO << "Ortho zNear=" << zNear << ", zFar=" << zFar << std::endl; } else { projection.getFrustum(left, right, bottom, top, zNear, zFar); OSG_INFO << "Frustum zNear=" << zNear << ", zFar=" << zFar << std::endl; } OSG_INFO << "Calculated zNear = " << cv->getCalculatedNearPlane() << ", zFar = " << cv->getCalculatedFarPlane() << std::endl; zNear = osg::maximum(zNear, cv->getCalculatedNearPlane()); zFar = osg::minimum(zFar, cv->getCalculatedFarPlane()); cv->setCalculatedNearPlane(zNear); cv->setCalculatedFarPlane(zFar); cv->clampProjectionMatrix(projection, zNear, zFar); //OSG_INFO<<"RTT zNear = "<<zNear<<", zFar = "<<zFar<<std::endl; OSG_INFO << "RTT Projection matrix after clamping " << projection << std::endl; camera->setProjectionMatrix(projection); _projectionMatrix = cv->getProjectionMatrix(); } } MWShadow::ComputeLightSpaceBounds::ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) { setCullingMode(osg::CullSettings::VIEW_FRUSTUM_CULLING); pushViewport(viewport); pushProjectionMatrix(new osg::RefMatrix(projectionMatrix)); pushModelViewMatrix(new osg::RefMatrix(viewMatrix), osg::Transform::ABSOLUTE_RF); } void MWShadow::ComputeLightSpaceBounds::apply(osg::Node& node) { if (isCulled(node)) return; // push the culling mode. pushCurrentMask(); traverse(node); // pop the culling mode. popCurrentMask(); } void MWShadow::ComputeLightSpaceBounds::apply(osg::Geode& node) { if (isCulled(node)) return; // push the culling mode. pushCurrentMask(); for (unsigned int i = 0; i<node.getNumDrawables(); ++i) { if (node.getDrawable(i)) { updateBound(node.getDrawable(i)->getBoundingBox()); } } // pop the culling mode. popCurrentMask(); } void MWShadow::ComputeLightSpaceBounds::apply(osg::Drawable& drawable) { if (isCulled(drawable)) return; // push the culling mode. pushCurrentMask(); updateBound(drawable.getBoundingBox()); // pop the culling mode. popCurrentMask(); } void MWShadow::ComputeLightSpaceBounds::apply(osg::Billboard&) { OSG_INFO << "Warning Billboards not yet supported" << std::endl; return; } void MWShadow::ComputeLightSpaceBounds::apply(osg::Projection&) { // projection nodes won't affect a shadow map so their subgraphs should be ignored return; } void MWShadow::ComputeLightSpaceBounds::apply(osg::Transform& transform) { if (isCulled(transform)) return; // push the culling mode. pushCurrentMask(); // absolute transforms won't affect a shadow map so their subgraphs should be ignored. if (transform.getReferenceFrame() == osg::Transform::RELATIVE_RF) { osg::ref_ptr<osg::RefMatrix> matrix = new osg::RefMatrix(*getModelViewMatrix()); transform.computeLocalToWorldMatrix(*matrix, this); pushModelViewMatrix(matrix.get(), transform.getReferenceFrame()); traverse(transform); popModelViewMatrix(); } // pop the culling mode. popCurrentMask(); } void MWShadow::ComputeLightSpaceBounds::apply(osg::Camera&) { // camera nodes won't affect a shadow map so their subgraphs should be ignored return; } void MWShadow::ComputeLightSpaceBounds::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 MWShadow::ComputeLightSpaceBounds::update(const osg::Vec3& v) { if (v.z()<-1.0f) { //OSG_NOTICE<<"discarding("<<v<<")"<<std::endl; return; } float x = v.x(); if (x<-1.0f) x = -1.0f; if (x>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())); } void MWShadow::cull(osgUtil::CullVisitor& cv) { if (!enableShadows) { _shadowedScene->osg::Group::traverse(cv); return; } OSG_INFO << std::endl << std::endl << "ViewDependentShadowMap::cull(osg::CullVisitor&" << &cv << ")" << std::endl; if (!_shadowCastingStateSet) { OSG_INFO << "Warning, init() has not yet been called so ShadowCastingStateSet has not been setup yet, unable to create shadows." << std::endl; _shadowedScene->osg::Group::traverse(cv); return; } ViewDependentData* vdd = getViewDependentData(&cv); if (!vdd) { OSG_INFO << "Warning, now ViewDependentData created, unable to create shadows." << std::endl; _shadowedScene->osg::Group::traverse(cv); return; } ShadowSettings* settings = getShadowedScene()->getShadowSettings(); OSG_INFO << "cv->getProjectionMatrix()=" << *cv.getProjectionMatrix() << std::endl; osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode(); osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix(); // check whether this main views projection is perspective or orthographic bool orthographicViewFrustum = viewProjectionMatrix(0, 3) == 0.0 && viewProjectionMatrix(1, 3) == 0.0 && viewProjectionMatrix(2, 3) == 0.0; double minZNear = 0.0; double maxZFar = DBL_MAX; if (cachedNearFarMode == osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { double left, right, top, bottom; if (orthographicViewFrustum) { viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); } else { viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); } OSG_INFO << "minZNear=" << minZNear << ", maxZFar=" << maxZFar << std::endl; } // set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible if (settings->getComputeNearFarModeOverride() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride()); } // 1. Traverse main scene graph cv.pushStateSet(_shadowRecievingPlaceholderStateSet.get()); osg::ref_ptr<osgUtil::StateGraph> decoratorStateGraph = cv.getCurrentStateGraph(); cullShadowReceivingScene(&cv); cv.popStateSet(); if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { OSG_INFO << "Just done main subgraph traversak" << std::endl; // make sure that the near plane is computed correctly so that any projection matrix computations // are all done correctly. cv.computeNearPlane(); } // clamp the minZNear and maxZFar to those provided by ShadowSettings maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(), maxZFar); if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio(); //OSG_NOTICE<<"maxZFar "<<maxZFar<<std::endl; Frustum frustum(&cv, minZNear, maxZFar); double reducedNear, reducedFar; if (cv.getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { reducedNear = osg::maximum<double>(cv.getCalculatedNearPlane(), minZNear); reducedFar = osg::minimum<double>(cv.getCalculatedFarPlane(), maxZFar); } else { reducedNear = minZNear; reducedFar = maxZFar; } // return compute near far mode back to it's original settings cv.setComputeNearFarMode(cachedNearFarMode); OSG_INFO << "frustum.eye=" << frustum.eye << ", frustum.centerNearPlane, " << frustum.centerNearPlane << " distance = " << (frustum.eye - frustum.centerNearPlane).length() << std::endl; // 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 textureUnit = settings->getBaseShadowTextureUnit(); unsigned int numValidShadows = 0; ShadowDataList& sdl = vdd->getShadowDataList(); ShadowDataList previous_sdl; previous_sdl.swap(sdl); unsigned int numShadowMapsPerLight = settings->getNumShadowMapsPerLight(); /*if (numShadowMapsPerLight>2) { OSG_NOTICE << "numShadowMapsPerLight of " << numShadowMapsPerLight << " is greater than maximum supported, falling back to 2." << std::endl; numShadowMapsPerLight = 2; }*/ 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; // 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" << std::endl; continue; } // 3.2 compute RTT camera view+projection matrix settings // osg::Matrixd projectionMatrix; osg::Matrixd viewMatrix; if (!computeShadowCameraSettings(frustum, pl, projectionMatrix, viewMatrix)) { OSG_NOTICE << "No valid Camera settings, no shadow to render" << std::endl; continue; } // if we are using multiple shadow maps and CastShadowTraversalMask is being used // traverse the scene to compute the extents of the objects if (/*numShadowMapsPerLight>1 &&*/ _shadowedScene->getCastsShadowTraversalMask() != 0xffffffff) { // osg::ElapsedTime timer; osg::ref_ptr<osg::Viewport> 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 "<<clsb._bb.xMin()<<", "<<clsb._bb.xMax()<<", "<<clsb._bb.yMin()<<", "<<clsb._bb.yMax()<<", "<<clsb._bb.zMin()<<", "<<clsb._bb.zMax()<<std::endl; // OSG_NOTICE<<" time "<<timer.elapsedTime_m()<<"ms, mask = "<<std::hex<<_shadowedScene->getCastsShadowTraversalMask()<<std::endl; if (clsb._bb.xMin()>-1.0f || clsb._bb.xMax()<1.0f || clsb._bb.yMin()>-1.0f || clsb._bb.yMax()<1.0f) { // OSG_NOTICE<<"Need to clamp projection matrix"<<std::endl; #if 1 double xMid = (clsb._bb.xMin() + clsb._bb.xMax())*0.5f; double xRange = clsb._bb.xMax() - clsb._bb.xMin(); #else double xMid = 0.0; double xRange = 2.0; #endif double yMid = (clsb._bb.yMin() + clsb._bb.yMax())*0.5f; double yRange = (clsb._bb.yMax() - clsb._bb.yMin()); osg::Matrixd cornerConverter = osg::Matrixd::inverse(projectionMatrix) * osg::Matrixd::inverse(viewMatrix) * *cv.getModelViewMatrix(); double minZ = DBL_MAX; double maxZ = -DBL_MAX; for (unsigned int i = 0; i < 8; i++) { osg::Vec3 corner = clsb._bb.corner(i); corner = corner * cornerConverter; maxZ = osg::maximum<double>(maxZ, -corner.z()); minZ = osg::minimum<double>(minZ, -corner.z()); } reducedNear = osg::maximum<double>(reducedNear, minZ); reducedFar = osg::minimum<double>(reducedFar, maxZ); // OSG_NOTICE<<" xMid="<<xMid<<", yMid="<<yMid<<", xRange="<<xRange<<", yRange="<<yRange<<std::endl; projectionMatrix = projectionMatrix * osg::Matrixd::translate(osg::Vec3d(-xMid, -yMid, 0.0)) * osg::Matrixd::scale(osg::Vec3d(2.0 / xRange, 2.0 / yRange, 1.0)); } } double splitPoint = 0.0; if (numShadowMapsPerLight>1) { osg::Vec3d eye_v = frustum.eye * viewMatrix; osg::Vec3d center_v = frustum.center * viewMatrix; osg::Vec3d viewdir_v = center_v - eye_v; viewdir_v.normalize(); osg::Vec3d lightdir(0.0, 0.0, -1.0); double dotProduct_v = lightdir * viewdir_v; double angle = acosf(dotProduct_v); osg::Vec3d eye_ls = eye_v * projectionMatrix; OSG_INFO << "Angle between view vector and eye " << osg::RadiansToDegrees(angle) << std::endl; OSG_INFO << "eye_ls=" << eye_ls << std::endl; if (eye_ls.y() >= -1.0 && eye_ls.y() <= 1.0) { OSG_INFO << "Eye point inside light space clip region " << std::endl; splitPoint = 0.0; } else { double n = -1.0 - eye_ls.y(); double f = 1.0 - eye_ls.y(); double sqrt_nf = sqrt(n*f); double mid = eye_ls.y() + sqrt_nf; double ratioOfMidToUseForSplit = 0.8; splitPoint = mid * ratioOfMidToUseForSplit; OSG_INFO << " n=" << n << ", f=" << f << ", sqrt_nf=" << sqrt_nf << " mid=" << mid << std::endl; } } // 4. For each light/shadow map for (unsigned int sm_i = 0; sm_i<numShadowMapsPerLight; ++sm_i) { osg::ref_ptr<ShadowData> sd; if (previous_sdl.empty()) { OSG_INFO << "Create new ShadowData" << std::endl; sd = new ShadowData(vdd); } else { OSG_INFO << "Taking ShadowData from from of previous_sdl" << std::endl; sd = previous_sdl.front(); previous_sdl.erase(previous_sdl.begin()); } if (debugHud) { osg::ref_ptr<osg::Texture2D> texture = sd->_texture; osg::ref_ptr<osg::StateSet> stateSet = debugGeometry[sm_i]->getOrCreateStateSet(); stateSet->setTextureAttributeAndModes(debugTextureUnit, texture, osg::StateAttribute::ON); unsigned int traversalMask = cv.getTraversalMask(); cv.setTraversalMask(debugGeometry[sm_i]->getNodeMask()); cv.pushStateSet(stateSet); debugCameras[sm_i]->accept(cv); cv.popStateSet(); cv.setTraversalMask(traversalMask); cv.getState()->setCheckForGLErrors(osg::State::ONCE_PER_ATTRIBUTE); } osg::ref_ptr<osg::Camera> camera = sd->_camera; camera->setProjectionMatrix(projectionMatrix); camera->setViewMatrix(viewMatrix); if (settings->getDebugDraw()) { camera->getViewport()->x() = pos_x; pos_x += static_cast<unsigned int>(camera->getViewport()->width()) + 40; } // transform polytope in model coords into light spaces eye coords. osg::Matrixd invertModelView; invertModelView.invert(camera->getViewMatrix()); osg::Polytope local_polytope(polytope); local_polytope.transformProvidingInverse(invertModelView); 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); #elif 0 // hardwired for 2 splits double r_start = (sm_i == 0) ? -1.0 : splitPoint; double r_end = (sm_i + 1 == numShadowMapsPerLight) ? 1.0 : splitPoint; #elif 0 double r_start, r_end; // Split such that each shadow map covers a quarter of the area of the one after it if (sm_i == 0) r_start = -1.0; else { r_start = (1 - pow(4.0, sm_i)) / (1 - pow(4.0, numShadowMapsPerLight)); r_start *= 2.0; //r_start += double(sm_i) / double(numShadowMapsPerLight); r_start -= 1.0; } if (sm_i + 1 == numShadowMapsPerLight) r_end = 1.0; else { r_end = (1 - pow(4.0, sm_i + 1)) / (1 - pow(4.0, numShadowMapsPerLight)); r_end *= 2.0; //r_end += double(sm_i + 1) / double(numShadowMapsPerLight); r_end -= 1.0; } #else double r_start, r_end; // split system based on the original Parallel Split Shadow Maps paper. double n = reducedNear; double f = reducedFar; double i = double(sm_i); double m = double(numShadowMapsPerLight); double ratio = Settings::Manager::getFloat("split point uniform logarithmic ratio", "Shadows"); double deltaBias = Settings::Manager::getFloat("split point bias", "Shadows"); if (sm_i == 0) r_start = -1.0; else { // compute the split point in main camera view double ciLog = n * pow(f / n, i / m); double ciUniform = n + (f - n) * i / m; double ci = ratio * ciLog + (1.0 - ratio) * ciUniform + deltaBias; // work out where this is in light space osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci; osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix; r_start = lightSpacePos.y(); } if (sm_i + 1 == numShadowMapsPerLight) r_end = 1.0; else { // compute the split point in main camera view double ciLog = n * pow(f / n, (i + 1) / m); double ciUniform = n + (f - n) * (i + 1) / m; double ci = ratio * ciLog + (1.0 - ratio) * ciUniform + deltaBias; // work out where this is in light space osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci; osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix; r_end = lightSpacePos.y(); } #endif // 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 + 1<numShadowMapsPerLight) r_end += 0.01; if (sm_i>0) { // 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 "<<plane<<std::endl; } if (sm_i + 1<numShadowMapsPerLight) { // not the last shadowmap so insert a polytope to clip the scene from beyond r_end // plane in clip space coords osg::Plane plane(0.0, -1.0, 0.0, r_end); // transform into eye coords plane.transformProvidingInverse(projectionMatrix); local_polytope.getPlaneList().push_back(plane); //OSG_NOTICE<<"Adding r_end plane "<<plane<<std::endl; } local_polytope.setupMask(); // OSG_NOTICE<<"Need to adjust RTT camera projection and view matrix here, r_start="<<r_start<<", r_end="<<r_end<<std::endl; // OSG_NOTICE<<" textureUnit = "<<textureUnit<<std::endl; double mid_r = (r_start + r_end)*0.5; double range_r = (r_end - r_start); // OSG_NOTICE<<" mid_r = "<<mid_r<<", range_r = "<<range_r<<std::endl; camera->setProjectionMatrix( camera->getProjectionMatrix() * osg::Matrixd::translate(osg::Vec3d(0.0, -mid_r, 0.0)) * osg::Matrixd::scale(osg::Vec3d(1.0, 2.0 / range_r, 1.0))); } osg::ref_ptr<VDSMCameraCullCallback> vdsmCallback = new VDSMCameraCullCallback(this, local_polytope); camera->setCullCallback(vdsmCallback.get()); // 4.3 traverse RTT camera // cv.pushStateSet(_shadowCastingStateSet.get()); cullShadowCastingScene(&cv, camera.get()); cv.popStateSet(); if (!orthographicViewFrustum && settings->getShadowMapProjectionHint() == osgShadow::ShadowSettings::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, sd->_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; if (textureUnit >= 8) { OSG_NOTICE << "Shadow texture unit is invalid for texgen, will not be used." << std::endl; } else { 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()<<std::endl; } Shader::ShaderManager::DefineMap MWShadow::getShadowDefines() { if (!enableShadows) return getShadowsDisabledDefines(); Shader::ShaderManager::DefineMap definesWithShadows; definesWithShadows.insert(std::make_pair(std::string("shadows_enabled"), std::string("1"))); for (int i = 0; i < numberOfShadowMapsPerLight; ++i) definesWithShadows["shadow_texture_unit_list"] += std::to_string(i) + ","; // remove extra comma definesWithShadows["shadow_texture_unit_list"] = definesWithShadows["shadow_texture_unit_list"].substr(0, definesWithShadows["shadow_texture_unit_list"].length() - 1); return definesWithShadows; } Shader::ShaderManager::DefineMap MWShadow::getShadowsDisabledDefines() { Shader::ShaderManager::DefineMap definesWithShadows; definesWithShadows.insert(std::make_pair(std::string("shadows_enabled"), std::string("0"))); definesWithShadows["shadow_texture_unit_list"] = ""; return definesWithShadows; } }