mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-09-24 04:11:50 -04:00
Merge branch 'improve_shadows_slim' into 'master'
Parallel Shadow Culling and Traversal See merge request OpenMW/openmw!4881
This commit is contained in:
commit
b6bfc4c06a
@ -25,9 +25,16 @@
|
|||||||
#include <osg/io_utils>
|
#include <osg/io_utils>
|
||||||
#include <osg/Depth>
|
#include <osg/Depth>
|
||||||
#include <osg/ClipControl>
|
#include <osg/ClipControl>
|
||||||
|
#include <osg/Math>
|
||||||
|
#include <osgUtil/RenderStage>
|
||||||
|
#include <osgUtil/StateGraph>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <future>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "glextensions.hpp"
|
#include "glextensions.hpp"
|
||||||
#include "shadowsbin.hpp"
|
#include "shadowsbin.hpp"
|
||||||
@ -242,7 +249,7 @@ class VDSMCameraCullCallback : public osg::NodeCallback
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope);
|
VDSMCameraCullCallback(MWShadowTechnique* vdsm, const osg::Polytope& polytope);
|
||||||
|
|
||||||
void operator()(osg::Node*, osg::NodeVisitor* nv) override;
|
void operator()(osg::Node*, osg::NodeVisitor* nv) override;
|
||||||
|
|
||||||
@ -257,47 +264,61 @@ class VDSMCameraCullCallback : public osg::NodeCallback
|
|||||||
osg::Polytope _polytope;
|
osg::Polytope _polytope;
|
||||||
};
|
};
|
||||||
|
|
||||||
VDSMCameraCullCallback::VDSMCameraCullCallback(MWShadowTechnique* vdsm, osg::Polytope& polytope):
|
VDSMCameraCullCallback::VDSMCameraCullCallback(MWShadowTechnique* vdsm, const osg::Polytope& polytope):
|
||||||
_vdsm(vdsm),
|
_vdsm(vdsm),
|
||||||
_polytope(polytope)
|
_polytope(polytope)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
{
|
{
|
||||||
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
||||||
osg::Camera* camera = node->asCamera();
|
osg::Camera* camera = node->asCamera();
|
||||||
OSG_INFO<<"VDSMCameraCullCallback::operator()(osg::Node* "<<camera<<", osg::NodeVisitor* "<<cv<<")"<<std::endl;
|
OSG_INFO<<"VDSMCameraCullCallback::operator()(osg::Node* "<<camera<<", osg::NodeVisitor* "<<cv<<")"<<std::endl;
|
||||||
|
// Make sure the current render bin is the camera's RTT render stage so that
|
||||||
|
// all render leaves generated below land in the RTT stage (not the parent).
|
||||||
|
if (osgUtil::RenderStage* stage = cv->getCurrentRenderStage())
|
||||||
|
cv->setCurrentRenderBin(stage);
|
||||||
|
|
||||||
#if 1
|
|
||||||
if (!_polytope.empty())
|
if (!_polytope.empty())
|
||||||
{
|
{
|
||||||
OSG_INFO<<"Pushing custom Polytope"<<std::endl;
|
auto& stack = cv->getProjectionCullingStack();
|
||||||
|
if (!stack.empty())
|
||||||
osg::CullingSet& cs = cv->getProjectionCullingStack().back();
|
{
|
||||||
|
osg::CullingSet& cs = stack.back();
|
||||||
cs.setFrustum(_polytope);
|
cs.setFrustum(_polytope);
|
||||||
|
cv->pushCullingSet();
|
||||||
cv->pushCullingSet();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No projection stack available; skip polytope for safety in parallel paths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Push casting state and shadows bin while traversing the RTT camera
|
||||||
|
if (auto* cast = _vdsm->getCastingStateSet())
|
||||||
|
{
|
||||||
|
cv->pushStateSet(cast);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
// bin has to go inside camera cull or the rendertexture stage will override it
|
// bin has to go inside camera cull or the rendertexture stage will override it
|
||||||
cv->pushStateSet(_vdsm->getOrCreateShadowsBinStateSet());
|
cv->pushStateSet(_vdsm->getOrCreateShadowsBinStateSet());
|
||||||
if (_vdsm->getShadowedScene())
|
if (_vdsm->getShadowedScene())
|
||||||
{
|
{
|
||||||
_vdsm->getShadowedScene()->osg::Group::traverse(*nv);
|
_vdsm->getShadowedScene()->osg::Group::traverse(*nv);
|
||||||
}
|
}
|
||||||
cv->popStateSet();
|
cv->popStateSet(); // pop shadows bin stateset
|
||||||
#if 1
|
if (_vdsm->getCastingStateSet()){
|
||||||
|
cv->popStateSet();
|
||||||
|
}
|
||||||
if (!_polytope.empty())
|
if (!_polytope.empty())
|
||||||
{
|
{
|
||||||
OSG_INFO<<"Popping custom Polytope"<<std::endl;
|
auto& stack = cv->getProjectionCullingStack();
|
||||||
cv->popCullingSet();
|
if (!stack.empty())
|
||||||
|
{
|
||||||
|
cv->popCullingSet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
_renderStage = cv->getCurrentRenderBin()->getStage();
|
|
||||||
|
|
||||||
|
// Capture the RTT render stage after traversal for PSM adjustments later.
|
||||||
|
_renderStage = cv->getCurrentRenderStage();
|
||||||
OSG_INFO<<"VDSM second : _renderStage = "<<_renderStage<<std::endl;
|
OSG_INFO<<"VDSM second : _renderStage = "<<_renderStage<<std::endl;
|
||||||
|
|
||||||
if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
|
if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR)
|
||||||
@ -305,10 +326,11 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||||||
// make sure that the near plane is computed correctly.
|
// make sure that the near plane is computed correctly.
|
||||||
cv->computeNearPlane();
|
cv->computeNearPlane();
|
||||||
|
|
||||||
osg::Matrixd projection = *(cv->getProjectionMatrix());
|
osg::RefMatrix* projRef = cv->getProjectionMatrix();
|
||||||
|
if (!projRef)
|
||||||
|
return; // cannot proceed safely without a projection matrix
|
||||||
|
osg::Matrixd projection = *projRef;
|
||||||
OSG_INFO<<"RTT Projection matrix "<<projection<<std::endl;
|
OSG_INFO<<"RTT Projection matrix "<<projection<<std::endl;
|
||||||
|
|
||||||
osg::Matrix::value_type left, right, bottom, top, zNear, zFar;
|
osg::Matrix::value_type left, right, bottom, top, zNear, zFar;
|
||||||
osg::Matrix::value_type epsilon = 1e-6;
|
osg::Matrix::value_type epsilon = 1e-6;
|
||||||
if (fabs(projection(0,3))<epsilon && fabs(projection(1,3))<epsilon && fabs(projection(2,3))<epsilon )
|
if (fabs(projection(0,3))<epsilon && fabs(projection(1,3))<epsilon && fabs(projection(2,3))<epsilon )
|
||||||
@ -316,7 +338,6 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||||||
projection.getOrtho(left, right,
|
projection.getOrtho(left, right,
|
||||||
bottom, top,
|
bottom, top,
|
||||||
zNear, zFar);
|
zNear, zFar);
|
||||||
|
|
||||||
OSG_INFO<<"Ortho zNear="<<zNear<<", zFar="<<zFar<<std::endl;
|
OSG_INFO<<"Ortho zNear="<<zNear<<", zFar="<<zFar<<std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -324,10 +345,8 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||||||
projection.getFrustum(left, right,
|
projection.getFrustum(left, right,
|
||||||
bottom, top,
|
bottom, top,
|
||||||
zNear, zFar);
|
zNear, zFar);
|
||||||
|
|
||||||
OSG_INFO<<"Frustum zNear="<<zNear<<", zFar="<<zFar<<std::endl;
|
OSG_INFO<<"Frustum zNear="<<zNear<<", zFar="<<zFar<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSG_INFO<<"Calculated zNear = "<<cv->getCalculatedNearPlane()<<", zFar = "<<cv->getCalculatedFarPlane()<<std::endl;
|
OSG_INFO<<"Calculated zNear = "<<cv->getCalculatedNearPlane()<<", zFar = "<<cv->getCalculatedFarPlane()<<std::endl;
|
||||||
|
|
||||||
zNear = osg::maximum(zNear, cv->getCalculatedNearPlane());
|
zNear = osg::maximum(zNear, cv->getCalculatedNearPlane());
|
||||||
@ -337,7 +356,6 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|||||||
cv->setCalculatedFarPlane(zFar);
|
cv->setCalculatedFarPlane(zFar);
|
||||||
|
|
||||||
cv->clampProjectionMatrix(projection, zNear, zFar);
|
cv->clampProjectionMatrix(projection, zNear, zFar);
|
||||||
|
|
||||||
//OSG_INFO<<"RTT zNear = "<<zNear<<", zFar = "<<zFar<<std::endl;
|
//OSG_INFO<<"RTT zNear = "<<zNear<<", zFar = "<<zFar<<std::endl;
|
||||||
OSG_INFO<<"RTT Projection matrix after clamping "<<projection<<std::endl;
|
OSG_INFO<<"RTT Projection matrix after clamping "<<projection<<std::endl;
|
||||||
|
|
||||||
@ -652,7 +670,6 @@ MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, d
|
|||||||
OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl;
|
OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneUtil::MWShadowTechnique::Frustum::setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride)
|
void SceneUtil::MWShadowTechnique::Frustum::setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride)
|
||||||
{
|
{
|
||||||
useCustomClipSpace = true;
|
useCustomClipSpace = true;
|
||||||
@ -691,7 +708,6 @@ void SceneUtil::MWShadowTechnique::Frustum::init()
|
|||||||
++itr)
|
++itr)
|
||||||
{
|
{
|
||||||
*itr = (*itr) * clipToWorld;
|
*itr = (*itr) * clipToWorld;
|
||||||
|
|
||||||
OSG_INFO<<" corner "<<*itr<<std::endl;
|
OSG_INFO<<" corner "<<*itr<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,7 +720,6 @@ void SceneUtil::MWShadowTechnique::Frustum::init()
|
|||||||
center = (centerNearPlane+centerFarPlane)*0.5;
|
center = (centerNearPlane+centerFarPlane)*0.5;
|
||||||
frustumCenterLine = centerFarPlane-centerNearPlane;
|
frustumCenterLine = centerFarPlane-centerNearPlane;
|
||||||
frustumCenterLine.normalize();
|
frustumCenterLine.normalize();
|
||||||
|
|
||||||
OSG_INFO<<" center "<<center<<std::endl;
|
OSG_INFO<<" center "<<center<<std::endl;
|
||||||
|
|
||||||
faces[0].push_back(0);
|
faces[0].push_back(0);
|
||||||
@ -1048,7 +1063,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSG_INFO<<std::endl<<std::endl<<"MWShadowTechnique::cull(osg::CullVisitor&"<<&cv<<")"<<std::endl;
|
|
||||||
|
|
||||||
if (!_shadowCastingStateSet)
|
if (!_shadowCastingStateSet)
|
||||||
{
|
{
|
||||||
@ -1065,13 +1079,17 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
_shadowedScene->osg::Group::traverse(cv);
|
_shadowedScene->osg::Group::traverse(cv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
OSG_INFO<<std::endl<<std::endl<<"MWShadowTechnique::cull(osg::CullVisitor&"<<&cv<<")"<<std::endl;
|
||||||
Uniforms& vddUniforms = vdd->_uniforms[cv.getTraversalNumber() % 2];
|
Uniforms& vddUniforms = vdd->_uniforms[cv.getTraversalNumber() % 2];
|
||||||
|
|
||||||
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
|
ShadowSettings* settings = getShadowedScene()->getShadowSettings();
|
||||||
|
const bool debugDraw = settings->getDebugDraw();
|
||||||
|
const bool isCascaded = settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED;
|
||||||
|
const bool isParallelSplit = settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT;
|
||||||
|
|
||||||
OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()<<std::endl;
|
OSG_INFO<<"cv->getProjectionMatrix()="<<*cv.getProjectionMatrix()<<std::endl;
|
||||||
|
|
||||||
|
|
||||||
osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();
|
osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode();
|
||||||
|
|
||||||
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
|
osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix();
|
||||||
@ -1125,8 +1143,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
||||||
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
||||||
|
|
||||||
//OSG_NOTICE<<"maxZFar "<<maxZFar<<std::endl;
|
|
||||||
|
|
||||||
// Workaround for absurdly huge viewing distances where OSG would otherwise push the near plane out.
|
// Workaround for absurdly huge viewing distances where OSG would otherwise push the near plane out.
|
||||||
cv.setNearFarRatio(minZNear / maxZFar);
|
cv.setNearFarRatio(minZNear / maxZFar);
|
||||||
|
|
||||||
@ -1146,7 +1162,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
if (itr != _viewDependentDataMap.end())
|
if (itr != _viewDependentDataMap.end())
|
||||||
{
|
{
|
||||||
OSG_INFO << "User provided a valid shared frustum hint, re-using previously generated shadow map" << std::endl;
|
OSG_INFO << "User provided a valid shared frustum hint, re-using previously generated shadow map" << std::endl;
|
||||||
|
|
||||||
copyShadowMap(cv, vdd, itr->second);
|
copyShadowMap(cv, vdd, itr->second);
|
||||||
|
|
||||||
// return compute near far mode back to it's original settings
|
// return compute near far mode back to it's original settings
|
||||||
@ -1184,8 +1199,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
// return compute near far mode back to it's original settings
|
// return compute near far mode back to it's original settings
|
||||||
cv.setComputeNearFarMode(cachedNearFarMode);
|
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
|
// 2. select active light sources
|
||||||
// create a list of light sources + their matrices to place them
|
// create a list of light sources + their matrices to place them
|
||||||
@ -1233,6 +1246,12 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precompute inverse view and base local polytope once per light
|
||||||
|
osg::Matrixd invViewMatrix;
|
||||||
|
invViewMatrix.invert(viewMatrix);
|
||||||
|
osg::Polytope baseLocalPolytope(polytope);
|
||||||
|
baseLocalPolytope.transformProvidingInverse(invViewMatrix);
|
||||||
|
|
||||||
// if we are using multiple shadow maps and CastShadowTraversalMask is being used
|
// if we are using multiple shadow maps and CastShadowTraversalMask is being used
|
||||||
// traverse the scene to compute the extents of the objects
|
// traverse the scene to compute the extents of the objects
|
||||||
if (/*numShadowMapsPerLight>1 &&*/ (_shadowedScene->getCastsShadowTraversalMask() & _worldMask) == 0)
|
if (/*numShadowMapsPerLight>1 &&*/ (_shadowedScene->getCastsShadowTraversalMask() & _worldMask) == 0)
|
||||||
@ -1323,12 +1342,9 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
|
|
||||||
osg::Vec3d eye_ls = eye_v * projectionMatrix;
|
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)
|
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;
|
splitPoint = 0.0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1339,13 +1355,108 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
double mid = eye_ls.y()+sqrt_nf;
|
double mid = eye_ls.y()+sqrt_nf;
|
||||||
double ratioOfMidToUseForSplit = 0.8;
|
double ratioOfMidToUseForSplit = 0.8;
|
||||||
splitPoint = mid * ratioOfMidToUseForSplit;
|
splitPoint = mid * ratioOfMidToUseForSplit;
|
||||||
|
|
||||||
OSG_INFO<<" n="<<n<<", f="<<f<<", sqrt_nf="<<sqrt_nf<<" mid="<<mid<<std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 4. For each light/shadow map
|
// Precompute per-cascade parameters and plane lists (parallelizable), reused below
|
||||||
|
struct SplitParams { double r_start = -1.0; double r_end = 1.0; double nearPlane = std::numeric_limits<double>::quiet_NaN(); double farPlane = std::numeric_limits<double>::quiet_NaN(); };
|
||||||
|
struct CascadePrep { SplitParams split; osg::Matrixd proj; std::vector<osg::Plane> extraPlanes; bool planesPushed = false; };
|
||||||
|
std::vector<CascadePrep> cascadePreps;
|
||||||
|
if (numShadowMapsPerLight > 1)
|
||||||
|
{
|
||||||
|
cascadePreps.resize(numShadowMapsPerLight);
|
||||||
|
std::vector<std::future<void>> preTasks;
|
||||||
|
if (mParallelShadowCullingEnabled)
|
||||||
|
preTasks.reserve(numShadowMapsPerLight);
|
||||||
|
auto doPrep = [&](unsigned int sm_i) {
|
||||||
|
CascadePrep cp;
|
||||||
|
// Split params
|
||||||
|
const double n = reducedNear;
|
||||||
|
const double f = reducedFar;
|
||||||
|
const double i = static_cast<double>(sm_i);
|
||||||
|
const double m = static_cast<double>(numShadowMapsPerLight);
|
||||||
|
if (sm_i == 0)
|
||||||
|
{
|
||||||
|
cp.split.r_start = -1.0;
|
||||||
|
cp.split.nearPlane = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const double ciLog = n * pow(f / n, i / m);
|
||||||
|
const double ciUniform = n + (f - n) * i / m;
|
||||||
|
const double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;
|
||||||
|
cp.split.nearPlane = ci;
|
||||||
|
osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci;
|
||||||
|
osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix;
|
||||||
|
cp.split.r_start = lightSpacePos.y();
|
||||||
|
}
|
||||||
|
if (sm_i + 1 == numShadowMapsPerLight)
|
||||||
|
{
|
||||||
|
cp.split.r_end = 1.0;
|
||||||
|
cp.split.farPlane = f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const double ciLog = n * pow(f / n, (i + 1) / m);
|
||||||
|
const double ciUniform = n + (f - n) * (i + 1) / m;
|
||||||
|
const double ci = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;
|
||||||
|
cp.split.farPlane = ci;
|
||||||
|
osg::Vec3d worldSpacePos = frustum.eye + frustum.frustumCenterLine * ci;
|
||||||
|
osg::Vec3d lightSpacePos = worldSpacePos * viewMatrix * projectionMatrix;
|
||||||
|
cp.split.r_end = lightSpacePos.y();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parallel-split planes and projection tweak (if needed)
|
||||||
|
if (isParallelSplit)
|
||||||
|
{
|
||||||
|
double r_start = cp.split.r_start;
|
||||||
|
double r_end = cp.split.r_end;
|
||||||
|
// Overlap bias is applied later with motion context, so only base planes here
|
||||||
|
if (sm_i>0)
|
||||||
|
{
|
||||||
|
osg::Plane plane(0.0,1.0,0.0,-r_start);
|
||||||
|
osg::Plane tplane = plane; tplane.transformProvidingInverse(projectionMatrix);
|
||||||
|
cp.extraPlanes.push_back(tplane);
|
||||||
|
}
|
||||||
|
if (sm_i+1<numShadowMapsPerLight)
|
||||||
|
{
|
||||||
|
osg::Plane plane(0.0,-1.0,0.0,r_end);
|
||||||
|
osg::Plane tplane = plane; tplane.transformProvidingInverse(projectionMatrix);
|
||||||
|
cp.extraPlanes.push_back(tplane);
|
||||||
|
}
|
||||||
|
cp.planesPushed = !cp.extraPlanes.empty();
|
||||||
|
if (cp.planesPushed)
|
||||||
|
{
|
||||||
|
// mid/range scaling matrix for projection tweak
|
||||||
|
const double mid_r = (r_start + r_end)*0.5;
|
||||||
|
const double range_r = (r_end - r_start);
|
||||||
|
cp.proj = osg::Matrixd::translate(osg::Vec3d(0.0,-mid_r,0.0)) *
|
||||||
|
osg::Matrixd::scale(osg::Vec3d(1.0,2.0/range_r,1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cascadePreps[sm_i] = std::move(cp);
|
||||||
|
};
|
||||||
|
for (unsigned int sm_i = 0; sm_i < numShadowMapsPerLight; ++sm_i)
|
||||||
|
{
|
||||||
|
if (mParallelShadowCullingEnabled)
|
||||||
|
preTasks.emplace_back(std::async(std::launch::async, doPrep, sm_i));
|
||||||
|
else
|
||||||
|
doPrep(sm_i);
|
||||||
|
}
|
||||||
|
for (auto& t : preTasks) t.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. For each light/shadow map
|
||||||
|
struct CascadeCullResult { osg::ref_ptr<ShadowData> sd; osg::ref_ptr<osg::Camera> camera; osg::ref_ptr<VDSMCameraCullCallback> cb; unsigned int sm_i = 0; bool rendered = false; double rStart = std::numeric_limits<double>::quiet_NaN(); double rEnd = std::numeric_limits<double>::quiet_NaN(); };
|
||||||
|
// Experimental parallel traversal: enabled only when both flags are on
|
||||||
|
const bool useParallelCull = (mParallelShadowTraversalEnabled && mParallelShadowCullingEnabled);
|
||||||
|
std::vector<std::future<CascadeCullResult>> parallelCullTasks;
|
||||||
|
// Note: we used to clone the main CullVisitor as a template, but we now construct
|
||||||
|
// a fresh CullVisitor per task and seed it explicitly to avoid inheriting unsafe state.
|
||||||
|
std::vector<CascadeCullResult> serialResults;
|
||||||
|
serialResults.reserve(numShadowMapsPerLight);
|
||||||
for (unsigned int sm_i=0; sm_i<numShadowMapsPerLight; ++sm_i)
|
for (unsigned int sm_i=0; sm_i<numShadowMapsPerLight; ++sm_i)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<ShadowData> sd;
|
osg::ref_ptr<ShadowData> sd;
|
||||||
@ -1367,167 +1478,196 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
camera->setProjectionMatrix(projectionMatrix);
|
camera->setProjectionMatrix(projectionMatrix);
|
||||||
camera->setViewMatrix(viewMatrix);
|
camera->setViewMatrix(viewMatrix);
|
||||||
|
|
||||||
if (settings->getDebugDraw())
|
if (debugDraw)
|
||||||
{
|
{
|
||||||
camera->getViewport()->x() = pos_x;
|
camera->getViewport()->x() = pos_x;
|
||||||
pos_x += static_cast<unsigned int>(camera->getViewport()->width()) + 40;
|
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);
|
|
||||||
|
|
||||||
double cascaseNear = reducedNear;
|
double cascaseNear = reducedNear;
|
||||||
double cascadeFar = reducedFar;
|
double cascadeFar = reducedFar;
|
||||||
|
// For skip-on-unchanged heuristic
|
||||||
|
double computedRStart = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
double computedREnd = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
|
||||||
|
// Local polytope used by both PARALLEL_SPLIT plane clipping and CASCADED cropping
|
||||||
|
osg::Polytope local_polytope;
|
||||||
|
|
||||||
|
// Start from precomputed base local polytope (light space eye coords)
|
||||||
|
local_polytope = baseLocalPolytope;
|
||||||
|
|
||||||
|
// compute the start and end range in non-dimensional coords if using multiple shadow maps
|
||||||
if (numShadowMapsPerLight>1)
|
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;
|
|
||||||
#else
|
|
||||||
double r_start, r_end;
|
double r_start, r_end;
|
||||||
|
if (!cascadePreps.empty())
|
||||||
// split system based on the original Parallel Split Shadow Maps paper.
|
{
|
||||||
double n = reducedNear;
|
cascaseNear = cascadePreps[sm_i].split.nearPlane;
|
||||||
double f = reducedFar;
|
cascadeFar = cascadePreps[sm_i].split.farPlane;
|
||||||
double i = double(sm_i);
|
r_start = cascadePreps[sm_i].split.r_start;
|
||||||
double m = double(numShadowMapsPerLight);
|
r_end = cascadePreps[sm_i].split.r_end;
|
||||||
if (sm_i == 0)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback to scalar path if no splits
|
||||||
r_start = -1.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 = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;
|
|
||||||
cascaseNear = ci;
|
|
||||||
|
|
||||||
// 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;
|
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 = _splitPointUniformLogRatio * ciLog + (1.0 - _splitPointUniformLogRatio) * ciUniform + _splitPointDeltaBias;
|
|
||||||
cascadeFar = ci;
|
|
||||||
|
|
||||||
// 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 but the last shadowmap shift the r_end so that it overlaps with the next shadowmap
|
||||||
// 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
|
// to prevent a seam showing through between the shadowmaps
|
||||||
if (sm_i+1<numShadowMapsPerLight) r_end+=0.01;
|
double cascadeOverlapBias = 0.01; // fixed overlap bias
|
||||||
|
if (sm_i + 1 < numShadowMapsPerLight) r_end += cascadeOverlapBias;
|
||||||
|
|
||||||
|
// Record signature
|
||||||
|
computedRStart = r_start;
|
||||||
|
computedREnd = r_end;
|
||||||
|
|
||||||
// We can't add these clipping planes with cascaded shadow maps as they wouldn't be parallel to the light direction.
|
// We can't add these clipping planes with cascaded shadow maps as they wouldn't be parallel to the light direction.
|
||||||
|
bool planesPushed = false;
|
||||||
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i>0)
|
if (isParallelSplit)
|
||||||
{
|
{
|
||||||
// not the first shadowmap so insert a polytope to clip the scene from before r_start
|
// Base planes from prepass; apply overlap bias now by tweaking r_end plane
|
||||||
|
if (!cascadePreps.empty())
|
||||||
// plane in clip space coords
|
{
|
||||||
osg::Plane plane(0.0,1.0,0.0,-r_start);
|
// Recreate planes with current r_start/r_end (already have base transformed planes, but we must account for bias)
|
||||||
|
if (sm_i>0)
|
||||||
// transform into eye coords
|
{
|
||||||
plane.transformProvidingInverse(projectionMatrix);
|
osg::Plane plane(0.0,1.0,0.0,-r_start);
|
||||||
local_polytope.getPlaneList().push_back(plane);
|
plane.transformProvidingInverse(projectionMatrix);
|
||||||
|
local_polytope.getPlaneList().push_back(plane);
|
||||||
//OSG_NOTICE<<"Adding r_start plane "<<plane<<std::endl;
|
planesPushed = true;
|
||||||
|
}
|
||||||
}
|
if (sm_i+1<numShadowMapsPerLight)
|
||||||
|
{
|
||||||
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT && sm_i+1<numShadowMapsPerLight)
|
osg::Plane plane(0.0,-1.0,0.0,r_end);
|
||||||
{
|
plane.transformProvidingInverse(projectionMatrix);
|
||||||
// not the last shadowmap so insert a polytope to clip the scene from beyond r_end
|
local_polytope.getPlaneList().push_back(plane);
|
||||||
|
planesPushed = true;
|
||||||
// plane in clip space coords
|
}
|
||||||
osg::Plane plane(0.0,-1.0,0.0,r_end);
|
if (planesPushed)
|
||||||
|
local_polytope.setupMask();
|
||||||
// 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();
|
|
||||||
|
|
||||||
if (settings->getMultipleShadowMapHint() == ShadowSettings::PARALLEL_SPLIT)
|
|
||||||
{
|
|
||||||
// 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)));
|
|
||||||
|
|
||||||
|
// Apply projection tweak from prepass (based on base mid/range)
|
||||||
|
if (cascadePreps[sm_i].planesPushed)
|
||||||
|
{
|
||||||
|
camera->setProjectionMatrix(camera->getProjectionMatrix() * cascadePreps[sm_i].proj);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<VDSMCameraCullCallback> vdsmCallback;
|
||||||
std::vector<osg::Plane> extraPlanes;
|
std::vector<osg::Plane> extraPlanes;
|
||||||
if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED)
|
extraPlanes.reserve(6);
|
||||||
|
bool planesPushed = false;
|
||||||
|
if (isCascaded)
|
||||||
{
|
{
|
||||||
cropShadowCameraToMainFrustum(frustum, camera, cascaseNear, cascadeFar, extraPlanes);
|
cropShadowCameraToMainFrustum(frustum, camera, cascaseNear, cascadeFar, extraPlanes);
|
||||||
for (const auto& plane : extraPlanes)
|
for (const auto& plane : extraPlanes)
|
||||||
local_polytope.getPlaneList().push_back(plane);
|
local_polytope.getPlaneList().push_back(plane);
|
||||||
local_polytope.setupMask();
|
planesPushed = !extraPlanes.empty();
|
||||||
|
if (planesPushed)
|
||||||
|
local_polytope.setupMask();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
cropShadowCameraToMainFrustum(frustum, camera, reducedNear, reducedFar, extraPlanes);
|
cropShadowCameraToMainFrustum(frustum, camera, reducedNear, reducedFar, extraPlanes);
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<VDSMCameraCullCallback> vdsmCallback = new VDSMCameraCullCallback(this, local_polytope);
|
// Texel snapping for the nearest cascade only (directional light), to stabilize during motion
|
||||||
|
if (sm_i == 0 && pl.directionalLight)
|
||||||
|
{
|
||||||
|
const osg::Viewport* vp = camera->getViewport();
|
||||||
|
const double texW = vp ? static_cast<double>(vp->width()) : static_cast<double>(settings->getTextureSize().x());
|
||||||
|
const double texH = vp ? static_cast<double>(vp->height()) : static_cast<double>(settings->getTextureSize().y());
|
||||||
|
if (texW > 0.0 && texH > 0.0)
|
||||||
|
{
|
||||||
|
osg::Vec3d clipCenter = frustum.center * camera->getViewMatrix() * camera->getProjectionMatrix();
|
||||||
|
const double ndcX = clipCenter.x();
|
||||||
|
const double ndcY = clipCenter.y();
|
||||||
|
const double u = (ndcX * 0.5 + 0.5) * texW;
|
||||||
|
const double v = (ndcY * 0.5 + 0.5) * texH;
|
||||||
|
const double uSnap = std::round(u);
|
||||||
|
const double vSnap = std::round(v);
|
||||||
|
const double du = (uSnap - u) / texW; // in [0..1]
|
||||||
|
const double dv = (vSnap - v) / texH;
|
||||||
|
const double ndcOffX = du * 2.0;
|
||||||
|
const double ndcOffY = dv * 2.0;
|
||||||
|
if (std::fabs(ndcOffX) > 0.0 || std::fabs(ndcOffY) > 0.0)
|
||||||
|
{
|
||||||
|
camera->setProjectionMatrix(
|
||||||
|
camera->getProjectionMatrix() *
|
||||||
|
osg::Matrixd::translate(osg::Vec3d(ndcOffX, ndcOffY, 0.0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure shadows bin StateSet exists before any parallel tasks to avoid races.
|
||||||
|
(void)getOrCreateShadowsBinStateSet();
|
||||||
|
|
||||||
|
// Use the camera's cull callback to set up the render stage, push casting/bin state, and apply the custom polytope.
|
||||||
|
vdsmCallback = new VDSMCameraCullCallback(this, local_polytope);
|
||||||
camera->setCullCallback(vdsmCallback.get());
|
camera->setCullCallback(vdsmCallback.get());
|
||||||
|
|
||||||
// 4.3 traverse RTT camera
|
// 4.3 traverse RTT camera
|
||||||
//
|
//
|
||||||
|
|
||||||
cv.pushStateSet(_shadowCastingStateSet.get());
|
// If full update frame and stationary, allow skipping deeper cascades whose light-space coverage is unchanged
|
||||||
|
if (!useParallelCull)
|
||||||
|
{
|
||||||
|
// Serial traversal on the main CullVisitor.
|
||||||
|
cv.pushStateSet(_shadowCastingStateSet.get());
|
||||||
|
cullShadowCastingScene(&cv, camera.get());
|
||||||
|
cv.popStateSet();
|
||||||
|
CascadeCullResult res; res.sd = sd; res.camera = camera; res.cb = vdsmCallback; res.sm_i = sm_i; res.rendered = true; res.rStart = computedRStart; res.rEnd = computedREnd;
|
||||||
|
serialResults.push_back(std::move(res));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parallel traversal using a private CullVisitor and render graph per cascade.
|
||||||
|
osg::ref_ptr<osg::Camera> taskCamera = camera;
|
||||||
|
osg::ref_ptr<VDSMCameraCullCallback> taskCb = vdsmCallback;
|
||||||
|
osg::ref_ptr<ShadowData> taskSd = sd;
|
||||||
|
const unsigned int castsMask = (_shadowedScene->getShadowSettings()->getCastsShadowTraversalMask() & _worldMask);
|
||||||
|
const auto mainComputeNF = cv.getComputeNearFarMode();
|
||||||
|
const auto mainTraversalNumber = cv.getTraversalNumber();
|
||||||
|
osg::ref_ptr<const osg::FrameStamp> mainFrameStamp = cv.getFrameStamp();
|
||||||
|
|
||||||
cullShadowCastingScene(&cv, camera.get());
|
parallelCullTasks.emplace_back(std::async(std::launch::async, [taskCamera, taskCb, taskSd, castsMask, mainComputeNF, mainTraversalNumber, mainFrameStamp, sm_i, computedRStart, computedREnd]() mutable {
|
||||||
|
CascadeCullResult res; res.sd = taskSd; res.camera = taskCamera; res.cb = taskCb; res.sm_i = sm_i; res.rendered = false; res.rStart = computedRStart; res.rEnd = computedREnd;
|
||||||
|
osg::ref_ptr<osgUtil::CullVisitor> localCv = new osgUtil::CullVisitor();
|
||||||
|
localCv->reset();
|
||||||
|
localCv->setTraversalMask(castsMask);
|
||||||
|
localCv->setComputeNearFarMode(mainComputeNF);
|
||||||
|
localCv->setTraversalNumber(mainTraversalNumber);
|
||||||
|
if (mainFrameStamp.valid())
|
||||||
|
localCv->setFrameStamp(const_cast<osg::FrameStamp*>(mainFrameStamp.get()));
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osgUtil::StateGraph> rootGraph = new osgUtil::StateGraph();
|
||||||
|
localCv->setStateGraph(rootGraph.get());
|
||||||
|
osg::ref_ptr<osgUtil::RenderStage> rootStage = new osgUtil::RenderStage();
|
||||||
|
rootStage->setViewport(taskCamera->getViewport());
|
||||||
|
rootStage->setInitialViewMatrix(new osg::RefMatrix(taskCamera->getViewMatrix()));
|
||||||
|
localCv->setCurrentRenderBin(rootStage.get());
|
||||||
|
}
|
||||||
|
taskCamera->accept(*localCv);
|
||||||
|
res.rendered = true;
|
||||||
|
return res;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
cv.popStateSet();
|
if (!useParallelCull && !orthographicViewFrustum && settings->getShadowMapProjectionHint()==ShadowSettings::PERSPECTIVE_SHADOW_MAP)
|
||||||
|
|
||||||
if (!orthographicViewFrustum && settings->getShadowMapProjectionHint()==ShadowSettings::PERSPECTIVE_SHADOW_MAP)
|
|
||||||
{
|
{
|
||||||
assignValidRegionSettings(cv, camera, sm_i, vddUniforms);
|
assignValidRegionSettings(cv, camera, sm_i, vddUniforms);
|
||||||
|
const double adjustNear = isCascaded ? cascaseNear : reducedNear;
|
||||||
if (settings->getMultipleShadowMapHint() == ShadowSettings::CASCADED)
|
const double adjustFar = isCascaded ? cascadeFar : reducedFar;
|
||||||
adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), cascaseNear, cascadeFar);
|
adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), adjustNear, adjustFar);
|
||||||
else
|
|
||||||
adjustPerspectiveShadowMapCameraSettings(vdsmCallback->getRenderStage(), frustum, pl, camera.get(), reducedNear, reducedFar);
|
|
||||||
if (vdsmCallback->getProjectionMatrix())
|
if (vdsmCallback->getProjectionMatrix())
|
||||||
{
|
|
||||||
vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());
|
vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.4 compute main scene graph TexGen + uniform settings + setup state
|
// 4.4 compute main scene graph TexGen + uniform settings + setup state
|
||||||
//
|
//
|
||||||
|
if (!useParallelCull)
|
||||||
{
|
{
|
||||||
assignShadowStateSettings(cv, camera, sm_i, vddUniforms);
|
assignShadowStateSettings(cv, camera, sm_i, vddUniforms);
|
||||||
}
|
}
|
||||||
@ -1545,9 +1685,55 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||||||
++textureUnit;
|
++textureUnit;
|
||||||
++numValidShadows ;
|
++numValidShadows ;
|
||||||
|
|
||||||
if (_debugHud)
|
if (!useParallelCull && _debugHud)
|
||||||
|
{
|
||||||
_debugHud->draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv);
|
_debugHud->draw(sd->_texture, sm_i, camera->getViewMatrix() * camera->getProjectionMatrix(), cv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parallel traversal join and finalization
|
||||||
|
if (useParallelCull)
|
||||||
|
{
|
||||||
|
for (auto& fut : parallelCullTasks)
|
||||||
|
{
|
||||||
|
CascadeCullResult res = fut.get();
|
||||||
|
if (!res.rendered)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
// Finalize perspective shadow map adjustments if needed.
|
||||||
|
if (!orthographicViewFrustum && settings->getShadowMapProjectionHint()==ShadowSettings::PERSPECTIVE_SHADOW_MAP)
|
||||||
|
{
|
||||||
|
double useNear = reducedNear;
|
||||||
|
double useFar = reducedFar;
|
||||||
|
if (isCascaded && res.sm_i < cascadePreps.size())
|
||||||
|
{
|
||||||
|
useNear = cascadePreps[res.sm_i].split.nearPlane;
|
||||||
|
useFar = cascadePreps[res.sm_i].split.farPlane;
|
||||||
|
}
|
||||||
|
assignValidRegionSettings(cv, res.camera, res.sm_i, vddUniforms);
|
||||||
|
adjustPerspectiveShadowMapCameraSettings(res.cb->getRenderStage(), frustum, pl, res.camera.get(), useNear, useFar);
|
||||||
|
if (res.cb->getProjectionMatrix())
|
||||||
|
res.cb->getProjectionMatrix()->set(res.camera->getProjectionMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign main-scene shadow uniforms now.
|
||||||
|
assignShadowStateSettings(cv, res.camera, res.sm_i, vddUniforms);
|
||||||
|
|
||||||
|
// Schedule the actual shadow RTT render on the main CullVisitor so draw traversal executes it.
|
||||||
|
// The worker cull built an isolated render graph not used by the main SceneView.
|
||||||
|
// By culling the camera here, we link its pre-render stage into the main render graph for this frame.
|
||||||
|
if (_shadowCastingStateSet.valid())
|
||||||
|
{
|
||||||
|
cv.pushStateSet(_shadowCastingStateSet.get());
|
||||||
|
cullShadowCastingScene(&cv, res.camera.get());
|
||||||
|
cv.popStateSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_debugHud)
|
||||||
|
_debugHud->draw(res.sd-> _texture, res.sm_i, res.camera->getViewMatrix() * res.camera->getProjectionMatrix(), cv);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vdd->setNumValidShadows(numValidShadows);
|
vdd->setNumValidShadows(numValidShadows);
|
||||||
@ -1683,7 +1869,7 @@ void MWShadowTechnique::createShaders()
|
|||||||
{
|
{
|
||||||
case(ShadowSettings::NO_SHADERS):
|
case(ShadowSettings::NO_SHADERS):
|
||||||
{
|
{
|
||||||
OSG_INFO<<"No shaders provided by, user must supply own shaders"<<std::endl;
|
OSG_WARN<<"No shaders provided; user must supply own shaders"<<std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case(ShadowSettings::PROVIDE_VERTEX_AND_FRAGMENT_SHADER):
|
case(ShadowSettings::PROVIDE_VERTEX_AND_FRAGMENT_SHADER):
|
||||||
@ -1784,9 +1970,6 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OSG_INFO<<" planes.size() = "<<planes.size()<<std::endl;
|
|
||||||
OSG_INFO<<" planes.getResultMask() = "<<polytope.getResultMask()<<std::endl;
|
|
||||||
OSG_INFO<<" resultMask = "<<result_mask<<std::endl;
|
|
||||||
polytope.setResultMask(result_mask);
|
polytope.setResultMask(result_mask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1799,6 +1982,7 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
|||||||
|
|
||||||
double d = planes[i].distance(positionedLight.lightPos3);
|
double d = planes[i].distance(positionedLight.lightPos3);
|
||||||
OSG_INFO<<" plane "<<planes[i]<<" planes["<<i<<"].distance(lightPos3)="<<d;
|
OSG_INFO<<" plane "<<planes[i]<<" planes["<<i<<"].distance(lightPos3)="<<d;
|
||||||
|
|
||||||
if (d<0.0)
|
if (d<0.0)
|
||||||
{
|
{
|
||||||
OSG_INFO<<" Need remove side "<<i<<std::endl;
|
OSG_INFO<<" Need remove side "<<i<<std::endl;
|
||||||
@ -1810,6 +1994,7 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
|||||||
result_mask = result_mask | selector_mask;
|
result_mask = result_mask | selector_mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OSG_INFO<<" planes.size() = "<<planes.size()<<std::endl;
|
OSG_INFO<<" planes.size() = "<<planes.size()<<std::endl;
|
||||||
OSG_INFO<<" planes.getResultMask() = "<<polytope.getResultMask()<<std::endl;
|
OSG_INFO<<" planes.getResultMask() = "<<polytope.getResultMask()<<std::endl;
|
||||||
OSG_INFO<<" resultMask = "<<result_mask<<std::endl;
|
OSG_INFO<<" resultMask = "<<result_mask<<std::endl;
|
||||||
@ -1865,7 +2050,7 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
|||||||
const osg::Polytope::PlaneList& planes = lightVolumePolytope.getPlaneList();
|
const osg::Polytope::PlaneList& planes = lightVolumePolytope.getPlaneList();
|
||||||
for(unsigned int i=0; i<planes.size(); ++i)
|
for(unsigned int i=0; i<planes.size(); ++i)
|
||||||
{
|
{
|
||||||
OSG_INFO<<" plane "<<planes[i]<<" "<<((lightVolumePolytope.getResultMask() & (0x1<<i))?"on":"off")<<std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lightVolumePolytope;
|
return lightVolumePolytope;
|
||||||
@ -1963,6 +2148,7 @@ bool MWShadowTechnique::computeShadowCameraSettings(Frustum& frustum, LightData&
|
|||||||
{
|
{
|
||||||
double zMax=-dbl_max;
|
double zMax=-dbl_max;
|
||||||
|
|
||||||
|
|
||||||
OSG_INFO<<"lightDir = "<<positionedLight.lightDir<<std::endl;
|
OSG_INFO<<"lightDir = "<<positionedLight.lightDir<<std::endl;
|
||||||
OSG_INFO<<"lightPos3 = "<<positionedLight.lightPos3<<std::endl;
|
OSG_INFO<<"lightPos3 = "<<positionedLight.lightPos3<<std::endl;
|
||||||
for(Frustum::Vertices::iterator itr = frustum.corners.begin();
|
for(Frustum::Vertices::iterator itr = frustum.corners.begin();
|
||||||
@ -2912,7 +3098,7 @@ bool MWShadowTechnique::adjustPerspectiveShadowMapCameraSettings(osgUtil::Render
|
|||||||
#if 0
|
#if 0
|
||||||
if (eye_ls.y()>-1.0)
|
if (eye_ls.y()>-1.0)
|
||||||
{
|
{
|
||||||
OSG_NOTICE<<"Eye point within light space - use standard shadow map."<<std::endl;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
@ -90,6 +91,11 @@ namespace SceneUtil {
|
|||||||
|
|
||||||
virtual void setupCastingShader(Shader::ShaderManager &shaderManager);
|
virtual void setupCastingShader(Shader::ShaderManager &shaderManager);
|
||||||
|
|
||||||
|
// Experimental: enable parallel shadow preparation/culling
|
||||||
|
void setParallelShadowCullingEnabled(bool enabled) { mParallelShadowCullingEnabled = enabled; }
|
||||||
|
// Experimental: enable parallel per-cascade RTT traversal
|
||||||
|
void setParallelShadowTraversalEnabled(bool enabled) { mParallelShadowTraversalEnabled = enabled; }
|
||||||
|
|
||||||
class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack
|
class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -272,6 +278,7 @@ namespace SceneUtil {
|
|||||||
void setWorldMask(unsigned int worldMask) { _worldMask = worldMask; }
|
void setWorldMask(unsigned int worldMask) { _worldMask = worldMask; }
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> getOrCreateShadowsBinStateSet();
|
osg::ref_ptr<osg::StateSet> getOrCreateShadowsBinStateSet();
|
||||||
|
osg::StateSet* getCastingStateSet() const { return _shadowCastingStateSet.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~MWShadowTechnique();
|
virtual ~MWShadowTechnique();
|
||||||
@ -309,6 +316,9 @@ namespace SceneUtil {
|
|||||||
|
|
||||||
unsigned int _worldMask = ~0u;
|
unsigned int _worldMask = ~0u;
|
||||||
|
|
||||||
|
bool mParallelShadowCullingEnabled = false;
|
||||||
|
bool mParallelShadowTraversalEnabled = false;
|
||||||
|
|
||||||
class DebugHUD final : public osg::Referenced
|
class DebugHUD final : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -80,6 +80,9 @@ namespace SceneUtil
|
|||||||
mShadowTechnique->enableDebugHUD();
|
mShadowTechnique->enableDebugHUD();
|
||||||
else
|
else
|
||||||
mShadowTechnique->disableDebugHUD();
|
mShadowTechnique->disableDebugHUD();
|
||||||
|
|
||||||
|
mShadowTechnique->setParallelShadowCullingEnabled(settings.mParallelShadowCulling);
|
||||||
|
mShadowTechnique->setParallelShadowTraversalEnabled(settings.mParallelShadowTraversal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShadowManager::disableShadowsForStateSet(osg::StateSet& stateset) const
|
void ShadowManager::disableShadowsForStateSet(osg::StateSet& stateset) const
|
||||||
|
@ -42,6 +42,8 @@ namespace Settings
|
|||||||
SettingValue<bool> mTerrainShadows{ mIndex, "Shadows", "terrain shadows" };
|
SettingValue<bool> mTerrainShadows{ mIndex, "Shadows", "terrain shadows" };
|
||||||
SettingValue<bool> mObjectShadows{ mIndex, "Shadows", "object shadows" };
|
SettingValue<bool> mObjectShadows{ mIndex, "Shadows", "object shadows" };
|
||||||
SettingValue<bool> mEnableIndoorShadows{ mIndex, "Shadows", "enable indoor shadows" };
|
SettingValue<bool> mEnableIndoorShadows{ mIndex, "Shadows", "enable indoor shadows" };
|
||||||
|
SettingValue<bool> mParallelShadowCulling{ mIndex, "Shadows", "parallel shadow culling" };
|
||||||
|
SettingValue<bool> mParallelShadowTraversal{ mIndex, "Shadows", "parallel shadow traversal" };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,6 +1021,12 @@ wait for all jobs on exit = false
|
|||||||
# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.
|
# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.
|
||||||
enable shadows = false
|
enable shadows = false
|
||||||
|
|
||||||
|
# compute per-cascade prep and cull RTT cameras in parallel
|
||||||
|
parallel shadow culling = false
|
||||||
|
|
||||||
|
# compute per-cascade RTT traversal in parallel
|
||||||
|
parallel shadow traversal = false
|
||||||
|
|
||||||
# How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance.
|
# How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance.
|
||||||
number of shadow maps = 3
|
number of shadow maps = 3
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user