mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 15:03:53 +02:00
Non shadow casters now increase shadow scene bounds only in light direction
This commit is contained in:
parent
3f3f541a20
commit
30600dff93
5 changed files with 179 additions and 65 deletions
|
@ -101,7 +101,7 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
|
||||
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withoutShadowCaster().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
|
||||
}
|
||||
|
||||
render::ItemKey GameWorkloadRenderItem::getKey() const {
|
||||
|
|
|
@ -264,22 +264,21 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
char jobName[64];
|
||||
sprintf(jobName, "ShadowCascadeSetup%d", i);
|
||||
const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i, _cullFunctor, tagBits, tagMask);
|
||||
const auto shadowRenderFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
const auto shadowBoundsFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
auto antiFrustum = render::Varying(ViewFrustumPointer());
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(2);
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
if (i > 1) {
|
||||
antiFrustum = cascadeFrustums[i - 2];
|
||||
}
|
||||
|
||||
// CPU jobs: finer grained culling
|
||||
const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying();
|
||||
const auto culledShadowItemsAndBounds = task.addJob<CullShapeBounds>("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
|
||||
const auto cullInputs = CullShadowBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
|
||||
const auto culledShadowItemsAndBounds = task.addJob<CullShadowBounds>("CullShadowCascade", cullInputs, shadowCullFunctor);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
sprintf(jobName, "RenderShadowMap%d", i);
|
||||
task.addJob<RenderShadowMap>(jobName, culledShadowItemsAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowRenderFilter);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowFilter);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
|
||||
|
@ -412,11 +411,8 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||
auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
// Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers)
|
||||
output.edit1() = baseFilter;
|
||||
// First item filter is to filter items to render in shadow map (so only keep casters)
|
||||
output.edit0() = baseFilter.withShadowCaster();
|
||||
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
|
||||
// Set the keylight render args
|
||||
auto& cascade = globalShadow->getCascade(_cascadeIndex);
|
||||
|
@ -429,11 +425,10 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
texelSize *= minTexelCount;
|
||||
_cullFunctor._minSquareSize = texelSize * texelSize;
|
||||
|
||||
output.edit2() = cascadeFrustum;
|
||||
output.edit1() = cascadeFrustum;
|
||||
} else {
|
||||
output.edit0() = ItemFilter::Builder::nothing();
|
||||
output.edit1() = ItemFilter::Builder::nothing();
|
||||
output.edit2() = ViewFrustumPointer();
|
||||
output.edit1() = ViewFrustumPointer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,3 +451,98 @@ void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext
|
|||
// Reset the render args
|
||||
args->_renderMode = input.get0();
|
||||
}
|
||||
|
||||
static AABox& merge(AABox& box, const AABox& otherBox, const glm::vec3& dir) {
|
||||
if (!otherBox.isInvalid()) {
|
||||
int vertexIndex = 0;
|
||||
vertexIndex |= ((dir.z > 0.0f) & 1) << 2;
|
||||
vertexIndex |= ((dir.y > 0.0f) & 1) << 1;
|
||||
vertexIndex |= ((dir.x < 0.0f) & 1);
|
||||
auto vertex = otherBox.getVertex((BoxVertex)vertexIndex);
|
||||
if (!box.isInvalid()) {
|
||||
const auto boxCenter = box.calcCenter();
|
||||
vertex -= boxCenter;
|
||||
vertex = dir * glm::max(0.0f, glm::dot(vertex, dir));
|
||||
vertex += boxCenter;
|
||||
}
|
||||
box += vertex;
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
void CullShadowBounds::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto& inShapes = inputs.get0();
|
||||
const auto& filter = inputs.get1();
|
||||
ViewFrustumPointer antiFrustum;
|
||||
auto& outShapes = outputs.edit0();
|
||||
auto& outBounds = outputs.edit1();
|
||||
|
||||
if (!inputs[3].isNull()) {
|
||||
antiFrustum = inputs.get2();
|
||||
}
|
||||
outShapes.clear();
|
||||
outBounds = AABox();
|
||||
|
||||
if (!filter.selectsNothing()) {
|
||||
auto& details = args->_details.edit(RenderDetails::SHADOW);
|
||||
render::CullTest test(_cullFunctor, args, details, antiFrustum);
|
||||
auto scene = args->_scene;
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
const auto globalLightDir = lightStage->getCurrentKeyLight()->getDirection();
|
||||
auto castersFilter = render::ItemFilter::Builder(filter).withShadowCaster().build();
|
||||
const auto& receiversFilter = filter;
|
||||
|
||||
for (auto& inItems : inShapes) {
|
||||
auto key = inItems.first;
|
||||
auto outItems = outShapes.find(key);
|
||||
if (outItems == outShapes.end()) {
|
||||
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
|
||||
outItems->second.reserve(inItems.second.size());
|
||||
}
|
||||
|
||||
details._considered += (int)inItems.second.size();
|
||||
|
||||
if (antiFrustum == nullptr) {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (castersFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
} else if (receiversFilter.test(shapeKey)) {
|
||||
// Receivers are not rendered but they still increase the bounds of the shadow scene
|
||||
// although only in the direction of the light direction so as to have a correct far
|
||||
// distance without decreasing the near distance.
|
||||
merge(outBounds, item.bound, globalLightDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (castersFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
} else if (receiversFilter.test(shapeKey)) {
|
||||
// Receivers are not rendered but they still increase the bounds of the shadow scene
|
||||
// although only in the direction of the light direction so as to have a correct far
|
||||
// distance without decreasing the near distance.
|
||||
merge(outBounds, item.bound, globalLightDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
details._rendered += (int)outItems->second.size();
|
||||
}
|
||||
|
||||
for (auto& items : outShapes) {
|
||||
items.second.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ private:
|
|||
|
||||
class RenderShadowCascadeSetup {
|
||||
public:
|
||||
using Outputs = render::VaryingSet3<render::ItemFilter, render::ItemFilter, ViewFrustumPointer>;
|
||||
using Outputs = render::VaryingSet2<render::ItemFilter, ViewFrustumPointer>;
|
||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
||||
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
|
||||
|
@ -147,4 +147,22 @@ public:
|
|||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
class CullShadowBounds {
|
||||
public:
|
||||
using Inputs = render::VaryingSet3<render::ShapeBounds, render::ItemFilter, ViewFrustumPointer>;
|
||||
using Outputs = render::VaryingSet2<render::ShapeBounds, AABox>;
|
||||
using JobModel = render::Job::ModelIO<CullShadowBounds, Inputs, Outputs>;
|
||||
|
||||
CullShadowBounds(render::CullFunctor cullFunctor) :
|
||||
_cullFunctor{ cullFunctor } {
|
||||
}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
render::CullFunctor _cullFunctor;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_RenderShadowTask_h
|
||||
|
|
|
@ -19,60 +19,50 @@
|
|||
|
||||
using namespace render;
|
||||
|
||||
// Culling Frustum / solidAngle test helper class
|
||||
struct Test {
|
||||
CullFunctor _functor;
|
||||
RenderArgs* _args;
|
||||
RenderDetails::Item& _renderDetails;
|
||||
ViewFrustumPointer _antiFrustum;
|
||||
glm::vec3 _eyePos;
|
||||
float _squareTanAlpha;
|
||||
CullTest::CullTest(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum) :
|
||||
_functor(functor),
|
||||
_args(pargs),
|
||||
_renderDetails(renderDetails),
|
||||
_antiFrustum(antiFrustum) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
/*_eyePos = _args->getViewFrustum().getPosition();
|
||||
float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
|
||||
auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees
|
||||
angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree
|
||||
auto tanAlpha = tan(angle);
|
||||
_squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
*/
|
||||
}
|
||||
|
||||
Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum = nullptr) :
|
||||
_functor(functor),
|
||||
_args(pargs),
|
||||
_renderDetails(renderDetails),
|
||||
_antiFrustum(antiFrustum) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
/*_eyePos = _args->getViewFrustum().getPosition();
|
||||
float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
|
||||
auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees
|
||||
angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree
|
||||
auto tanAlpha = tan(angle);
|
||||
_squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
*/
|
||||
bool CullTest::frustumTest(const AABox& bound) {
|
||||
if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool frustumTest(const AABox& bound) {
|
||||
if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
bool CullTest::antiFrustumTest(const AABox& bound) {
|
||||
assert(_antiFrustum);
|
||||
if (_antiFrustum->boxInsideFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool antiFrustumTest(const AABox& bound) {
|
||||
assert(_antiFrustum);
|
||||
if (_antiFrustum->boxInsideFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
bool CullTest::solidAngleTest(const AABox& bound) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
//auto eyeToPoint = bound.calcCenter() - _eyePos;
|
||||
//auto boundSize = bound.getDimensions();
|
||||
//float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
//if (test < 0.0f) {
|
||||
if (!_functor(_args, bound)) {
|
||||
_renderDetails._tooSmall++;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solidAngleTest(const AABox& bound) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
//auto eyeToPoint = bound.calcCenter() - _eyePos;
|
||||
//auto boundSize = bound.getDimensions();
|
||||
//float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
//if (test < 0.0f) {
|
||||
if (!_functor(_args, bound)) {
|
||||
_renderDetails._tooSmall++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
|
||||
const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
|
@ -205,7 +195,7 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
|||
args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one
|
||||
}
|
||||
|
||||
Test test(_cullFunctor, args, details);
|
||||
CullTest test(_cullFunctor, args, details);
|
||||
|
||||
// Now we have a selection of items to render
|
||||
outItems.clear();
|
||||
|
@ -382,7 +372,7 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
|
||||
if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
|
||||
auto& details = args->_details.edit(_detailType);
|
||||
Test test(_cullFunctor, args, details, antiFrustum);
|
||||
CullTest test(_cullFunctor, args, details, antiFrustum);
|
||||
auto scene = args->_scene;
|
||||
|
||||
for (auto& inItems : inShapes) {
|
||||
|
|
|
@ -22,6 +22,22 @@ namespace render {
|
|||
void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
|
||||
const ItemBounds& inItems, ItemBounds& outItems);
|
||||
|
||||
// Culling Frustum / solidAngle test helper class
|
||||
struct CullTest {
|
||||
CullFunctor _functor;
|
||||
RenderArgs* _args;
|
||||
RenderDetails::Item& _renderDetails;
|
||||
ViewFrustumPointer _antiFrustum;
|
||||
glm::vec3 _eyePos;
|
||||
float _squareTanAlpha;
|
||||
|
||||
CullTest(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum = nullptr);
|
||||
|
||||
bool frustumTest(const AABox& bound);
|
||||
bool antiFrustumTest(const AABox& bound);
|
||||
bool solidAngleTest(const AABox& bound);
|
||||
};
|
||||
|
||||
class FetchNonspatialItems {
|
||||
public:
|
||||
using JobModel = Job::ModelIO<FetchNonspatialItems, ItemFilter, ItemBounds>;
|
||||
|
|
Loading…
Reference in a new issue