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 ?
if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight);
_shadowIndex = _stage->addShadow(_sunIndex);
_shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT);
} else {
_stage->updateLightArrayBuffer(_sunIndex);
}

View file

@ -284,7 +284,10 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust
return DEFAULT_SCATTERING_SHADER;
case LightingMode:
return DEFAULT_LIGHTING_SHADER;
case ShadowMode:
case Shadow0Mode:
case Shadow1Mode:
case Shadow2Mode:
case Shadow3Mode:
return DEFAULT_SHADOW_SHADER;
case LinearDepthMode:
return DEFAULT_LINEAR_DEPTH_SHADER;
@ -438,7 +441,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow();
const auto& globalShadow = lightAndShadow.second;
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) {

View file

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

View file

@ -19,6 +19,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 };
const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 };
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);
Index shadowId = INVALID_INDEX;
if (light) {
shadowId = _shadows.newElement(std::make_shared<Shadow>(light));
shadowId = _shadows.newElement(std::make_shared<Shadow>(light, cascadeCount));
_descs[lightIndex].shadowId = shadowId;
}
return shadowId;

View file

@ -31,6 +31,8 @@ public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
static const unsigned int SUN_SHADOW_CASCADE_COUNT;
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
@ -109,7 +111,7 @@ public:
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
Index addShadow(Index lightIndex);
Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U);
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));
_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);
_shadowFrustumMeshVertices = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ);
_shadowFrustumMeshStream.addBuffer(_shadowFrustumMeshVertices._buffer, _shadowFrustumMeshVertices._offset, _shadowFrustumMeshVertices._stride);
for (auto i = 0; i < MAX_SHADOW_FRUSTUM_COUNT; i++) {
_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) {
updateFrustum(args->getViewFrustum(), _viewFrustumMeshVertices);
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
const auto globalShadow = lightStage->getCurrentKeyShadow();
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.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U);
batch._glUniform4f(0, 1.0f, 0.0f, 0.0f, 1.0f);
batch.setInputStream(0, _shadowFrustumMeshStream);
batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U);
if (globalShadow) {
const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount());
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;
});

View file

@ -193,13 +193,15 @@ public:
private:
static const int MAX_SHADOW_FRUSTUM_COUNT{ 4 };
bool _updateFrustums{ true };
gpu::PipelinePointer _pipeline;
gpu::BufferView _frustumMeshIndices;
gpu::BufferView _viewFrustumMeshVertices;
gpu::BufferView _shadowFrustumMeshVertices;
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);
};

View file

@ -137,9 +137,11 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
assert(lightStage);
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;
ShapeKey::Builder defaultKeyBuilder;
@ -149,7 +151,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
// the minimal Z range.
adjustNearFar(inShapeBounds, adjustedShadowFrustum);
// Reapply the frustum as it has been adjusted
shadow->setFrustum(0, adjustedShadowFrustum);
shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum);
args->popViewFrustum();
args->pushViewFrustum(adjustedShadowFrustum);
@ -215,22 +217,25 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
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:
// 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 culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter);
// CPU jobs:
// Fetch and cull the items from the scene
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowSelection", shadowFilter);
const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying();
const auto culledShadowSelection = task.addJob<CullSpatialSelection>("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW);
// Sort
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
// Sort
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
// GPU jobs: Render to shadow map
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber);
// GPU jobs: Render to shadow map
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i);
task.addJob<RenderShadowTeardown>("ShadowTeardown", cachedMode);
task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
}
}
void RenderShadowTask::configure(const Config& configuration) {
@ -239,15 +244,18 @@ void RenderShadowTask::configure(const Config& 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>();
assert(lightStage);
// Cache old render args
RenderArgs* args = renderContext->args;
output.edit0() = args->_renderMode;
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow) {
// Cache old render args
RenderArgs* args = renderContext->args;
output = args->_renderMode;
const auto globalShadowCascadeCount = globalShadow->getCascadeCount();
if (globalShadow && _cascadeIndex<globalShadowCascadeCount) {
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
const auto nearClip = args->getViewFrustum().getNearClip();
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_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;
if (_cascadeIndex == 0) {
@ -268,6 +276,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
// Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
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;
// Reset the render args
args->popViewFrustum();
args->_renderMode = input;
args->_renderMode = input.get0();
if (!input.get1().selectsNothing()) {
args->popViewFrustum();
}
};

View file

@ -24,11 +24,12 @@ public:
using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>;
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);
protected:
render::ShapePlumberPointer _shapePlumber;
unsigned int _cascadeIndex;
};
class RenderShadowTaskConfig : public render::Task::Config::Persistent {
@ -54,11 +55,11 @@ public:
class RenderShadowSetup {
public:
using Output = RenderArgs::RenderMode;
using JobModel = render::Job::ModelO<RenderShadowSetup, Output>;
using Outputs = render::VaryingSet2<RenderArgs::RenderMode, render::ItemFilter>;
using JobModel = render::Job::ModelO<RenderShadowSetup, Outputs>;
RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
void run(const render::RenderContextPointer& renderContext, Output& output);
void run(const render::RenderContextPointer& renderContext, Outputs& output);
private:
@ -67,7 +68,7 @@ private:
class RenderShadowTeardown {
public:
using Input = RenderArgs::RenderMode;
using Input = RenderShadowSetup::Outputs;
using JobModel = render::Job::ModelI<RenderShadowTeardown, 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;
}
void FetchSpatialTree::run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
auto& scene = renderContext->_scene;
void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) {
// start fresh
outSelection.clear();
// Eventually use a frozen frustum
auto queryFrustum = args->getViewFrustum();
if (_freezeFrustum) {
if (_justFrozeFrustum) {
_justFrozeFrustum = false;
_frozenFrutstum = args->getViewFrustum();
}
queryFrustum = _frozenFrutstum;
}
if (!filter.selectsNothing()) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
auto& scene = renderContext->_scene;
// Octree selection!
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle);
// Eventually use a frozen frustum
auto queryFrustum = args->getViewFrustum();
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) {
@ -113,11 +115,12 @@ void CullSpatialSelection::configure(const Config& config) {
}
void CullSpatialSelection::run(const RenderContextPointer& renderContext,
const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) {
const Inputs& inputs, ItemBounds& outItems) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
auto& scene = renderContext->_scene;
auto& inSelection = inputs.get0();
auto& details = args->_details.edit(_detailType);
details._considered += (int)inSelection.numItems();
@ -126,9 +129,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
if (_freezeFrustum) {
if (_justFrozeFrustum) {
_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
@ -181,122 +184,124 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
outItems.clear();
outItems.reserve(inSelection.numItems());
// Now get the bound, and
// 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)
const auto filter = inputs.get1();
if (!filter.selectsNothing()) {
// Now get the bound, and
// 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) {
// inside & fit items: filter only, culling is disabled
{
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 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)) {
if (_skipCulling) {
// inside & fit items: filter only, culling is disabled
{
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);
}
}
}
}
// 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)) {
// 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 & 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)) {
// 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);
}
}
}
}
// 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();
// Restore frustum if using the frozen one:
if (_freezeFrustum) {
args->popViewFrustum();

View file

@ -51,19 +51,16 @@ namespace render {
class FetchSpatialTree {
bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false };
ViewFrustum _frozenFrutstum;
ViewFrustum _frozenFrustum;
float _lodAngle;
public:
using Config = FetchSpatialTreeConfig;
using JobModel = Job::ModelO<FetchSpatialTree, ItemSpatialTree::ItemSelection, Config>;
using JobModel = Job::ModelIO<FetchSpatialTree, ItemFilter, ItemSpatialTree::ItemSelection, Config>;
FetchSpatialTree() {}
FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {}
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
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 {
@ -88,25 +85,24 @@ namespace render {
bool _freezeFrustum{ false }; // initialized by Config
bool _justFrozeFrustum{ false };
bool _skipCulling{ false };
ViewFrustum _frozenFrutstum;
ViewFrustum _frozenFrustum;
public:
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 },
_detailType(type),
_filter(filter) {}
_detailType(type) {}
CullSpatialSelection(CullFunctor cullFunctor) :
_cullFunctor{ cullFunctor } {}
CullFunctor _cullFunctor;
RenderDetails::Type _detailType{ RenderDetails::OTHER };
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
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& 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
static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); }
static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); }
@ -191,12 +193,14 @@ public:
static Builder background() { return Builder().withViewSpace().withLayered(); }
static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().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) {}
// Item Filter operator testing if a key pass the filter
bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); }
bool selectsNothing() const { return !_mask.any(); }
class Less {
public:

View file

@ -22,9 +22,11 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
// CPU jobs:
// 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 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
const auto nonspatialSelection = task.addJob<FetchNonspatialItems>("FetchOverlaySelection");

View file

@ -184,7 +184,10 @@ Rectangle {
ListElement { text: "Lightmap"; color: "White" }
ListElement { text: "Scattering"; 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: "Half Linear Depth"; color: "White" }
ListElement { text: "Half Normal"; color: "White" }