mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
Shadow maps rendered in cascades
This commit is contained in:
parent
08b06281f4
commit
103e036b70
14 changed files with 226 additions and 178 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -64,7 +64,10 @@ protected:
|
|||
LightmapMode,
|
||||
ScatteringMode,
|
||||
LightingMode,
|
||||
ShadowMode,
|
||||
Shadow0Mode,
|
||||
Shadow1Mode,
|
||||
Shadow2Mode,
|
||||
Shadow3Mode,
|
||||
LinearDepthMode,
|
||||
HalfLinearDepthMode,
|
||||
HalfNormalMode,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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" }
|
||||
|
|
Loading…
Reference in a new issue