mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
Merge pull request #12323 from Zvork/shadow
Shadow performance / acnee improvements
This commit is contained in:
commit
51bf3c1466
24 changed files with 722 additions and 192 deletions
|
@ -18,8 +18,7 @@
|
|||
|
||||
using RenderArgsPointer = std::shared_ptr<RenderArgs>;
|
||||
|
||||
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
|
||||
void MainRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) {
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, render::ItemKey::TAG_BITS_1, render::ItemKey::TAG_BITS_1);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -62,7 +62,7 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
|
|||
samplerDesc._wrapModeU = Sampler::WRAP_BORDER;
|
||||
samplerDesc._wrapModeV = Sampler::WRAP_BORDER;
|
||||
samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR;
|
||||
samplerDesc._comparisonFunc = LESS_EQUAL;
|
||||
samplerDesc._comparisonFunc = LESS;
|
||||
|
||||
depthTexture->setSampler(Sampler(samplerDesc));
|
||||
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
|
||||
|
|
|
@ -64,8 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc
|
|||
return voxelSizeScale / powf(2.0f, renderLevel);
|
||||
}
|
||||
|
||||
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
const float maxScale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
|
||||
return atan(maxScale / visibleDistanceAtMaxScale);
|
||||
}
|
||||
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
// Smallest visible element is 1cm
|
||||
const float smallestSize = 0.01f;
|
||||
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ float calculateRenderAccuracy(const glm::vec3& position,
|
|||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
|
||||
|
||||
float getAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
|
||||
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
|
||||
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
|
||||
|
|
|
@ -23,8 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
|
|||
0.5, 0.5, 0.5, 1.0 };
|
||||
const int LightStage::Shadow::MAP_SIZE = 1024;
|
||||
|
||||
static const auto MAX_BIAS = 0.006f;
|
||||
|
||||
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||
|
||||
LightStage::LightStage() {
|
||||
|
@ -63,7 +61,7 @@ LightStage::LightStage() {
|
|||
|
||||
LightStage::Shadow::Schema::Schema() {
|
||||
ShadowTransform defaultTransform;
|
||||
defaultTransform.bias = MAX_BIAS;
|
||||
defaultTransform.fixedBias = 0.005f;
|
||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||
invMapSize = 1.0f / MAP_SIZE;
|
||||
cascadeCount = 1;
|
||||
|
@ -214,13 +212,10 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
|||
cascade._frustum->setOrientation(orientation);
|
||||
cascade._frustum->setPosition(position);
|
||||
}
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f);
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth, float farDepth) {
|
||||
float nearDepth, float farDepth, float fixedBias, float slopeBias) {
|
||||
assert(nearDepth < farDepth);
|
||||
assert(cascadeIndex < _cascades.size());
|
||||
|
||||
|
@ -269,12 +264,10 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
|
|||
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
// Adapt shadow bias to shadow resolution with a totally empirical formula
|
||||
const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y));
|
||||
const auto REFERENCE_TEXEL_DENSITY = 7.5f;
|
||||
const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim;
|
||||
schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity);
|
||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
schemaCascade.fixedBias = fixedBias;
|
||||
schemaCascade.slopeBias = slopeBias;
|
||||
}
|
||||
|
||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||
|
@ -285,7 +278,9 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
|
|||
|
||||
*cascade._frustum = shadowFrustum;
|
||||
// Update the buffer
|
||||
_schemaBuffer.edit<Schema>().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
void setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||
float nearDepth = 1.0f, float farDepth = 1000.0f, float fixedBias = 0.005f, float slopeBias = 0.005f);
|
||||
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||
|
||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||
|
@ -213,6 +213,7 @@ protected:
|
|||
Index _sunOffLightId;
|
||||
|
||||
Index _defaultLightId;
|
||||
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
|
|
@ -200,8 +200,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
|
|||
});
|
||||
}
|
||||
|
||||
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask) {
|
||||
cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; };
|
||||
void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cameraCullFunctor, uint8_t tagBits, uint8_t tagMask) {
|
||||
::CullFunctor shadowCullFunctor = [this](const RenderArgs* args, const AABox& bounds) {
|
||||
return _cullFunctor(args, bounds);
|
||||
};
|
||||
|
||||
// Prepare the ShapePipeline
|
||||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
|
@ -213,26 +215,54 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
initZPassPipelines(*shapePlumber, state);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
const auto setupOutput = task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||
const auto queryResolution = setupOutput.getN<RenderShadowSetup::Outputs>(2);
|
||||
// Fetch and cull the items from the scene
|
||||
static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
|
||||
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
|
||||
const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
|
||||
|
||||
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
|
||||
// frustum culling or this will make shadow casters out of the camera frustum disappear.
|
||||
const auto cameraFrustum = setupOutput.getN<RenderShadowSetup::Outputs>(2);
|
||||
const auto applyFunctorInputs = ApplyCullFunctorOnItemBounds::Inputs(shadowItems, cameraFrustum).asVarying();
|
||||
const auto culledShadowItems = task.addJob<ApplyCullFunctorOnItemBounds>("ShadowCullCamera", applyFunctorInputs, cameraCullFunctor);
|
||||
|
||||
// Sort
|
||||
const auto sortedPipelines = task.addJob<PipelineSortShapes>("PipelineSortShadow", culledShadowItems);
|
||||
const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadow", sortedPipelines, true);
|
||||
|
||||
render::Varying cascadeFrustums[SHADOW_CASCADE_MAX_COUNT] = {
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer(),
|
||||
ViewFrustumPointer()
|
||||
};
|
||||
|
||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>("ShadowCascadeSetup", i, tagBits, tagMask);
|
||||
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
char jobName[64];
|
||||
sprintf(jobName, "ShadowCascadeSetup%d", i);
|
||||
const auto cascadeSetupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i, _cullFunctor, tagBits, tagMask);
|
||||
const auto shadowFilter = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(0);
|
||||
auto antiFrustum = render::Varying(ViewFrustumPointer());
|
||||
cascadeFrustums[i] = cascadeSetupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||
if (i > 1) {
|
||||
antiFrustum = cascadeFrustums[i - 2];
|
||||
}
|
||||
|
||||
// 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);
|
||||
// CPU jobs: finer grained culling
|
||||
const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
|
||||
const auto culledShadowItemsAndBounds = task.addJob<CullShapeBounds>("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", setupOutput);
|
||||
sprintf(jobName, "RenderShadowMap%d", i);
|
||||
task.addJob<RenderShadowMap>(jobName, culledShadowItemsAndBounds, shapePlumber, i);
|
||||
task.addJob<RenderShadowCascadeTeardown>("ShadowCascadeTeardown", shadowFilter);
|
||||
}
|
||||
|
||||
task.addJob<RenderShadowTeardown>("ShadowTeardown", setupOutput);
|
||||
}
|
||||
|
||||
void RenderShadowTask::configure(const Config& configuration) {
|
||||
|
@ -241,15 +271,107 @@ void RenderShadowTask::configure(const Config& configuration) {
|
|||
// Task::configure(configuration);
|
||||
}
|
||||
|
||||
void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||
RenderShadowSetup::RenderShadowSetup() :
|
||||
_cameraFrustum{ std::make_shared<ViewFrustum>() },
|
||||
_coarseShadowFrustum{ std::make_shared<ViewFrustum>() } {
|
||||
|
||||
}
|
||||
|
||||
void RenderShadowSetup::configure(const Config& configuration) {
|
||||
setConstantBias(0, configuration.constantBias0);
|
||||
setConstantBias(1, configuration.constantBias1);
|
||||
setConstantBias(2, configuration.constantBias2);
|
||||
setConstantBias(3, configuration.constantBias3);
|
||||
setSlopeBias(0, configuration.slopeBias0);
|
||||
setSlopeBias(1, configuration.slopeBias1);
|
||||
setSlopeBias(2, configuration.slopeBias2);
|
||||
setSlopeBias(3, configuration.slopeBias3);
|
||||
}
|
||||
|
||||
void RenderShadowSetup::setConstantBias(int cascadeIndex, float value) {
|
||||
_bias[cascadeIndex]._constant = value * value * value * 0.004f;
|
||||
}
|
||||
|
||||
void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) {
|
||||
_bias[cascadeIndex]._slope = value * value * value * 0.01f;
|
||||
}
|
||||
|
||||
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;
|
||||
output.edit1() = glm::ivec2(0, 0);
|
||||
// Save main camera frustum
|
||||
*_cameraFrustum = args->getViewFrustum();
|
||||
output.edit2() = _cameraFrustum;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow) {
|
||||
globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
|
||||
auto& firstCascade = globalShadow->getCascade(0);
|
||||
auto& firstCascadeFrustum = firstCascade.getFrustum();
|
||||
unsigned int cascadeIndex;
|
||||
|
||||
// Adjust each cascade frustum
|
||||
for (cascadeIndex = 0; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
|
||||
auto& bias = _bias[cascadeIndex];
|
||||
globalShadow->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(),
|
||||
SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
|
||||
bias._constant, bias._slope);
|
||||
}
|
||||
|
||||
// Now adjust coarse frustum bounds
|
||||
auto frustumPosition = firstCascadeFrustum->getPosition();
|
||||
auto farTopLeft = firstCascadeFrustum->getFarTopLeft() - frustumPosition;
|
||||
auto farBottomRight = firstCascadeFrustum->getFarBottomRight() - frustumPosition;
|
||||
|
||||
auto left = glm::dot(farTopLeft, firstCascadeFrustum->getRight());
|
||||
auto right = glm::dot(farBottomRight, firstCascadeFrustum->getRight());
|
||||
auto top = glm::dot(farTopLeft, firstCascadeFrustum->getUp());
|
||||
auto bottom = glm::dot(farBottomRight, firstCascadeFrustum->getUp());
|
||||
auto near = firstCascadeFrustum->getNearClip();
|
||||
auto far = firstCascadeFrustum->getFarClip();
|
||||
|
||||
for (cascadeIndex = 1; cascadeIndex < globalShadow->getCascadeCount(); ++cascadeIndex) {
|
||||
auto& cascadeFrustum = globalShadow->getCascade(cascadeIndex).getFrustum();
|
||||
|
||||
farTopLeft = cascadeFrustum->getFarTopLeft() - frustumPosition;
|
||||
farBottomRight = cascadeFrustum->getFarBottomRight() - frustumPosition;
|
||||
|
||||
auto cascadeLeft = glm::dot(farTopLeft, cascadeFrustum->getRight());
|
||||
auto cascadeRight = glm::dot(farBottomRight, cascadeFrustum->getRight());
|
||||
auto cascadeTop = glm::dot(farTopLeft, cascadeFrustum->getUp());
|
||||
auto cascadeBottom = glm::dot(farBottomRight, cascadeFrustum->getUp());
|
||||
auto cascadeNear = cascadeFrustum->getNearClip();
|
||||
auto cascadeFar = cascadeFrustum->getFarClip();
|
||||
left = glm::min(left, cascadeLeft);
|
||||
right = glm::max(right, cascadeRight);
|
||||
bottom = glm::min(bottom, cascadeBottom);
|
||||
top = glm::max(top, cascadeTop);
|
||||
near = glm::min(near, cascadeNear);
|
||||
far = glm::max(far, cascadeFar);
|
||||
}
|
||||
|
||||
_coarseShadowFrustum->setPosition(firstCascadeFrustum->getPosition());
|
||||
_coarseShadowFrustum->setOrientation(firstCascadeFrustum->getOrientation());
|
||||
_coarseShadowFrustum->setProjection(glm::ortho<float>(left, right, bottom, top, near, far));
|
||||
_coarseShadowFrustum->calculate();
|
||||
|
||||
// Push frustum for further culling and selection
|
||||
args->pushViewFrustum(*_coarseShadowFrustum);
|
||||
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
|
||||
// We want for the octree query enough resolution to catch the details in the lowest cascade. So compute
|
||||
// the desired resolution for the first cascade frustum and extrapolate it to the coarse frustum.
|
||||
glm::ivec2 queryResolution = firstCascade.framebuffer->getSize();
|
||||
queryResolution.x = int(queryResolution.x * _coarseShadowFrustum->getWidth() / firstCascadeFrustum->getWidth());
|
||||
queryResolution.y = int(queryResolution.y * _coarseShadowFrustum->getHeight() / firstCascadeFrustum->getHeight());
|
||||
output.edit1() = queryResolution;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,39 +381,44 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
|||
// Cache old render args
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
output.edit0() = args->_renderMode;
|
||||
output.edit2() = args->_sizeScale;
|
||||
|
||||
const auto globalShadow = lightStage->getCurrentKeyShadow();
|
||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
|
||||
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||
output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
|
||||
|
||||
// Set the keylight render args
|
||||
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
||||
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
|
||||
if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) {
|
||||
const float shadowSizeScale = 1e16f;
|
||||
// Set the size scale to a ridiculously high value to prevent small object culling which assumes
|
||||
// the view frustum is a perspective projection. But this isn't the case for the sun which
|
||||
// is an orthographic projection.
|
||||
args->_sizeScale = shadowSizeScale;
|
||||
}
|
||||
auto& cascade = globalShadow->getCascade(_cascadeIndex);
|
||||
auto& cascadeFrustum = cascade.getFrustum();
|
||||
args->pushViewFrustum(*cascadeFrustum);
|
||||
auto texelSize = glm::min(cascadeFrustum->getHeight(), cascadeFrustum->getWidth()) / cascade.framebuffer->getSize().x;
|
||||
// Set the cull threshold to 24 shadow texels. This is totally arbitrary
|
||||
const auto minTexelCount = 24.0f;
|
||||
// TODO : maybe adapt that with LOD management system?
|
||||
texelSize *= minTexelCount;
|
||||
_cullFunctor._minSquareSize = texelSize * texelSize;
|
||||
|
||||
output.edit1() = cascadeFrustum;
|
||||
} else {
|
||||
output.edit1() = ItemFilter::Builder::nothing();
|
||||
output.edit0() = ItemFilter::Builder::nothing();
|
||||
output.edit1() = ViewFrustumPointer();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) {
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.selectsNothing()) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
assert(args->hasViewFrustum());
|
||||
}
|
||||
|
||||
void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
assert(args->hasViewFrustum());
|
||||
// Reset the render args
|
||||
args->_renderMode = input.get0();
|
||||
args->_sizeScale = input.get2();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <render/CullTask.h>
|
||||
|
||||
#include "Shadows_shared.slh"
|
||||
|
||||
class ViewFrustum;
|
||||
|
||||
class RenderShadowMap {
|
||||
|
@ -48,40 +50,101 @@ public:
|
|||
using JobModel = render::Task::Model<RenderShadowTask, Config>;
|
||||
|
||||
RenderShadowTask() {}
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor shouldRender, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cameraCullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00);
|
||||
|
||||
void configure(const Config& configuration);
|
||||
|
||||
struct CullFunctor {
|
||||
float _minSquareSize{ 0.0f };
|
||||
|
||||
bool operator()(const RenderArgs* args, const AABox& bounds) const {
|
||||
// Cull only objects that are too small relatively to shadow frustum
|
||||
const auto boundsSquareRadius = glm::dot(bounds.getDimensions(), bounds.getDimensions());
|
||||
return boundsSquareRadius > _minSquareSize;
|
||||
}
|
||||
};
|
||||
|
||||
CullFunctor _cullFunctor;
|
||||
|
||||
};
|
||||
|
||||
class RenderShadowSetupConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float constantBias0 MEMBER constantBias0 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias1 MEMBER constantBias1 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias2 MEMBER constantBias2 NOTIFY dirty)
|
||||
Q_PROPERTY(float constantBias3 MEMBER constantBias3 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias0 MEMBER slopeBias0 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias1 MEMBER slopeBias1 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias2 MEMBER slopeBias2 NOTIFY dirty)
|
||||
Q_PROPERTY(float slopeBias3 MEMBER slopeBias3 NOTIFY dirty)
|
||||
public:
|
||||
|
||||
float constantBias0{ 0.15f };
|
||||
float constantBias1{ 0.15f };
|
||||
float constantBias2{ 0.175f };
|
||||
float constantBias3{ 0.2f };
|
||||
float slopeBias0{ 0.6f };
|
||||
float slopeBias1{ 0.6f };
|
||||
float slopeBias2{ 0.7f };
|
||||
float slopeBias3{ 0.82f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class RenderShadowSetup {
|
||||
public:
|
||||
using JobModel = render::Job::Model<RenderShadowSetup>;
|
||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, glm::ivec2, ViewFrustumPointer>;
|
||||
using Config = RenderShadowSetupConfig;
|
||||
using JobModel = render::Job::ModelO<RenderShadowSetup, Outputs, Config>;
|
||||
|
||||
RenderShadowSetup() {}
|
||||
void run(const render::RenderContextPointer& renderContext);
|
||||
RenderShadowSetup();
|
||||
void configure(const Config& configuration);
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||
|
||||
private:
|
||||
|
||||
ViewFrustumPointer _cameraFrustum;
|
||||
ViewFrustumPointer _coarseShadowFrustum;
|
||||
struct {
|
||||
float _constant;
|
||||
float _slope;
|
||||
} _bias[SHADOW_CASCADE_MAX_COUNT];
|
||||
|
||||
void setConstantBias(int cascadeIndex, float value);
|
||||
void setSlopeBias(int cascadeIndex, float value);
|
||||
};
|
||||
|
||||
class RenderShadowCascadeSetup {
|
||||
public:
|
||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
|
||||
using Outputs = render::VaryingSet2<render::ItemFilter, ViewFrustumPointer>;
|
||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
||||
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : _cascadeIndex{ cascadeIndex }, _tagBits(tagBits), _tagMask(tagMask) {}
|
||||
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
|
||||
_cascadeIndex{ cascadeIndex }, _cullFunctor{ cullFunctor }, _tagBits(tagBits), _tagMask(tagMask) {}
|
||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||
|
||||
private:
|
||||
|
||||
unsigned int _cascadeIndex;
|
||||
RenderShadowTask::CullFunctor& _cullFunctor;
|
||||
uint8_t _tagBits{ 0x00 };
|
||||
uint8_t _tagMask{ 0x00 };
|
||||
};
|
||||
|
||||
class RenderShadowCascadeTeardown {
|
||||
public:
|
||||
using Input = RenderShadowCascadeSetup::Outputs;
|
||||
using Input = render::ItemFilter;
|
||||
using JobModel = render::Job::ModelI<RenderShadowCascadeTeardown, Input>;
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
class RenderShadowTeardown {
|
||||
public:
|
||||
using Input = RenderShadowSetup::Outputs;
|
||||
using JobModel = render::Job::ModelI<RenderShadowTeardown, Input>;
|
||||
void run(const render::RenderContextPointer& renderContext, const Input& input);
|
||||
};
|
||||
|
||||
#endif // hifi_RenderShadowTask_h
|
||||
|
|
|
@ -17,18 +17,9 @@
|
|||
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred, uint8_t tagBits, uint8_t tagMask) {
|
||||
// auto items = input.get<Input>();
|
||||
|
||||
// Shadows use an orthographic projection because they are linked to sunlights
|
||||
// but the cullFunctor passed is probably tailored for perspective projection and culls too much.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) {
|
||||
// Cull only objects that are too small relatively to shadow frustum
|
||||
auto& frustum = args->getViewFrustum();
|
||||
auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth());
|
||||
const auto boundsRadius = bounds.getDimensions().length();
|
||||
const auto relativeBoundRadius = boundsRadius / frustumSize;
|
||||
const auto threshold = 1e-3f;
|
||||
return relativeBoundRadius > threshold;
|
||||
return true;
|
||||
}, tagBits, tagMask);
|
||||
// Warning : the cull functor passed to the shadow pass should only be testing for LOD culling. If frustum culling
|
||||
// is performed, then casters not in the view frustum will be removed, which is not what we wish.
|
||||
task.addJob<RenderShadowTask>("RenderShadowTask", cullFunctor, tagBits, tagMask);
|
||||
|
||||
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
|
||||
assert(items.canCast<RenderFetchCullSortTask::Output>());
|
||||
|
|
|
@ -79,31 +79,26 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
|||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) +
|
||||
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3])
|
||||
);
|
||||
|
||||
return shadowAttenuation;
|
||||
}
|
||||
|
||||
float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
||||
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
||||
// If a point is not in the map, do not attenuate
|
||||
return 1.0;
|
||||
}
|
||||
// Multiply bias if we are at a grazing angle with light
|
||||
float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal));
|
||||
float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor);
|
||||
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
|
||||
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
|
||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
||||
}
|
||||
|
||||
float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) {
|
||||
float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) {
|
||||
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
||||
vec4 cascadeShadowCoords[2];
|
||||
vec4 cascadeShadowCoords[2] = { vec4(0), vec4(0) };
|
||||
ivec2 cascadeIndices;
|
||||
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||
|
||||
// Adjust bias if we are at a grazing angle with light
|
||||
float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1);
|
||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]);
|
||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], oneMinusNdotL);
|
||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]);
|
||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], oneMinusNdotL);
|
||||
}
|
||||
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||
// Falloff to max distance
|
||||
|
|
|
@ -37,14 +37,15 @@ float getShadowScale() {
|
|||
return shadow.invMapSize;
|
||||
}
|
||||
|
||||
float getShadowBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].bias;
|
||||
float getShadowFixedBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].fixedBias;
|
||||
}
|
||||
|
||||
vec3 getShadowDirInViewSpace() {
|
||||
return shadow.lightDirInViewSpace;
|
||||
float getShadowSlopeBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].slopeBias;
|
||||
}
|
||||
|
||||
|
||||
// Compute the texture coordinates from world coordinates
|
||||
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
||||
vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position;
|
||||
|
@ -52,8 +53,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
|||
}
|
||||
|
||||
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
|
||||
bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0));
|
||||
bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1));
|
||||
bvec2 greaterThanZero = greaterThan(cascadeTexCoords.xy, vec2(0));
|
||||
bvec2 lessThanOne = lessThan(cascadeTexCoords.xy, vec2(1));
|
||||
return all(greaterThanZero) && all(lessThanOne);
|
||||
}
|
||||
|
||||
|
@ -81,10 +82,10 @@ float evalShadowCascadeWeight(vec4 cascadeTexCoords) {
|
|||
float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) {
|
||||
cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]);
|
||||
cascadeIndices.y = cascadeIndices.x+1;
|
||||
if (cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||
if (firstCascadeWeight<1.0 && cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||
cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]);
|
||||
|
||||
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||
float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]);
|
||||
// Returns the mix amount between first and second cascade.
|
||||
return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);
|
||||
|
|
|
@ -11,16 +11,14 @@
|
|||
|
||||
struct ShadowTransform {
|
||||
MAT4 reprojection;
|
||||
|
||||
float bias;
|
||||
float fixedBias;
|
||||
float slopeBias;
|
||||
float _padding1;
|
||||
float _padding2;
|
||||
float _padding3;
|
||||
};
|
||||
|
||||
struct ShadowParameters {
|
||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||
VEC3 lightDirInViewSpace;
|
||||
int cascadeCount;
|
||||
float invMapSize;
|
||||
float invCascadeBlendWidth;
|
||||
|
|
|
@ -28,7 +28,9 @@ void main(void) {
|
|||
|
||||
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
||||
vec4 worldPos = getViewInverse() * viewPos;
|
||||
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal);
|
||||
Light shadowLight = getKeyLight();
|
||||
vec3 worldLightDirection = getLightDirection(shadowLight);
|
||||
float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal);
|
||||
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
discard;
|
||||
|
|
|
@ -28,7 +28,9 @@ void main(void) {
|
|||
|
||||
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
||||
vec4 worldPos = getViewInverse() * viewPos;
|
||||
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal);
|
||||
Light shadowLight = getKeyLight();
|
||||
vec3 worldLightDirection = getLightDirection(shadowLight);
|
||||
float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal);
|
||||
|
||||
// Light mapped or not ?
|
||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||
|
|
|
@ -14,11 +14,66 @@
|
|||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
#include <OctreeUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <OctreeUtils.h>
|
||||
|
||||
using namespace render;
|
||||
|
||||
// Culling Frustum / solidAngle test helper class
|
||||
struct Test {
|
||||
CullFunctor _functor;
|
||||
RenderArgs* _args;
|
||||
RenderDetails::Item& _renderDetails;
|
||||
ViewFrustumPointer _antiFrustum;
|
||||
glm::vec3 _eyePos;
|
||||
float _squareTanAlpha;
|
||||
|
||||
Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails, ViewFrustumPointer antiFrustum = nullptr) :
|
||||
_functor(functor),
|
||||
_args(pargs),
|
||||
_renderDetails(renderDetails),
|
||||
_antiFrustum(antiFrustum) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
/*_eyePos = _args->getViewFrustum().getPosition();
|
||||
float a = glm::degrees(Octree::getPerspectiveAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
|
||||
auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees
|
||||
angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree
|
||||
auto tanAlpha = tan(angle);
|
||||
_squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
*/
|
||||
}
|
||||
|
||||
bool frustumTest(const AABox& bound) {
|
||||
if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool antiFrustumTest(const AABox& bound) {
|
||||
assert(_antiFrustum);
|
||||
if (_antiFrustum->boxInsideFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solidAngleTest(const AABox& bound) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
//auto eyeToPoint = bound.calcCenter() - _eyePos;
|
||||
//auto boundSize = bound.getDimensions();
|
||||
//float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
//if (test < 0.0f) {
|
||||
if (!_functor(_args, bound)) {
|
||||
_renderDetails._tooSmall++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
|
||||
const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
assert(renderContext->args);
|
||||
|
@ -84,18 +139,21 @@ void FetchSpatialTree::configure(const Config& config) {
|
|||
_lodAngle = config.lodAngle;
|
||||
}
|
||||
|
||||
void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) {
|
||||
void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection) {
|
||||
// start fresh
|
||||
outSelection.clear();
|
||||
|
||||
auto& filter = inputs.get0();
|
||||
auto frustumResolution = inputs.get1();
|
||||
|
||||
if (!filter.selectsNothing()) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
auto& scene = renderContext->_scene;
|
||||
|
||||
// Eventually use a frozen frustum
|
||||
auto queryFrustum = args->getViewFrustum();
|
||||
// Eventually use a frozen frustum
|
||||
if (_freezeFrustum) {
|
||||
if (_justFrozeFrustum) {
|
||||
_justFrozeFrustum = false;
|
||||
|
@ -105,8 +163,19 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Item
|
|||
}
|
||||
|
||||
// Octree selection!
|
||||
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
|
||||
scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle);
|
||||
float threshold = 0.0f;
|
||||
if (queryFrustum.isPerspective()) {
|
||||
threshold = getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust);
|
||||
if (frustumResolution.y > 0) {
|
||||
threshold = glm::max(queryFrustum.getFieldOfView() / frustumResolution.y, threshold);
|
||||
}
|
||||
} else {
|
||||
threshold = getOrthographicAccuracySize(args->_sizeScale, args->_boundaryLevelAdjust);
|
||||
glm::vec2 frustumSize = glm::vec2(queryFrustum.getWidth(), queryFrustum.getHeight());
|
||||
const auto pixelResolution = frustumResolution.x > 0 ? frustumResolution : glm::ivec2(2048, 2048);
|
||||
threshold = glm::max(threshold, glm::min(frustumSize.x / pixelResolution.x, frustumSize.y / pixelResolution.y));
|
||||
}
|
||||
scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,50 +205,6 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
|||
args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one
|
||||
}
|
||||
|
||||
// Culling Frustum / solidAngle test helper class
|
||||
struct Test {
|
||||
CullFunctor _functor;
|
||||
RenderArgs* _args;
|
||||
RenderDetails::Item& _renderDetails;
|
||||
glm::vec3 _eyePos;
|
||||
float _squareTanAlpha;
|
||||
|
||||
Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) :
|
||||
_functor(functor),
|
||||
_args(pargs),
|
||||
_renderDetails(renderDetails)
|
||||
{
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
/*_eyePos = _args->getViewFrustum().getPosition();
|
||||
float a = glm::degrees(Octree::getAccuracyAngle(_args->_sizeScale, _args->_boundaryLevelAdjust));
|
||||
auto angle = std::min(glm::radians(45.0f), a); // no worse than 45 degrees
|
||||
angle = std::max(glm::radians(1.0f / 60.0f), a); // no better than 1 minute of degree
|
||||
auto tanAlpha = tan(angle);
|
||||
_squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
*/
|
||||
}
|
||||
|
||||
bool frustumTest(const AABox& bound) {
|
||||
if (!_args->getViewFrustum().boxIntersectsFrustum(bound)) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solidAngleTest(const AABox& bound) {
|
||||
// FIXME: Keep this code here even though we don't use it yet
|
||||
//auto eyeToPoint = bound.calcCenter() - _eyePos;
|
||||
//auto boundSize = bound.getDimensions();
|
||||
//float test = (glm::dot(boundSize, boundSize) / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
//if (test < 0.0f) {
|
||||
if (!_functor(_args, bound)) {
|
||||
_renderDetails._tooSmall++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Test test(_cullFunctor, args, details);
|
||||
|
||||
// Now we have a selection of items to render
|
||||
|
@ -311,3 +336,146 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext,
|
|||
|
||||
std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size();
|
||||
}
|
||||
|
||||
void CullShapeBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto& inShapes = inputs.get0();
|
||||
const auto& filter = inputs.get1();
|
||||
const auto& antiFrustum = inputs.get2();
|
||||
auto& outShapes = outputs.edit0();
|
||||
auto& outBounds = outputs.edit1();
|
||||
|
||||
outShapes.clear();
|
||||
outBounds = AABox();
|
||||
|
||||
if (!filter.selectsNothing()) {
|
||||
auto& details = args->_details.edit(_detailType);
|
||||
Test test(_cullFunctor, args, details, antiFrustum);
|
||||
|
||||
for (auto& inItems : inShapes) {
|
||||
auto key = inItems.first;
|
||||
auto outItems = outShapes.find(key);
|
||||
if (outItems == outShapes.end()) {
|
||||
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
|
||||
outItems->second.reserve(inItems.second.size());
|
||||
}
|
||||
|
||||
details._considered += (int)inItems.second.size();
|
||||
|
||||
if (antiFrustum == nullptr) {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
|
||||
outItems->second.emplace_back(item);
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
details._rendered += (int)outItems->second.size();
|
||||
}
|
||||
|
||||
for (auto& items : outShapes) {
|
||||
items.second.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
auto& inItems = inputs.get0();
|
||||
auto& outItems = outputs;
|
||||
auto inputFrustum = inputs.get1();
|
||||
|
||||
if (inputFrustum != nullptr) {
|
||||
args->pushViewFrustum(*inputFrustum);
|
||||
}
|
||||
|
||||
outItems.clear();
|
||||
outItems.reserve(inItems.size());
|
||||
|
||||
for (auto& item : inItems) {
|
||||
if (_cullFunctor(args, item.bound)) {
|
||||
outItems.emplace_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputFrustum != nullptr) {
|
||||
args->popViewFrustum();
|
||||
}
|
||||
}
|
||||
|
||||
void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||
const Inputs& inputs, ItemBounds& outItems) {
|
||||
assert(renderContext->args);
|
||||
auto& scene = renderContext->_scene;
|
||||
auto& inSelection = inputs.get0();
|
||||
|
||||
// Now we have a selection of items to render
|
||||
outItems.clear();
|
||||
outItems.reserve(inSelection.numItems());
|
||||
|
||||
const auto filter = inputs.get1();
|
||||
if (!filter.selectsNothing()) {
|
||||
// Now get the bound, and
|
||||
// filter individually against the _filter
|
||||
|
||||
// inside & fit items: filter only
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,14 +53,16 @@ namespace render {
|
|||
bool _justFrozeFrustum{ false };
|
||||
ViewFrustum _frozenFrustum;
|
||||
float _lodAngle;
|
||||
|
||||
public:
|
||||
using Config = FetchSpatialTreeConfig;
|
||||
using JobModel = Job::ModelIO<FetchSpatialTree, ItemFilter, ItemSpatialTree::ItemSelection, Config>;
|
||||
using Inputs = render::VaryingSet2<ItemFilter, glm::ivec2>;
|
||||
using JobModel = Job::ModelIO<FetchSpatialTree, Inputs, ItemSpatialTree::ItemSelection, Config>;
|
||||
|
||||
FetchSpatialTree() {}
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection);
|
||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemSpatialTree::ItemSelection& outSelection);
|
||||
};
|
||||
|
||||
class CullSpatialSelectionConfig : public Job::Config {
|
||||
|
@ -96,7 +98,8 @@ namespace render {
|
|||
_detailType(type) {}
|
||||
|
||||
CullSpatialSelection(CullFunctor cullFunctor) :
|
||||
_cullFunctor{ cullFunctor } {}
|
||||
_cullFunctor{ cullFunctor } {
|
||||
}
|
||||
|
||||
CullFunctor _cullFunctor;
|
||||
RenderDetails::Type _detailType{ RenderDetails::OTHER };
|
||||
|
@ -105,6 +108,51 @@ namespace render {
|
|||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
|
||||
};
|
||||
|
||||
class CullShapeBounds {
|
||||
public:
|
||||
using Inputs = render::VaryingSet3<ShapeBounds, ItemFilter, ViewFrustumPointer>;
|
||||
using Outputs = render::VaryingSet2<ShapeBounds, AABox>;
|
||||
using JobModel = Job::ModelIO<CullShapeBounds, Inputs, Outputs>;
|
||||
|
||||
CullShapeBounds(CullFunctor cullFunctor, RenderDetails::Type type) :
|
||||
_cullFunctor{ cullFunctor },
|
||||
_detailType(type) {}
|
||||
|
||||
CullShapeBounds(CullFunctor cullFunctor) :
|
||||
_cullFunctor{ cullFunctor } {
|
||||
}
|
||||
|
||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
CullFunctor _cullFunctor;
|
||||
RenderDetails::Type _detailType{ RenderDetails::OTHER };
|
||||
|
||||
};
|
||||
|
||||
class FetchSpatialSelection {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<ItemSpatialTree::ItemSelection, ItemFilter>;
|
||||
using JobModel = Job::ModelIO<FetchSpatialSelection, Inputs, ItemBounds>;
|
||||
|
||||
FetchSpatialSelection() {}
|
||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
|
||||
};
|
||||
|
||||
class ApplyCullFunctorOnItemBounds {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<ItemBounds, ViewFrustumPointer>;
|
||||
using Outputs = ItemBounds;
|
||||
using JobModel = Job::ModelIO<ApplyCullFunctorOnItemBounds, Inputs, Outputs>;
|
||||
|
||||
ApplyCullFunctorOnItemBounds(render::CullFunctor cullFunctor) : _cullFunctor(cullFunctor) {}
|
||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
render::CullFunctor _cullFunctor;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_render_CullTask_h;
|
|
@ -148,7 +148,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS
|
|||
}
|
||||
// Draw the LOD Reticle
|
||||
{
|
||||
float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
|
||||
float angle = glm::degrees(getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust));
|
||||
Transform crosshairModel;
|
||||
crosshairModel.setTranslation(glm::vec3(0.0, 0.0, -1000.0));
|
||||
crosshairModel.setScale(1000.0f * tanf(glm::radians(angle))); // Scaling at the actual tan of the lod angle => Multiplied by TWO
|
||||
|
|
|
@ -24,7 +24,8 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin
|
|||
// Fetch and cull the items from the scene
|
||||
const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered().withTagBits(tagBits, tagMask);
|
||||
const auto spatialFilter = render::Varying(filter);
|
||||
const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", spatialFilter);
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(filter, glm::ivec2(0,0)).asVarying();
|
||||
const auto spatialSelection = task.addJob<FetchSpatialTree>("FetchSceneSelection", fetchInput);
|
||||
const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying();
|
||||
const auto culledSpatialSelection = task.addJob<CullSpatialSelection>("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM);
|
||||
|
||||
|
|
|
@ -12,9 +12,26 @@
|
|||
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
|
||||
using namespace render;
|
||||
|
||||
void Octree::PerspectiveSelector::setAngle(float a) {
|
||||
const float MAX_LOD_ANGLE = glm::radians(45.0f);
|
||||
const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f);
|
||||
|
||||
angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a));
|
||||
auto tanAlpha = tan(angle);
|
||||
squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
}
|
||||
|
||||
float Octree::PerspectiveSelector::testThreshold(const Coord3f& point, float size) const {
|
||||
auto eyeToPoint = point - eyePos;
|
||||
return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
}
|
||||
|
||||
float Octree::OrthographicSelector::testThreshold(const Coord3f& point, float size) const {
|
||||
return (size * size) - squareMinSize;
|
||||
}
|
||||
|
||||
|
||||
const float Octree::INV_DEPTH_DIM[] = {
|
||||
1.0f,
|
||||
|
@ -520,10 +537,9 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum
|
|||
|
||||
// Test for lod
|
||||
auto cellLocation = cell.getlocation();
|
||||
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (lod < 0.0f) {
|
||||
float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (test < 0.0f) {
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Select this cell partially in frustum
|
||||
|
@ -543,13 +559,13 @@ int Octree::selectTraverse(Index cellID, CellSelection& selection, const Frustum
|
|||
}
|
||||
|
||||
|
||||
int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
|
||||
int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
|
||||
int numSelectedsIn = (int) selection.size();
|
||||
auto cell = getConcreteCell(cellID);
|
||||
|
||||
auto cellLocation = cell.getlocation();
|
||||
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (lod < 0.0f) {
|
||||
float test = selector.testThreshold(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (test < 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -580,24 +596,40 @@ int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside)
|
|||
return (int) selection.size() - numSelectedsIn;
|
||||
}
|
||||
|
||||
|
||||
int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const {
|
||||
int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const {
|
||||
auto worldPlanes = frustum.getPlanes();
|
||||
FrustumSelector selector;
|
||||
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
|
||||
::Plane octPlane;
|
||||
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
|
||||
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
|
||||
if (frustum.isPerspective()) {
|
||||
PerspectiveSelector selector;
|
||||
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
|
||||
::Plane octPlane;
|
||||
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
|
||||
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
|
||||
}
|
||||
|
||||
selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH);
|
||||
selector.setAngle(threshold);
|
||||
|
||||
return Octree::select(selection, selector);
|
||||
} else {
|
||||
OrthographicSelector selector;
|
||||
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
|
||||
::Plane octPlane;
|
||||
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
|
||||
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
|
||||
}
|
||||
|
||||
// Divide the threshold (which is in world distance units) by the dimension of the octree
|
||||
// as all further computations will be done in normalized octree units
|
||||
threshold *= getInvCellWidth(ROOT_DEPTH);
|
||||
selector.setSize(threshold);
|
||||
|
||||
return Octree::select(selection, selector);
|
||||
}
|
||||
|
||||
selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH);
|
||||
selector.setAngle(glm::radians(lodAngle));
|
||||
|
||||
return Octree::select(selection, selector);
|
||||
}
|
||||
|
||||
int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const {
|
||||
selectCells(selection.cellSelection, frustum, lodAngle);
|
||||
int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum,
|
||||
float threshold) const {
|
||||
selectCells(selection.cellSelection, frustum, threshold);
|
||||
|
||||
// Just grab the items in every selected bricks
|
||||
for (auto brickId : selection.cellSelection.insideBricks) {
|
||||
|
|
|
@ -312,23 +312,27 @@ namespace render {
|
|||
class FrustumSelector {
|
||||
public:
|
||||
Coord4f frustum[6];
|
||||
|
||||
virtual ~FrustumSelector() {}
|
||||
virtual float testThreshold(const Coord3f& point, float size) const = 0;
|
||||
};
|
||||
|
||||
class PerspectiveSelector : public FrustumSelector {
|
||||
public:
|
||||
Coord3f eyePos;
|
||||
float angle;
|
||||
float squareTanAlpha;
|
||||
|
||||
const float MAX_LOD_ANGLE = glm::radians(45.0f);
|
||||
const float MIN_LOD_ANGLE = glm::radians(1.0f / 60.0f);
|
||||
void setAngle(float a);
|
||||
float testThreshold(const Coord3f& point, float size) const override;
|
||||
};
|
||||
|
||||
void setAngle(float a) {
|
||||
angle = std::max(MIN_LOD_ANGLE, std::min(MAX_LOD_ANGLE, a));
|
||||
auto tanAlpha = tan(angle);
|
||||
squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
}
|
||||
class OrthographicSelector : public FrustumSelector {
|
||||
public:
|
||||
float squareMinSize;
|
||||
|
||||
float testSolidAngle(const Coord3f& point, float size) const {
|
||||
auto eyeToPoint = point - eyePos;
|
||||
return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
}
|
||||
void setSize(float a) { squareMinSize = a * a; }
|
||||
float testThreshold(const Coord3f& point, float size) const override;
|
||||
};
|
||||
|
||||
int select(CellSelection& selection, const FrustumSelector& selector) const;
|
||||
|
@ -443,7 +447,7 @@ namespace render {
|
|||
Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey);
|
||||
|
||||
// Selection and traverse
|
||||
int selectCells(CellSelection& selection, const ViewFrustum& frustum, float lodAngle) const;
|
||||
int selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const;
|
||||
|
||||
class ItemSelection {
|
||||
public:
|
||||
|
@ -469,7 +473,8 @@ namespace render {
|
|||
}
|
||||
};
|
||||
|
||||
int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum, float lodAngle) const;
|
||||
int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum,
|
||||
float threshold) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -280,6 +280,18 @@ bool ViewFrustum::boxIntersectsFrustum(const AABox& box) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ViewFrustum::boxInsideFrustum(const AABox& box) const {
|
||||
// only check against frustum
|
||||
for (int i = 0; i < NUM_FRUSTUM_PLANES; i++) {
|
||||
const glm::vec3& normal = _planes[i].getNormal();
|
||||
// check distance to nearest box point
|
||||
if (_planes[i].distance(box.getNearestVertex(normal)) < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewFrustum::sphereIntersectsKeyhole(const glm::vec3& center, float radius) const {
|
||||
// check positive touch against central sphere
|
||||
if (glm::length(center - _position) <= (radius + _centerSphereRadius)) {
|
||||
|
@ -847,3 +859,7 @@ void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8
|
|||
triangle.v2 = points[vertexIndices[2]];
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewFrustum::isPerspective() const {
|
||||
return _projection[3][2] != 0.0f && _projection[2][3] != 0.0f && _projection[3][3] == 0.0f;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
// setters for lens attributes
|
||||
void setProjection(const glm::mat4 & projection);
|
||||
void setFocalLength(float focalLength) { _focalLength = focalLength; }
|
||||
bool isPerspective() const;
|
||||
|
||||
// getters for lens attributes
|
||||
const glm::mat4& getProjection() const { return _projection; }
|
||||
|
@ -103,6 +104,7 @@ public:
|
|||
bool sphereIntersectsFrustum(const glm::vec3& center, float radius) const;
|
||||
bool cubeIntersectsFrustum(const AACube& box) const;
|
||||
bool boxIntersectsFrustum(const AABox& box) const;
|
||||
bool boxInsideFrustum(const AABox& box) const;
|
||||
|
||||
bool sphereIntersectsKeyhole(const glm::vec3& center, float radius) const;
|
||||
bool cubeIntersectsKeyhole(const AACube& cube) const;
|
||||
|
|
|
@ -14,7 +14,7 @@ var qml = Script.resolvePath('shadow.qml');
|
|||
var window = new OverlayWindow({
|
||||
title: 'Shadow Debug',
|
||||
source: qml,
|
||||
width: 200,
|
||||
height: 90
|
||||
width: 250,
|
||||
height: 300
|
||||
});
|
||||
window.closed.connect(function() { Script.stop(); });
|
|
@ -10,11 +10,15 @@
|
|||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
import "configSlider"
|
||||
|
||||
Column {
|
||||
id: root
|
||||
spacing: 8
|
||||
property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum");
|
||||
property var shadowConfig : Render.getConfig("RenderMainView.ShadowSetup");
|
||||
property var shadow0Config: Render.getConfig("RenderMainView.DrawShadowFrustum0");
|
||||
property var shadow1Config: Render.getConfig("RenderMainView.DrawShadowFrustum1");
|
||||
property var shadow2Config: Render.getConfig("RenderMainView.DrawShadowFrustum2");
|
||||
|
@ -33,6 +37,14 @@ Column {
|
|||
shadow1Config.enabled = false;
|
||||
shadow2Config.enabled = false;
|
||||
shadow3Config.enabled = false;
|
||||
shadow0Config.isFrozen = false;
|
||||
shadow1Config.isFrozen = false;
|
||||
shadow2Config.isFrozen = false;
|
||||
shadow3Config.isFrozen = false;
|
||||
shadow0BoundConfig.isFrozen = false;
|
||||
shadow1BoundConfig.isFrozen = false;
|
||||
shadow2BoundConfig.isFrozen = false;
|
||||
shadow3BoundConfig.isFrozen = false;
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
|
@ -68,4 +80,69 @@ Column {
|
|||
font.italic: true
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 0 constant bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "constantBias0"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 1 constant bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "constantBias1"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 2 constant bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "constantBias2"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 3 constant bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "constantBias3"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 0 slope bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "slopeBias0"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 1 slope bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "slopeBias1"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 2 slope bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "slopeBias2"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
ConfigSlider {
|
||||
label: qsTr("Cascade 3 slope bias")
|
||||
integral: false
|
||||
config: shadowConfig
|
||||
property: "slopeBias3"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue