Exposed max shadow distance and a shadow bias scale parameter in graphics::Light

This commit is contained in:
Olivier Prat 2019-04-18 11:06:12 +02:00
parent 3fc711dba2
commit 3cb9c518ea
5 changed files with 87 additions and 59 deletions

View file

@ -73,6 +73,22 @@ bool Light::getCastShadows() const {
return _castShadows; return _castShadows;
} }
void Light::setShadowsMaxDistance(const float maxDistance) {
_shadowsMaxDistance = std::max(0.0f, maxDistance);
}
float Light::getShadowsMaxDistance() const {
return _shadowsMaxDistance;
}
void Light::setShadowsBiasScale(const float scale) {
_shadowsBiasScale = std::max(0.0f, scale);
}
float Light::getShadowsBiasScale() const {
return _shadowsBiasScale;
}
void Light::setColor(const Color& color) { void Light::setColor(const Color& color) {
_lightSchemaBuffer.edit().irradiance.color = color; _lightSchemaBuffer.edit().irradiance.color = color;
updateLightRadius(); updateLightRadius();

View file

@ -106,6 +106,12 @@ public:
void setCastShadows(const bool castShadows); void setCastShadows(const bool castShadows);
bool getCastShadows() const; bool getCastShadows() const;
void setShadowsMaxDistance(const float maxDistance);
float getShadowsMaxDistance() const;
void setShadowsBiasScale(const float scale);
float getShadowsBiasScale() const;
void setOrientation(const Quat& orientation); void setOrientation(const Quat& orientation);
const glm::quat& getOrientation() const { return _transform.getRotation(); } const glm::quat& getOrientation() const { return _transform.getRotation(); }
@ -192,10 +198,11 @@ protected:
Type _type { SUN }; Type _type { SUN };
float _spotCos { -1.0f }; // stored here to be able to reset the spot angle when turning the type spot on/off float _spotCos { -1.0f }; // stored here to be able to reset the spot angle when turning the type spot on/off
void updateLightRadius(); float _shadowsMaxDistance{ 40.0f };
float _shadowsBiasScale{ 1.0f };
bool _castShadows{ false }; bool _castShadows{ false };
void updateLightRadius();
}; };
typedef std::shared_ptr< Light > LightPointer; typedef std::shared_ptr< Light > LightPointer;

View file

@ -120,7 +120,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru
return far; return far;
} }
LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount) : LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) :
_light{ light } { _light{ light } {
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
Schema schema; Schema schema;
@ -149,70 +149,77 @@ LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsi
cascade.framebuffer->setDepthBuffer(map, depthFormat, cascadeIndex); cascade.framebuffer->setDepthBuffer(map, depthFormat, cascadeIndex);
} }
setMaxDistance(maxDistance); if (light) {
setMaxDistance(light->getShadowsMaxDistance());
}
} }
void LightStage::Shadow::setLight(graphics::LightPointer light) { void LightStage::Shadow::setLight(graphics::LightPointer light) {
_light = light; _light = light;
if (light) {
setMaxDistance(light->getShadowsMaxDistance());
}
} }
void LightStage::Shadow::setMaxDistance(float value) { void LightStage::Shadow::setMaxDistance(float value) {
// This overlaping factor isn't really used directly for blending of shadow cascades. It value = std::max(1e-3f, value);
// just there to be sure the cascades do overlap. The blending width used is relative if (value != _maxDistance) {
// to the UV space and is set in the Schema with invCascadeBlendWidth. // This overlaping factor isn't really used directly for blending of shadow cascades. It's
static const auto OVERLAP_FACTOR = 1.0f / 5.0f; // just there to be sure the cascades do overlap. The blending width used is relative
// to the UV space and is set in the Schema with invCascadeBlendWidth.
static const auto OVERLAP_FACTOR = 1.0f / 5.0f;
_maxDistance = std::max(0.0f, value); _maxDistance = value;
if (_cascades.size() == 1) { if (_cascades.size() == 1) {
_cascades.front().setMinDistance(0.0f); _cascades.front().setMinDistance(0.0f);
_cascades.front().setMaxDistance(_maxDistance); _cascades.front().setMaxDistance(_maxDistance);
} else { } else {
// Distribute the cascades along that distance // Distribute the cascades along that distance
// TODO : these parameters should be exposed to the user as part of the light entity parameters, no? // TODO : these parameters should be exposed to the user as part of the light entity parameters, no?
static const auto LOW_MAX_DISTANCE = 2.0f; static const auto LOW_MAX_DISTANCE = 2.0f;
static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions
// The max cascade distance is computed by multiplying the previous cascade's max distance by a certain // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain
// factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow
// and an optimal one based on the global min and max shadow distance, all cascades considered. The final // and an optimal one based on the global min and max shadow distance, all cascades considered. The final
// distance is a gradual blend between the two // distance is a gradual blend between the two
const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS);
const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1));
float maxCascadeUserDistance = LOW_MAX_DISTANCE; float maxCascadeUserDistance = LOW_MAX_DISTANCE;
float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; float maxCascadeOptimalDistance = LOW_MAX_DISTANCE;
float minCascadeDistance = 0.0f; float minCascadeDistance = 0.0f;
for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) {
float blendFactor = cascadeIndex / float(_cascades.size() - 1); float blendFactor = cascadeIndex / float(_cascades.size() - 1);
float maxCascadeDistance; float maxCascadeDistance;
if (cascadeIndex == size_t(_cascades.size() - 1)) { if (cascadeIndex == size_t(_cascades.size() - 1)) {
maxCascadeDistance = _maxDistance; maxCascadeDistance = _maxDistance;
} else { } else {
maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor;
}
float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR;
_cascades[cascadeIndex].setMinDistance(minCascadeDistance);
_cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance);
// Compute distances for next cascade
minCascadeDistance = maxCascadeDistance;
maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale;
maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale;
maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance);
} }
float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR;
_cascades[cascadeIndex].setMinDistance(minCascadeDistance);
_cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance);
// Compute distances for next cascade
minCascadeDistance = maxCascadeDistance;
maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale;
maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale;
maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance);
} }
}
// Update the buffer // Update the buffer
const auto& lastCascade = _cascades.back(); const auto& lastCascade = _cascades.back();
auto& schema = _schemaBuffer.edit<Schema>(); auto& schema = _schemaBuffer.edit<Schema>();
schema.maxDistance = _maxDistance; schema.maxDistance = _maxDistance;
schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance());
}
} }
void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,

View file

@ -74,7 +74,7 @@ public:
float left, float right, float bottom, float top, float viewMaxShadowDistance) const; float left, float right, float bottom, float top, float viewMaxShadowDistance) const;
}; };
Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); Shadow(graphics::LightPointer light, unsigned int cascadeCount = 1);
void setLight(graphics::LightPointer light); void setLight(graphics::LightPointer light);
@ -104,16 +104,14 @@ public:
}; };
protected: protected:
using Cascades = std::vector<Cascade>; using Cascades = std::vector<Cascade>;
static const glm::mat4 _biasMatrix; static const glm::mat4 _biasMatrix;
graphics::LightPointer _light; graphics::LightPointer _light;
float _maxDistance; float _maxDistance{ 0.0f };
Cascades _cascades; Cascades _cascades;
UniformBufferView _schemaBuffer = nullptr; UniformBufferView _schemaBuffer = nullptr;
}; };

View file

@ -34,7 +34,6 @@
#define SHADOW_FRUSTUM_NEAR 1.0f #define SHADOW_FRUSTUM_NEAR 1.0f
#define SHADOW_FRUSTUM_FAR 500.0f #define SHADOW_FRUSTUM_FAR 500.0f
static const unsigned int SHADOW_CASCADE_COUNT{ 4 }; static const unsigned int SHADOW_CASCADE_COUNT{ 4 };
static const float SHADOW_MAX_DISTANCE{ 40.0f };
using namespace render; using namespace render;
@ -367,7 +366,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c
output.edit2() = _cameraFrustum; output.edit2() = _cameraFrustum;
if (!_globalShadowObject) { if (!_globalShadowObject) {
_globalShadowObject = std::make_shared<LightStage::Shadow>(graphics::LightPointer(), SHADOW_MAX_DISTANCE, SHADOW_CASCADE_COUNT); _globalShadowObject = std::make_shared<LightStage::Shadow>(currentKeyLight, SHADOW_CASCADE_COUNT);
} }
_globalShadowObject->setLight(currentKeyLight); _globalShadowObject->setLight(currentKeyLight);
@ -378,11 +377,12 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c
unsigned int cascadeIndex; unsigned int cascadeIndex;
// Adjust each cascade frustum // Adjust each cascade frustum
const auto biasScale = currentKeyLight->getShadowsBiasScale();
for (cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) { for (cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) {
auto& bias = _bias[cascadeIndex]; auto& bias = _bias[cascadeIndex];
_globalShadowObject->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(), _globalShadowObject->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(),
SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
bias._constant, bias._slope); bias._constant, bias._slope * biasScale);
} }
_shadowFrameCache->pushShadow(_globalShadowObject); _shadowFrameCache->pushShadow(_globalShadowObject);