mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 16:36:54 +02:00
Much better cascade blend
This commit is contained in:
parent
917ce7165c
commit
b246c479e3
7 changed files with 74 additions and 75 deletions
|
@ -151,21 +151,23 @@ static const std::string DEFAULT_SHADOW_SHADER{
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
|
static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
|
||||||
"vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );"
|
"vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(1) );"
|
||||||
"vec4 getFragmentColor() {"
|
"vec4 getFragmentColor() {"
|
||||||
" DeferredFrameTransform deferredTransform = getDeferredFrameTransform();"
|
" DeferredFrameTransform deferredTransform = getDeferredFrameTransform();"
|
||||||
" DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);"
|
" DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);"
|
||||||
" vec4 viewPosition = vec4(frag.position.xyz, 1.0);"
|
" vec4 viewPosition = vec4(frag.position.xyz, 1.0);"
|
||||||
|
" float viewDepth = -viewPosition.z;"
|
||||||
" vec4 worldPosition = getViewInverse() * viewPosition;"
|
" vec4 worldPosition = getViewInverse() * viewPosition;"
|
||||||
" vec4 cascadeShadowCoords[4] = vec4[4]("
|
" vec4 cascadeShadowCoords[2];"
|
||||||
" evalShadowTexcoord(0, worldPosition),"
|
|
||||||
" evalShadowTexcoord(1, worldPosition),"
|
|
||||||
" evalShadowTexcoord(2, worldPosition),"
|
|
||||||
" evalShadowTexcoord(3, worldPosition)"
|
|
||||||
" );"
|
|
||||||
" ivec2 cascadeIndices;"
|
" ivec2 cascadeIndices;"
|
||||||
" float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);"
|
" float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);"
|
||||||
" return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);"
|
" vec3 firstCascadeColor = cascadeColors[cascadeIndices.x];"
|
||||||
|
" vec3 secondCascadeColor = cascadeColors[cascadeIndices.x];"
|
||||||
|
" if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {"
|
||||||
|
" secondCascadeColor = cascadeColors[cascadeIndices.y];"
|
||||||
|
" }"
|
||||||
|
" vec3 color = mix(firstCascadeColor, secondCascadeColor, cascadeMix);"
|
||||||
|
" return vec4(mix(vec3(0.0), color, evalShadowFalloff(viewDepth)), 1.0);"
|
||||||
"}"
|
"}"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,9 @@ LightStage::Shadow::Schema::Schema() {
|
||||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||||
invMapSize = 1.0f / MAP_SIZE;
|
invMapSize = 1.0f / MAP_SIZE;
|
||||||
cascadeCount = 1;
|
cascadeCount = 1;
|
||||||
|
invCascadeBlendWidth = 1.0f / 0.1f;
|
||||||
|
invFalloffDistance = 1.0f / 2.0f;
|
||||||
|
maxDistance = 20.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared<ViewFrustum>() } {
|
LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared<ViewFrustum>() } {
|
||||||
|
@ -52,11 +55,10 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const {
|
||||||
|
|
||||||
LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } {
|
LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _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;
|
||||||
|
schema.cascadeCount = cascadeCount;
|
||||||
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
|
||||||
_cascades.resize(cascadeCount);
|
_cascades.resize(cascadeCount);
|
||||||
_schemaBuffer.edit<Schema>().cascadeCount = cascadeCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||||
|
@ -125,12 +127,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie
|
||||||
cascade._frustum->calculate();
|
cascade._frustum->calculate();
|
||||||
|
|
||||||
// Update the buffer
|
// Update the buffer
|
||||||
auto& schemaCascade = _schemaBuffer.edit<Schema>().cascades[cascadeIndex];
|
auto& schema = _schemaBuffer.edit<Schema>();
|
||||||
schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix();
|
if (cascadeIndex == getCascadeCount() - 1) {
|
||||||
schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance;
|
schema.maxDistance = viewMaxShadowDistance;
|
||||||
schemaCascade.maxDistance = viewMaxShadowDistance;
|
schema.invFalloffDistance = 1.0f / viewOverlapDistance;
|
||||||
cascade.minDistance = viewMinShadowDistance;
|
}
|
||||||
cascade.maxDistance = viewMaxShadowDistance;
|
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||||
|
|
|
@ -56,8 +56,6 @@ public:
|
||||||
|
|
||||||
gpu::FramebufferPointer framebuffer;
|
gpu::FramebufferPointer framebuffer;
|
||||||
gpu::TexturePointer map;
|
gpu::TexturePointer map;
|
||||||
float minDistance;
|
|
||||||
float maxDistance;
|
|
||||||
|
|
||||||
const std::shared_ptr<ViewFrustum>& getFrustum() const { return _frustum; }
|
const std::shared_ptr<ViewFrustum>& getFrustum() const { return _frustum; }
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,6 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
|
||||||
|
|
||||||
const auto nearClip = args->getViewFrustum().getNearClip();
|
const auto nearClip = args->getViewFrustum().getNearClip();
|
||||||
const auto farClip = args->getViewFrustum().getFarClip();
|
const auto farClip = args->getViewFrustum().getFarClip();
|
||||||
const auto nearDepth = -args->_boomOffset.z;
|
|
||||||
|
|
||||||
static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f;
|
static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f;
|
||||||
float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE;
|
float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE;
|
||||||
|
@ -266,7 +265,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
|
||||||
float shadowOverlapDistance = 0.0f;
|
float shadowOverlapDistance = 0.0f;
|
||||||
|
|
||||||
if (globalShadow->getCascadeCount() > 1) {
|
if (globalShadow->getCascadeCount() > 1) {
|
||||||
static const float LOW_CASCADE_MAX_DISTANCE = 2.0f;
|
static const float LOW_CASCADE_MAX_DISTANCE = 3.0f;
|
||||||
const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1));
|
const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1));
|
||||||
|
|
||||||
maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex);
|
maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex);
|
||||||
|
@ -276,9 +275,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
|
||||||
shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f;
|
shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f;
|
||||||
maxCascadeDistance += shadowOverlapDistance;
|
maxCascadeDistance += shadowOverlapDistance;
|
||||||
if (_cascadeIndex == 0) {
|
if (_cascadeIndex == 0) {
|
||||||
minCascadeDistance = nearDepth;
|
minCascadeDistance = nearClip;
|
||||||
}
|
}
|
||||||
minCascadeDistance = std::max(minCascadeDistance, nearDepth);
|
minCascadeDistance = std::max(minCascadeDistance, nearClip);
|
||||||
maxCascadeDistance = std::min(maxCascadeDistance, farClip);
|
maxCascadeDistance = std::min(maxCascadeDistance, farClip);
|
||||||
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,7 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
||||||
if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 ||
|
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
||||||
shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 ||
|
|
||||||
shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) {
|
|
||||||
// If a point is not in the map, do not attenuate
|
// If a point is not in the map, do not attenuate
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
@ -95,21 +93,18 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets
|
||||||
|
|
||||||
float evalShadowAttenuation(vec4 worldPosition, float viewDepth) {
|
float evalShadowAttenuation(vec4 worldPosition, float viewDepth) {
|
||||||
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
||||||
vec4 cascadeShadowCoords[4] = vec4[4] (
|
vec4 cascadeShadowCoords[2];
|
||||||
evalShadowTexcoord(0, worldPosition),
|
|
||||||
evalShadowTexcoord(1, worldPosition),
|
|
||||||
evalShadowTexcoord(2, worldPosition),
|
|
||||||
evalShadowTexcoord(3, worldPosition)
|
|
||||||
);
|
|
||||||
ivec2 cascadeIndices;
|
ivec2 cascadeIndices;
|
||||||
float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices);
|
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||||
|
|
||||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
||||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]);
|
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0]);
|
||||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]);
|
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1]);
|
||||||
}
|
}
|
||||||
return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||||
|
// Falloff to max distance
|
||||||
|
return mix(1.0, attenuation, evalShadowFalloff(viewDepth));
|
||||||
}
|
}
|
||||||
|
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -21,12 +21,12 @@ int getShadowCascadeCount() {
|
||||||
return shadow.cascadeCount;
|
return shadow.cascadeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getShadowCascadeInvTransitionWidth(int cascadeIndex) {
|
float getShadowCascadeInvBlendWidth() {
|
||||||
return shadow.cascades[cascadeIndex].invTransitionWidth;
|
return shadow.invCascadeBlendWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getShadowCascadeMaxDistance(int cascadeIndex) {
|
float evalShadowFalloff(float depth) {
|
||||||
return shadow.cascades[cascadeIndex].maxDistance;
|
return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4 getShadowReprojection(int cascadeIndex) {
|
mat4 getShadowReprojection(int cascadeIndex) {
|
||||||
|
@ -48,44 +48,46 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
||||||
return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0);
|
return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) {
|
bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) {
|
||||||
|
bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0));
|
||||||
|
bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1));
|
||||||
|
return all(greaterThanZero) && all(lessThanOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) {
|
||||||
int cascadeIndex;
|
int cascadeIndex;
|
||||||
for (cascadeIndex=0 ; cascadeIndex<getShadowCascadeCount()-1 ; cascadeIndex++) {
|
startCascadeIndex = min(startCascadeIndex, getShadowCascadeCount()-1);
|
||||||
vec4 shadowTexcoord = cascadeShadowCoords[cascadeIndex];
|
for (cascadeIndex=startCascadeIndex ; cascadeIndex<getShadowCascadeCount() ; cascadeIndex++) {
|
||||||
bvec2 greaterThanZero = greaterThanEqual(shadowTexcoord.xy, vec2(0));
|
cascadeShadowCoords = evalShadowTexcoord(cascadeIndex, worldPosition);
|
||||||
bvec2 lessThanOne = lessThanEqual(shadowTexcoord.xy, vec2(1));
|
if (isShadowCascadeProjectedOnPixel(cascadeShadowCoords)) {
|
||||||
if (all(greaterThanZero) && all(lessThanOne)) {
|
|
||||||
return cascadeIndex;
|
return cascadeIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cascadeIndex;
|
return cascadeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalCascadeIndicesAndMix(float viewDepth, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) {
|
float evalShadowCascadeWeight(vec4 cascadeTexCoords) {
|
||||||
int cascadeCount = getShadowCascadeCount();
|
// Inspired by DirectX CascadeShadowMaps11 example
|
||||||
#if 0
|
vec2 distanceToOne = vec2(1.0) - cascadeTexCoords.xy;
|
||||||
// Cascade selection based on :
|
float blend1 = min( cascadeTexCoords.x, cascadeTexCoords.y );
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx
|
float blend2 = min( distanceToOne.x, distanceToOne.y );
|
||||||
vec4 cascadeDepthLimits = vec4(
|
float blend = min( blend1, blend2 );
|
||||||
getShadowCascadeMinDistance(0),
|
return clamp(blend * getShadowCascadeInvBlendWidth(), 0.0, 1.0);
|
||||||
getShadowCascadeMinDistance(1),
|
}
|
||||||
getShadowCascadeMinDistance(2),
|
|
||||||
getShadowCascadeMinDistance(3)
|
|
||||||
);
|
|
||||||
bvec4 comparison = greaterThan( vec4(viewDepth), cascadeDepthLimits);
|
|
||||||
bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3));
|
|
||||||
int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison)));
|
|
||||||
cascadeIndex = min( cascadeIndex, cascadeCount-1 );
|
|
||||||
#else
|
|
||||||
int cascadeIndex = getFirstValidShadowTexcoord(cascadeShadowCoords);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cascadeIndices.x = cascadeIndex;
|
float determineShadowCascadesOnPixel(vec4 worldPosition, float viewDepth, out vec4 cascadeShadowCoords[2], out ivec2 cascadeIndices) {
|
||||||
cascadeIndices.y = cascadeIndex+1;
|
cascadeIndices.x = getFirstShadowCascadeOnPixel(0, worldPosition, cascadeShadowCoords[0]);
|
||||||
|
cascadeIndices.y = cascadeIndices.x+1;
|
||||||
|
if (cascadeIndices.x < (getShadowCascadeCount()-1)) {
|
||||||
|
cascadeIndices.y = getFirstShadowCascadeOnPixel(cascadeIndices.y, worldPosition, cascadeShadowCoords[1]);
|
||||||
|
|
||||||
float maxDepth = getShadowCascadeMaxDistance(cascadeIndices.x);
|
float firstCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[0]);
|
||||||
float cascadeMixRatio = (maxDepth-viewDepth) * getShadowCascadeInvTransitionWidth(cascadeIndices.x);
|
float secondCascadeWeight = evalShadowCascadeWeight(cascadeShadowCoords[1]);
|
||||||
return 1.0 - clamp(cascadeMixRatio, 0.0, 1.0);
|
// Returns the mix amount between first and second cascade.
|
||||||
|
return ((1.0-firstCascadeWeight) * secondCascadeWeight) / (firstCascadeWeight + secondCascadeWeight);
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -13,17 +13,18 @@ struct ShadowTransform {
|
||||||
MAT4 reprojection;
|
MAT4 reprojection;
|
||||||
|
|
||||||
float bias;
|
float bias;
|
||||||
float maxDistance;
|
float _padding1;
|
||||||
float invTransitionWidth;
|
float _padding2;
|
||||||
float _padding;
|
float _padding3;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShadowParameters {
|
struct ShadowParameters {
|
||||||
|
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||||
int cascadeCount;
|
int cascadeCount;
|
||||||
float invMapSize;
|
float invMapSize;
|
||||||
float _padding1;
|
float invCascadeBlendWidth;
|
||||||
float _padding2;
|
float maxDistance;
|
||||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
float invFalloffDistance;
|
||||||
};
|
};
|
||||||
|
|
||||||
// <@if 1@>
|
// <@if 1@>
|
||||||
|
|
Loading…
Reference in a new issue