diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index a8b65492d3..a825967da8 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -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 { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 327eac8867..c9171d7b69 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -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(jobName, i, _cullFunctor, tagBits, tagMask); - const auto shadowRenderFilter = cascadeSetupOutput.getN(0); - const auto shadowBoundsFilter = cascadeSetupOutput.getN(1); + const auto shadowFilter = cascadeSetupOutput.getN(0); auto antiFrustum = render::Varying(ViewFrustumPointer()); - cascadeFrustums[i] = cascadeSetupOutput.getN(2); + cascadeFrustums[i] = cascadeSetupOutput.getN(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("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW); + const auto cullInputs = CullShadowBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying(); + const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, shadowCullFunctor); // GPU jobs: Render to shadow map sprintf(jobName, "RenderShadowMap%d", i); task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i); - task.addJob("ShadowCascadeTeardown", shadowRenderFilter); + task.addJob("ShadowCascadeTeardown", shadowFilter); } task.addJob("ShadowTeardown", setupOutput); @@ -412,11 +411,8 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { - 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(); + 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(); + } + } +} diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 19ffcb4234..1309124f92 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -118,7 +118,7 @@ private: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet3; + using Outputs = render::VaryingSet2; using JobModel = render::Job::ModelO; 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; + using Outputs = render::VaryingSet2; + using JobModel = render::Job::ModelIO; + + 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 diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 8cfe7683ce..b5f1718f6c 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -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) { diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index 47abe8a960..2ef9e92eaa 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -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;