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;
}
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) {
_lightSchemaBuffer.edit().irradiance.color = color;
updateLightRadius();

View file

@ -106,6 +106,12 @@ public:
void setCastShadows(const bool castShadows);
bool getCastShadows() const;
void setShadowsMaxDistance(const float maxDistance);
float getShadowsMaxDistance() const;
void setShadowsBiasScale(const float scale);
float getShadowsBiasScale() const;
void setOrientation(const Quat& orientation);
const glm::quat& getOrientation() const { return _transform.getRotation(); }
@ -192,10 +198,11 @@ protected:
Type _type { SUN };
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 };
void updateLightRadius();
};
typedef std::shared_ptr< Light > LightPointer;

View file

@ -120,7 +120,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru
return far;
}
LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount) :
LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) :
_light{ light } {
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
Schema schema;
@ -149,70 +149,77 @@ LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsi
cascade.framebuffer->setDepthBuffer(map, depthFormat, cascadeIndex);
}
setMaxDistance(maxDistance);
if (light) {
setMaxDistance(light->getShadowsMaxDistance());
}
}
void LightStage::Shadow::setLight(graphics::LightPointer light) {
_light = light;
if (light) {
setMaxDistance(light->getShadowsMaxDistance());
}
}
void LightStage::Shadow::setMaxDistance(float value) {
// This overlaping factor isn't really used directly for blending of shadow cascades. It
// 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;
value = std::max(1e-3f, value);
if (value != _maxDistance) {
// This overlaping factor isn't really used directly for blending of shadow cascades. It's
// 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) {
_cascades.front().setMinDistance(0.0f);
_cascades.front().setMaxDistance(_maxDistance);
} else {
// 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_MAX_DISTANCE = 2.0f;
static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions
if (_cascades.size() == 1) {
_cascades.front().setMinDistance(0.0f);
_cascades.front().setMaxDistance(_maxDistance);
} else {
// 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_MAX_DISTANCE = 2.0f;
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
// 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
// distance is a gradual blend between the two
const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS);
const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1));
// 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
// 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
const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS);
const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1));
float maxCascadeUserDistance = LOW_MAX_DISTANCE;
float maxCascadeOptimalDistance = LOW_MAX_DISTANCE;
float minCascadeDistance = 0.0f;
float maxCascadeUserDistance = LOW_MAX_DISTANCE;
float maxCascadeOptimalDistance = LOW_MAX_DISTANCE;
float minCascadeDistance = 0.0f;
for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) {
float blendFactor = cascadeIndex / float(_cascades.size() - 1);
float maxCascadeDistance;
for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) {
float blendFactor = cascadeIndex / float(_cascades.size() - 1);
float maxCascadeDistance;
if (cascadeIndex == size_t(_cascades.size() - 1)) {
maxCascadeDistance = _maxDistance;
} else {
maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor;
if (cascadeIndex == size_t(_cascades.size() - 1)) {
maxCascadeDistance = _maxDistance;
} else {
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
const auto& lastCascade = _cascades.back();
auto& schema = _schemaBuffer.edit<Schema>();
schema.maxDistance = _maxDistance;
schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance());
// Update the buffer
const auto& lastCascade = _cascades.back();
auto& schema = _schemaBuffer.edit<Schema>();
schema.maxDistance = _maxDistance;
schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance());
}
}
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;
};
Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount = 1);
Shadow(graphics::LightPointer light, unsigned int cascadeCount = 1);
void setLight(graphics::LightPointer light);
@ -104,16 +104,14 @@ public:
};
protected:
using Cascades = std::vector<Cascade>;
static const glm::mat4 _biasMatrix;
graphics::LightPointer _light;
float _maxDistance;
float _maxDistance{ 0.0f };
Cascades _cascades;
UniformBufferView _schemaBuffer = nullptr;
};

View file

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