Shadow maps rendered in cascades

This commit is contained in:
Olivier Prat 2017-11-10 18:30:37 +01:00
parent 08b06281f4
commit 103e036b70
14 changed files with 226 additions and 178 deletions

View file

@ -116,7 +116,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
// Do we need to allocate the light in the stage ? // Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_sunIndex)) { if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight); _sunIndex = _stage->addLight(_sunLight);
_shadowIndex = _stage->addShadow(_sunIndex); _shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT);
} else { } else {
_stage->updateLightArrayBuffer(_sunIndex); _stage->updateLightArrayBuffer(_sunIndex);
} }

View file

@ -284,7 +284,10 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust
return DEFAULT_SCATTERING_SHADER; return DEFAULT_SCATTERING_SHADER;
case LightingMode: case LightingMode:
return DEFAULT_LIGHTING_SHADER; return DEFAULT_LIGHTING_SHADER;
case ShadowMode: case Shadow0Mode:
case Shadow1Mode:
case Shadow2Mode:
case Shadow3Mode:
return DEFAULT_SHADOW_SHADER; return DEFAULT_SHADOW_SHADER;
case LinearDepthMode: case LinearDepthMode:
return DEFAULT_LINEAR_DEPTH_SHADER; return DEFAULT_LINEAR_DEPTH_SHADER;
@ -438,7 +441,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow();
const auto& globalShadow = lightAndShadow.second; const auto& globalShadow = lightAndShadow.second;
if (globalShadow) { if (globalShadow) {
batch.setResourceTexture(Shadow, globalShadow->getCascade(0).map); const auto cascadeIndex = glm::clamp(_mode - Mode::Shadow0Mode, 0, (int)globalShadow->getCascadeCount() - 1);
batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map);
} }
if (linearDepthTarget) { if (linearDepthTarget) {

View file

@ -64,7 +64,10 @@ protected:
LightmapMode, LightmapMode,
ScatteringMode, ScatteringMode,
LightingMode, LightingMode,
ShadowMode, Shadow0Mode,
Shadow1Mode,
Shadow2Mode,
Shadow3Mode,
LinearDepthMode, LinearDepthMode,
HalfLinearDepthMode, HalfLinearDepthMode,
HalfNormalMode, HalfNormalMode,

View file

@ -19,6 +19,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 }; 0.5, 0.5, 0.5, 1.0 };
const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 };
LightStage::LightStage() { LightStage::LightStage() {
} }
@ -168,11 +169,11 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
} }
} }
LightStage::Index LightStage::addShadow(Index lightIndex) { LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) {
auto light = getLight(lightIndex); auto light = getLight(lightIndex);
Index shadowId = INVALID_INDEX; Index shadowId = INVALID_INDEX;
if (light) { if (light) {
shadowId = _shadows.newElement(std::make_shared<Shadow>(light)); shadowId = _shadows.newElement(std::make_shared<Shadow>(light, cascadeCount));
_descs[lightIndex].shadowId = shadowId; _descs[lightIndex].shadowId = shadowId;
} }
return shadowId; return shadowId;

View file

@ -31,6 +31,8 @@ public:
static std::string _stageName; static std::string _stageName;
static const std::string& getName() { return _stageName; } static const std::string& getName() { return _stageName; }
static const unsigned int SUN_SHADOW_CASCADE_COUNT;
using Index = render::indexed_container::Index; using Index = render::indexed_container::Index;
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX }; static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
@ -109,7 +111,7 @@ public:
Index findLight(const LightPointer& light) const; Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light); Index addLight(const LightPointer& light);
Index addShadow(Index lightIndex); Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U);
LightPointer removeLight(Index index); LightPointer removeLight(Index index);

View file

@ -533,19 +533,25 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) {
_frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX));
_viewFrustumMeshVertices = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); _viewFrustumMeshVertices = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ);
_viewFrustumMeshStream.addBuffer(_viewFrustumMeshVertices._buffer, _viewFrustumMeshVertices._offset, _viewFrustumMeshVertices._stride); _viewFrustumMeshStream.addBuffer(_viewFrustumMeshVertices._buffer, _viewFrustumMeshVertices._offset, _viewFrustumMeshVertices._stride);
_shadowFrustumMeshVertices = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); for (auto i = 0; i < MAX_SHADOW_FRUSTUM_COUNT; i++) {
_shadowFrustumMeshStream.addBuffer(_shadowFrustumMeshVertices._buffer, _shadowFrustumMeshVertices._offset, _shadowFrustumMeshVertices._stride); _shadowFrustumMeshVertices[i] = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ);
_shadowFrustumMeshStream[i].addBuffer(_shadowFrustumMeshVertices[i]._buffer, _shadowFrustumMeshVertices[i]._offset, _shadowFrustumMeshVertices[i]._stride);
}
} }
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (_updateFrustums) { if (_updateFrustums) {
updateFrustum(args->getViewFrustum(), _viewFrustumMeshVertices); updateFrustum(args->getViewFrustum(), _viewFrustumMeshVertices);
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow) { if (globalShadow) {
updateFrustum(*globalShadow->getCascade(0).getFrustum(), _shadowFrustumMeshVertices); const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount());
for (auto i = 0; i < cascadeCount; i++) {
updateFrustum(*globalShadow->getCascade(i).getFrustum(), _shadowFrustumMeshVertices[i]);
}
} }
} }
@ -583,9 +589,16 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) {
batch.setInputStream(0, _viewFrustumMeshStream); batch.setInputStream(0, _viewFrustumMeshStream);
batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U);
batch._glUniform4f(0, 1.0f, 0.0f, 0.0f, 1.0f); if (globalShadow) {
batch.setInputStream(0, _shadowFrustumMeshStream); const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount());
batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); for (auto i = 0; i < cascadeCount; i++) {
float cascadeTint = i / (float)(globalShadow->getCascadeCount() - 1);
batch._glUniform4f(0, 1.0f, 0.0f, cascadeTint, 1.0f);
batch.setInputStream(0, _shadowFrustumMeshStream[i]);
batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U);
}
}
args->_batch = nullptr; args->_batch = nullptr;
}); });

View file

@ -193,13 +193,15 @@ public:
private: private:
static const int MAX_SHADOW_FRUSTUM_COUNT{ 4 };
bool _updateFrustums{ true }; bool _updateFrustums{ true };
gpu::PipelinePointer _pipeline; gpu::PipelinePointer _pipeline;
gpu::BufferView _frustumMeshIndices; gpu::BufferView _frustumMeshIndices;
gpu::BufferView _viewFrustumMeshVertices; gpu::BufferView _viewFrustumMeshVertices;
gpu::BufferView _shadowFrustumMeshVertices;
gpu::BufferStream _viewFrustumMeshStream; gpu::BufferStream _viewFrustumMeshStream;
gpu::BufferStream _shadowFrustumMeshStream; gpu::BufferView _shadowFrustumMeshVertices[MAX_SHADOW_FRUSTUM_COUNT];
gpu::BufferStream _shadowFrustumMeshStream[MAX_SHADOW_FRUSTUM_COUNT];
static void updateFrustum(const ViewFrustum& frustum, gpu::BufferView& vertexBuffer); static void updateFrustum(const ViewFrustum& frustum, gpu::BufferView& vertexBuffer);
}; };

View file

@ -137,9 +137,11 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
assert(lightStage); assert(lightStage);
auto shadow = lightStage->getCurrentKeyShadow(); auto shadow = lightStage->getCurrentKeyShadow();
if (!shadow) return; if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) {
return;
}
const auto& fbo = shadow->getCascade(0).framebuffer; const auto& fbo = shadow->getCascade(_cascadeIndex).framebuffer;
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
ShapeKey::Builder defaultKeyBuilder; ShapeKey::Builder defaultKeyBuilder;
@ -149,7 +151,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
// the minimal Z range. // the minimal Z range.
adjustNearFar(inShapeBounds, adjustedShadowFrustum); adjustNearFar(inShapeBounds, adjustedShadowFrustum);
// Reapply the frustum as it has been adjusted // Reapply the frustum as it has been adjusted
shadow->setFrustum(0, adjustedShadowFrustum); shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum);
args->popViewFrustum(); args->popViewFrustum();
args->pushViewFrustum(adjustedShadowFrustum); args->pushViewFrustum(adjustedShadowFrustum);
@ -215,22 +217,25 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
initZPassPipelines(*shapePlumber, state); initZPassPipelines(*shapePlumber, state);
} }
const auto cachedMode = task.addJob<RenderShadowSetup>("ShadowSetup", 0); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup", i);
const auto shadowFilter = setupOutput.getN<RenderShadowSetup::Outputs>(1);
// CPU jobs: // CPU jobs:
// Fetch and cull the items from the scene // Fetch and cull the items from the scene
auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter); const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying();
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter); const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW);
// Sort // Sort
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection); const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true); const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
// GPU jobs: Render to shadow map // GPU jobs: Render to shadow map
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber); task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i);
task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode); task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
}
} }
void RenderShadowTask::configure(const Config& configuration) { void RenderShadowTask::configure(const Config& configuration) {
@ -239,15 +244,18 @@ void RenderShadowTask::configure(const Config& configuration) {
// Task::configure(configuration); // Task::configure(configuration);
} }
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
auto lightStage = renderContext->_scene->getStage<LightStage>(); auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage); assert(lightStage);
// Cache old render args
RenderArgs* args = renderContext->args;
output.edit0() = args->_renderMode;
const auto globalShadow = lightStage->getCurrentKeyShadow(); const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow) { const auto globalShadowCascadeCount = globalShadow->getCascadeCount();
// Cache old render args if (globalShadow && _cascadeIndex<globalShadowCascadeCount) {
RenderArgs* args = renderContext->args; output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
output = args->_renderMode;
const auto nearClip = args->getViewFrustum().getNearClip(); const auto nearClip = args->getViewFrustum().getNearClip();
const auto farClip = args->getViewFrustum().getFarClip(); const auto farClip = args->getViewFrustum().getFarClip();
@ -255,7 +263,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
static const float SHADOW_MAX_DISTANCE = 25.0f; static const float SHADOW_MAX_DISTANCE = 25.0f;
static const float SHADOW_OVERLAP_DISTANCE = 1.0f; static const float SHADOW_OVERLAP_DISTANCE = 1.0f;
float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadowCascadeCount - 1 - _cascadeIndex);
float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE;
if (_cascadeIndex == 0) { if (_cascadeIndex == 0) {
@ -268,6 +276,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
// Set the keylight render args // Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
} else {
output.edit1() = ItemFilter::Builder::nothing();
} }
} }
@ -275,6 +285,8 @@ void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
// Reset the render args // Reset the render args
args->popViewFrustum(); args->_renderMode = input.get0();
args->_renderMode = input; if (!input.get1().selectsNothing()) {
args->popViewFrustum();
}
}; };

View file

@ -24,11 +24,12 @@ public:
using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>; using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>;
using JobModel = render::Job::ModelI<RenderShadowMap, Inputs>; using JobModel = render::Job::ModelI<RenderShadowMap, Inputs>;
RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} RenderShadowMap(render::ShapePlumberPointer shapePlumber, unsigned int cascadeIndex) : _shapePlumber{ shapePlumber }, _cascadeIndex{ cascadeIndex } {}
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
protected: protected:
render::ShapePlumberPointer _shapePlumber; render::ShapePlumberPointer _shapePlumber;
unsigned int _cascadeIndex;
}; };
class RenderShadowTaskConfig : public render::Task::Config::Persistent { class RenderShadowTaskConfig : public render::Task::Config::Persistent {
@ -54,11 +55,11 @@ public:
class RenderShadowSetup { class RenderShadowSetup {
public: public:
using Output = RenderArgs::RenderMode; using Outputs = render::VaryingSet2<RenderArgs::RenderMode, render::ItemFilter>;
using JobModel = render::Job::ModelO<RenderShadowSetup, Output>; using JobModel = render::Job::ModelO<RenderShadowSetup, Outputs>;
RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
void run(const render::RenderContextPointer& renderContext, Output& output); void run(const render::RenderContextPointer& renderContext, Outputs& output);
private: private:
@ -67,7 +68,7 @@ private:
class RenderShadowTeardown { class RenderShadowTeardown {
public: public:
using Input = RenderArgs::RenderMode; using Input = RenderShadowSetup::Outputs;
using JobModel = render::Job::ModelI<RenderShadowTeardown, Input>; using JobModel = render::Job::ModelI<RenderShadowTeardown, Input>;
void run(const render::RenderContextPointer& renderContext, const Input& input); void run(const render::RenderContextPointer& renderContext, const Input& input);
}; };

View file

@ -82,28 +82,30 @@ void FetchSpatialTree::configure(const Config& config) {
_lodAngle = config.lodAngle; _lodAngle = config.lodAngle;
} }
void FetchSpatialTree::run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) { void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
auto& scene = renderContext->_scene;
// start fresh // start fresh
outSelection.clear(); outSelection.clear();
// Eventually use a frozen frustum if (!filter.selectsNothing()) {
auto queryFrustum = args->getViewFrustum(); assert(renderContext->args);
if (_freezeFrustum) { assert(renderContext->args->hasViewFrustum());
if (_justFrozeFrustum) { RenderArgs* args = renderContext->args;
_justFrozeFrustum = false; auto& scene = renderContext->_scene;
_frozenFrutstum = args->getViewFrustum();
}
queryFrustum = _frozenFrutstum;
}
// Octree selection! // Eventually use a frozen frustum
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); auto queryFrustum = args->getViewFrustum();
scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle); if (_freezeFrustum) {
if (_justFrozeFrustum) {
_justFrozeFrustum = false;
_frozenFrustum = args->getViewFrustum();
}
queryFrustum = _frozenFrustum;
}
// Octree selection!
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle);
}
} }
void CullSpatialSelection::configure(const Config& config) { void CullSpatialSelection::configure(const Config& config) {
@ -113,11 +115,12 @@ void CullSpatialSelection::configure(const Config& config) {
} }
void CullSpatialSelection::run(const RenderContextPointer& renderContext, void CullSpatialSelection::run(const RenderContextPointer& renderContext,
const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { const Inputs& inputs, ItemBounds& outItems) {
assert(renderContext->args); assert(renderContext->args);
assert(renderContext->args->hasViewFrustum()); assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
auto& scene = renderContext->_scene; auto& scene = renderContext->_scene;
auto& inSelection = inputs.get0();
auto& details = args->_details.edit(_detailType); auto& details = args->_details.edit(_detailType);
details._considered += (int)inSelection.numItems(); details._considered += (int)inSelection.numItems();
@ -126,9 +129,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
if (_freezeFrustum) { if (_freezeFrustum) {
if (_justFrozeFrustum) { if (_justFrozeFrustum) {
_justFrozeFrustum = false; _justFrozeFrustum = false;
_frozenFrutstum = args->getViewFrustum(); _frozenFrustum = args->getViewFrustum();
} }
args->pushViewFrustum(_frozenFrutstum); // replace the true view frustum by the frozen one args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one
} }
// Culling Frustum / solidAngle test helper class // Culling Frustum / solidAngle test helper class
@ -181,122 +184,124 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
outItems.clear(); outItems.clear();
outItems.reserve(inSelection.numItems()); outItems.reserve(inSelection.numItems());
// Now get the bound, and const auto filter = inputs.get1();
// filter individually against the _filter if (!filter.selectsNothing()) {
// visibility cull if partially selected ( octree cell contianing it was partial) // Now get the bound, and
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) // filter individually against the _filter
// visibility cull if partially selected ( octree cell contianing it was partial)
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
if (_skipCulling) { if (_skipCulling) {
// inside & fit items: filter only, culling is disabled // inside & fit items: filter only, culling is disabled
{ {
PerformanceTimer perfTimer("insideFitItems"); PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) { for (auto id : inSelection.insideItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) { if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound()); ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
// inside & subcell items: filter only, culling is disabled
{
PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
// partial & fit items: filter only, culling is disabled
{
PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
// partial & subcell items: filter only, culling is disabled
{
PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) {
auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
} else {
// inside & fit items: easy, just filter
{
PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) {
auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
// inside & subcell items: filter & distance cull
{
PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
} }
} }
} }
}
// partial & fit items: filter & frustum cull // inside & subcell items: filter only, culling is disabled
{ {
PerformanceTimer perfTimer("partialFitItems"); PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.partialItems) { for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) { if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound()); ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
} }
} }
} }
}
// partial & subcell items:: filter & frutum cull & solidangle cull // partial & fit items: filter only, culling is disabled
{ {
PerformanceTimer perfTimer("partialSmallItems"); PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialSubcellItems) { for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id); auto& item = scene->getItem(id);
if (_filter.test(item.getKey())) { if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound()); ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) { outItems.emplace_back(itemBound);
}
}
}
// partial & subcell items: filter only, culling is disabled
{
PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) {
auto& item = scene->getItem(id);
if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
} else {
// inside & fit items: easy, just filter
{
PerformanceTimer perfTimer("insideFitItems");
for (auto id : inSelection.insideItems) {
auto& item = scene->getItem(id);
if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
outItems.emplace_back(itemBound);
}
}
}
// inside & subcell items: filter & distance cull
{
PerformanceTimer perfTimer("insideSmallItems");
for (auto id : inSelection.insideSubcellItems) {
auto& item = scene->getItem(id);
if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
if (test.solidAngleTest(itemBound.bound)) { if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound); outItems.emplace_back(itemBound);
} }
} }
} }
} }
// partial & fit items: filter & frustum cull
{
PerformanceTimer perfTimer("partialFitItems");
for (auto id : inSelection.partialItems) {
auto& item = scene->getItem(id);
if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) {
outItems.emplace_back(itemBound);
}
}
}
}
// partial & subcell items:: filter & frutum cull & solidangle cull
{
PerformanceTimer perfTimer("partialSmallItems");
for (auto id : inSelection.partialSubcellItems) {
auto& item = scene->getItem(id);
if (filter.test(item.getKey())) {
ItemBound itemBound(id, item.getBound());
if (test.frustumTest(itemBound.bound)) {
if (test.solidAngleTest(itemBound.bound)) {
outItems.emplace_back(itemBound);
}
}
}
}
}
} }
} }
details._rendered += (int)outItems.size(); details._rendered += (int)outItems.size();
// Restore frustum if using the frozen one: // Restore frustum if using the frozen one:
if (_freezeFrustum) { if (_freezeFrustum) {
args->popViewFrustum(); args->popViewFrustum();

View file

@ -51,19 +51,16 @@ namespace render {
class FetchSpatialTree { class FetchSpatialTree {
bool _freezeFrustum{ false }; // initialized by Config bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false }; bool _justFrozeFrustum{ false };
ViewFrustum _frozenFrutstum; ViewFrustum _frozenFrustum;
float _lodAngle; float _lodAngle;
public: public:
using Config = FetchSpatialTreeConfig; using Config = FetchSpatialTreeConfig;
using JobModel = Job::ModelO<FetchSpatialTree, ItemSpatialTree::ItemSelection, Config>; using JobModel = Job::ModelIO<FetchSpatialTree, ItemFilter, ItemSpatialTree::ItemSelection, Config>;
FetchSpatialTree() {} FetchSpatialTree() {}
FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {}
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
void configure(const Config& config); void configure(const Config& config);
void run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection); void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection);
}; };
class CullSpatialSelectionConfig : public Job::Config { class CullSpatialSelectionConfig : public Job::Config {
@ -88,25 +85,24 @@ namespace render {
bool _freezeFrustum{ false }; // initialized by Config bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false }; bool _justFrozeFrustum{ false };
bool _skipCulling{ false }; bool _skipCulling{ false };
ViewFrustum _frozenFrutstum; ViewFrustum _frozenFrustum;
public: public:
using Config = CullSpatialSelectionConfig; using Config = CullSpatialSelectionConfig;
using JobModel = Job::ModelIO<CullSpatialSelection, ItemSpatialTree::ItemSelection, ItemBounds, Config>; using Inputs = render::VaryingSet2<ItemSpatialTree::ItemSelection, ItemFilter>;
using JobModel = Job::ModelIO<CullSpatialSelection, Inputs, ItemBounds, Config>;
CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type, const ItemFilter& filter) : CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type) :
_cullFunctor{ cullFunctor }, _cullFunctor{ cullFunctor },
_detailType(type), _detailType(type) {}
_filter(filter) {}
CullSpatialSelection(CullFunctor cullFunctor) : CullSpatialSelection(CullFunctor cullFunctor) :
_cullFunctor{ cullFunctor } {} _cullFunctor{ cullFunctor } {}
CullFunctor _cullFunctor; CullFunctor _cullFunctor;
RenderDetails::Type _detailType{ RenderDetails::OTHER }; RenderDetails::Type _detailType{ RenderDetails::OTHER };
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
void configure(const Config& config); void configure(const Config& config);
void run(const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems); void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
}; };
} }

View file

@ -182,6 +182,8 @@ public:
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); }
// Convenient standard keys that we will keep on using all over the place // Convenient standard keys that we will keep on using all over the place
static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); }
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
@ -191,12 +193,14 @@ public:
static Builder background() { return Builder().withViewSpace().withLayered(); } static Builder background() { return Builder().withViewSpace().withLayered(); }
static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); }
static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); }
static Builder nothing() { return Builder().withNothing(); }
}; };
ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {}
// Item Filter operator testing if a key pass the filter // Item Filter operator testing if a key pass the filter
bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
bool selectsNothing() const { return !_mask.any(); }
class Less { class Less {
public: public:

View file

@ -22,9 +22,11 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
// CPU jobs: // CPU jobs:
// Fetch and cull the items from the scene // Fetch and cull the items from the scene
auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered();
const auto spatialFilter = render::Varying(filter);
const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", spatialFilter); const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", spatialFilter);
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying();
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);
// Overlays are not culled // Overlays are not culled
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchOverlaySelection"); const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchOverlaySelection");

View file

@ -184,7 +184,10 @@ Rectangle {
ListElement { text: "Lightmap"; color: "White" } ListElement { text: "Lightmap"; color: "White" }
ListElement { text: "Scattering"; color: "White" } ListElement { text: "Scattering"; color: "White" }
ListElement { text: "Lighting"; color: "White" } ListElement { text: "Lighting"; color: "White" }
ListElement { text: "Shadow"; color: "White" } ListElement { text: "Shadow Cascade 0"; color: "White" }
ListElement { text: "Shadow Cascade 1"; color: "White" }
ListElement { text: "Shadow Cascade 2"; color: "White" }
ListElement { text: "Shadow Cascade 3"; color: "White" }
ListElement { text: "Linear Depth"; color: "White" } ListElement { text: "Linear Depth"; color: "White" }
ListElement { text: "Half Linear Depth"; color: "White" } ListElement { text: "Half Linear Depth"; color: "White" }
ListElement { text: "Half Normal"; color: "White" } ListElement { text: "Half Normal"; color: "White" }