Moved shadow cascade distances computation to shadow, not shadow task

This commit is contained in:
Olivier Prat 2017-12-06 10:05:39 +01:00
parent e16b427ab6
commit 1d8d8335c5
4 changed files with 76 additions and 53 deletions

View file

@ -27,6 +27,8 @@
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
static const unsigned int SUN_SHADOW_CASCADE_COUNT{ 4 };
static const float SUN_SHADOW_MAX_DISTANCE{ 40.0f };
using namespace render;
using namespace render::entities;
@ -116,7 +118,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, LightStage::SUN_SHADOW_CASCADE_COUNT);
_shadowIndex = _stage->addShadow(_sunIndex, SUN_SHADOW_MAX_DISTANCE, SUN_SHADOW_CASCADE_COUNT);
} else {
_stage->updateLightArrayBuffer(_sunIndex);
}

View file

@ -23,7 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
0.5, 0.5, 0.5, 1.0 };
const int LightStage::Shadow::MAP_SIZE = 1024;
const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 };
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
@ -40,7 +39,10 @@ LightStage::Shadow::Schema::Schema() {
maxDistance = 20.0f;
}
LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared<ViewFrustum>() } {
LightStage::Shadow::Cascade::Cascade() :
_frustum{ std::make_shared<ViewFrustum>() },
_minDistance{ 0.0f },
_maxDistance{ 20.0f } {
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
map = framebuffer->getDepthStencilBuffer();
}
@ -88,21 +90,66 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru
return far;
}
LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } {
LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount) :
_light{ light } {
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
Schema schema;
schema.cascadeCount = cascadeCount;
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
_cascades.resize(cascadeCount);
setMaxDistance(maxDistance);
}
void LightStage::Shadow::setMaxDistance(float value) {
_maxDistance = std::max(0.0f, value);
// Distribute the cascades along that distance
// TODO : these parameters should be exposed to the user as part of the light entity parameters, no?
static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f;
// Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a
// tighter distribution around the view position.
static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f;
if (_cascades.size() == 1) {
_cascades.front().setMinDistance(0.0f);
_cascades.front().setMaxDistance(_maxDistance);
} else {
for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) {
float maxCascadeDistance;
float minCascadeDistance;
float shadowOverlapDistance;
const auto deltaCascadeMaxDistance = (_maxDistance - LOW_CASCADE_MAX_DISTANCE);
const auto maxAlpha = powf(cascadeIndex / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS);
const auto minAlpha = powf(std::max<float>(cascadeIndex - 1, 0) / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS);
maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha;
minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha;
if (cascadeIndex == 0) {
minCascadeDistance = 0.0f;
} else {
minCascadeDistance = std::max(minCascadeDistance, 0.0f);
}
shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f;
maxCascadeDistance += shadowOverlapDistance;
_cascades[cascadeIndex].setMinDistance(minCascadeDistance);
_cascades[cascadeIndex].setMaxDistance(maxCascadeDistance);
}
}
// Update the buffer
const auto& lastCascade = _cascades.back();
auto& schema = _schemaBuffer.edit<Schema>();
schema.maxDistance = _maxDistance;
schema.invFalloffDistance = 3.0f / (lastCascade.getMaxDistance() - lastCascade.getMinDistance());
}
void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance,
float viewCascadeOverlapDistance, float viewMaxShadowDistance,
float nearDepth, float farDepth) {
assert(viewMinCascadeShadowDistance < viewMaxCascadeShadowDistance);
assert(nearDepth < farDepth);
assert(viewCascadeOverlapDistance > 0.0f);
assert(cascadeIndex < _cascades.size());
// Orient the keylight frustum
@ -119,6 +166,9 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie
}
auto& cascade = _cascades[cascadeIndex];
const auto viewMinCascadeShadowDistance = std::max(viewFrustum.getNearClip(), cascade.getMinDistance());
const auto viewMaxCascadeShadowDistance = std::min(viewFrustum.getFarClip(), cascade.getMaxDistance());
const auto viewMaxShadowDistance = _cascades.back().getMaxDistance();
cascade._frustum->setOrientation(orientation);
@ -165,10 +215,6 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie
// Update the buffer
auto& schema = _schemaBuffer.edit<Schema>();
if (cascadeIndex == getCascadeCount() - 1) {
schema.maxDistance = viewMaxCascadeShadowDistance;
schema.invFalloffDistance = 1.0f / viewCascadeOverlapDistance;
}
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
}
@ -218,12 +264,12 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
}
}
LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) {
LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) {
auto light = getLight(lightIndex);
Index shadowId = INVALID_INDEX;
if (light) {
assert(_descs[lightIndex].shadowId == INVALID_INDEX);
shadowId = _shadows.newElement(std::make_shared<Shadow>(light, cascadeCount));
shadowId = _shadows.newElement(std::make_shared<Shadow>(light, maxDistance, cascadeCount));
_descs[lightIndex].shadowId = shadowId;
}
return shadowId;

View file

@ -31,8 +31,6 @@ 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;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
@ -62,19 +60,24 @@ public:
const glm::mat4& getView() const;
const glm::mat4& getProjection() const;
void setMinDistance(float value) { _minDistance = value; }
void setMaxDistance(float value) { _maxDistance = value; }
float getMinDistance() const { return _minDistance; }
float getMaxDistance() const { return _maxDistance; }
private:
std::shared_ptr<ViewFrustum> _frustum;
float _minDistance;
float _maxDistance;
float computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse,
float left, float right, float bottom, float top, float viewMaxShadowDistance) const;
};
Shadow(model::LightPointer light, unsigned int cascadeCount = 1);
Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1);
void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance,
float viewCascadeOverlapDistance, float viewMaxShadowDistance,
float nearDepth = 1.0f, float farDepth = 1000.0f);
void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
@ -83,6 +86,9 @@ public:
unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); }
const Cascade& getCascade(unsigned int index) const { return _cascades[index]; }
float getMaxDistance() const { return _maxDistance; }
void setMaxDistance(float value);
const model::LightPointer& getLight() const { return _light; }
protected:
@ -94,6 +100,7 @@ public:
static const glm::mat4 _biasMatrix;
model::LightPointer _light;
float _maxDistance;
Cascades _cascades;
class Schema : public ShadowParameters {
@ -111,7 +118,7 @@ public:
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U);
Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U);
LightPointer removeLight(Index index);

View file

@ -237,39 +237,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
const auto nearClip = args->getViewFrustum().getNearClip();
const auto farClip = args->getViewFrustum().getFarClip();
// TODO : these parameters should be exposed to the user as part of the light entity parameters, no?
static const auto HIGH_CASCADE_MAX_DISTANCE = 40.0f;
static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f;
// Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a
// tighter distribution around the view position.
static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f;
float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE;
float minCascadeDistance = nearClip;
float shadowOverlapDistance = 0.0f;
if (globalShadow->getCascadeCount() > 1) {
const auto deltaCascadeMaxDistance = (HIGH_CASCADE_MAX_DISTANCE - LOW_CASCADE_MAX_DISTANCE);
const auto maxAlpha = powf(_cascadeIndex / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS);
const auto minAlpha = powf(std::max<float>(_cascadeIndex-1, 0) / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS);
maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha;
minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha;
}
if (_cascadeIndex == 0) {
minCascadeDistance = nearClip;
} else {
minCascadeDistance = std::max(minCascadeDistance, nearClip);
}
shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f;
maxCascadeDistance += shadowOverlapDistance;
maxCascadeDistance = std::min(maxCascadeDistance, farClip);
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance,
shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
// Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));