Working CSM with smooth blend between cascades. Switched to 4 1024 cascades

This commit is contained in:
Olivier Prat 2017-11-14 16:57:22 +01:00
parent ac0e816f8c
commit 74b0b52edb
11 changed files with 62 additions and 36 deletions

View file

@ -38,7 +38,7 @@ void DebugDeferredBufferConfig::setMode(int newMode) {
emit dirty();
}
enum Slot {
enum TextureSlot {
Albedo = 0,
Normal,
Specular,
@ -55,7 +55,11 @@ enum Slot {
AmbientOcclusionBlurred
};
enum ParamSlot {
CameraCorrection = 0,
DeferredFrameTransform,
ShadowTransform
};
static const std::string DEFAULT_ALBEDO_SHADER {
"vec4 getFragmentColor() {"
@ -147,7 +151,7 @@ static const std::string DEFAULT_SHADOW_SHADER{
};
static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
"vec3 cascadeColors[4] = vec3[4]( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(0,0,0) );"
"vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );"
"vec4 getFragmentColor() {"
" DeferredFrameTransform deferredTransform = getDeferredFrameTransform();"
" DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);"
@ -160,7 +164,7 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
" evalShadowTexcoord(3, worldPosition)"
" );"
" ivec2 cascadeIndices;"
" float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);"
" float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);"
" return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);"
" }"
};
@ -378,6 +382,10 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str
const auto program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding("cameraCorrectionBuffer", CameraCorrection));
slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", DeferredFrameTransform));
slotBindings.insert(gpu::Shader::Binding("shadowTransformBuffer", ShadowTransform));
slotBindings.insert(gpu::Shader::Binding("albedoMap", Albedo));
slotBindings.insert(gpu::Shader::Binding("normalMap", Normal));
slotBindings.insert(gpu::Shader::Binding("specularMap", Specular));
@ -429,6 +437,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
auto& linearDepthTarget = inputs.get1();
auto& surfaceGeometryFramebuffer = inputs.get2();
auto& ambientOcclusionFramebuffer = inputs.get3();
auto& frameTransform = inputs.get4();
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
@ -465,11 +474,9 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
const auto& globalShadow = lightAndShadow.second;
if (globalShadow) {
const auto cascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)globalShadow->getCascadeCount() - 1);
const auto shadowBufferLoc = pipeline->getProgram()->getUniformBuffers().findLocation("shadowTransformBuffer");
batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map);
if (shadowBufferLoc >= 0) {
batch.setUniformBuffer(shadowBufferLoc, globalShadow->getBuffer());
}
batch.setUniformBuffer(ShadowTransform, globalShadow->getBuffer());
batch.setUniformBuffer(DeferredFrameTransform, frameTransform->getFrameTransformBuffer());
}
if (linearDepthTarget) {

View file

@ -15,6 +15,7 @@
#include <QFileInfo>
#include <render/DrawTask.h>
#include "DeferredFrameTransform.h"
#include "DeferredFramebuffer.h"
#include "SurfaceGeometryPass.h"
#include "AmbientOcclusionEffect.h"
@ -37,7 +38,7 @@ signals:
class DebugDeferredBuffer {
public:
using Inputs = render::VaryingSet4<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer>;
using Inputs = render::VaryingSet5<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, DeferredFrameTransformPointer>;
using Config = DebugDeferredBufferConfig;
using JobModel = render::Job::ModelI<DebugDeferredBuffer, Inputs, Config>;

View file

@ -21,7 +21,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0 };
const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 };
const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 };
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
@ -58,10 +58,11 @@ LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount)
}
void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
float viewMinShadowDistance, float viewMaxShadowDistance,
float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance,
float nearDepth, float farDepth) {
assert(viewMinShadowDistance < viewMaxShadowDistance);
assert(nearDepth < farDepth);
assert(viewOverlapDistance > 0.0f);
assert(cascadeIndex < _cascades.size());
// Orient the keylight frustum
@ -124,7 +125,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie
// Update the buffer
auto& schemaCascade = _schemaBuffer.edit<Schema>().cascades[cascadeIndex];
schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix();
schemaCascade.minDistance = viewMinShadowDistance;
schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance;
schemaCascade.maxDistance = viewMaxShadowDistance;
cascade.minDistance = viewMinShadowDistance;
cascade.maxDistance = viewMaxShadowDistance;

View file

@ -71,7 +71,9 @@ public:
Shadow(model::LightPointer light, unsigned int cascadeCount = 1);
void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f);
void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance,
float nearDepth = 1.0f, float farDepth = 1000.0f);
void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
const UniformBufferView& getBuffer() const { return _schemaBuffer; }

View file

@ -225,7 +225,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Debugging stages
{
// Debugging Deferred buffer job
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, deferredFrameTransform));
task.addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,

View file

@ -260,17 +260,20 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O
const auto farClip = args->getViewFrustum().getFarClip();
const auto nearDepth = -args->_boomOffset.z;
static const float SHADOW_MAX_DISTANCE = 25.0f;
static const float SHADOW_OVERLAP_DISTANCE = 1.0f;
float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex);
float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE;
static const float SHADOW_MAX_DISTANCE = 30.0f;
static const float CASCADE_LEVEL_SCALE = 2.0f;
float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(CASCADE_LEVEL_SCALE, globalShadow->getCascadeCount() - 1 - _cascadeIndex);
float minCascadeDistance = maxCascadeDistance / CASCADE_LEVEL_SCALE;
float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f;
if (_cascadeIndex == 0) {
minCascadeDistance = nearDepth;
}
} else {
minCascadeDistance -= shadowOverlapDistance;
}
minCascadeDistance = std::max(minCascadeDistance, nearDepth);
maxCascadeDistance = std::min(maxCascadeDistance, farClip);
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
// Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));

View file

@ -13,6 +13,9 @@
<@include ShadowCore.slh@>
#define SHADOW_NOISE_ENABLED 0
#define SHADOW_SCREEN_SPACE_DITHER 1
// the shadow texture
uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT];
@ -41,17 +44,20 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) {
float shadowScale = getShadowScale();
ShadowSampleOffsets offsets;
#if 0
#if SHADOW_SCREEN_SPACE_DITHER
// Pattern dithering in screen space
ivec2 coords = ivec2(gl_FragCoord.xy);
#else
// Pattern dithering in world space (mm resolution)
ivec2 coords = ivec2(position.x, position.y+position.z);
#endif
#if SHADOW_NOISE_ENABLED
// Add some noise to break dithering
int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4;
coords.x += index & 1;
coords.y += (index & 2) >> 1;
#endif
// Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html
ivec2 offset = coords & ivec2(1,1);
@ -87,7 +93,7 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord);
}
float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) {
float evalShadowAttenuation(vec4 worldPosition, float viewDepth) {
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
vec4 cascadeShadowCoords[4] = vec4[4] (
evalShadowTexcoord(0, worldPosition),
@ -96,11 +102,11 @@ float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) {
evalShadowTexcoord(3, worldPosition)
);
ivec2 cascadeIndices;
float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);
float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices);
vec2 cascadeAttenuations = vec2(1.0, 1.0);
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]);
if (cascadeMix > 0.0) {
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]);
}
return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);

View file

@ -13,7 +13,7 @@
<@include Shadows_shared.slh@>
uniform shadowTransformBuffer {
layout(std140) uniform shadowTransformBuffer {
ShadowParameters shadow;
};
@ -21,8 +21,12 @@ int getShadowCascadeCount() {
return shadow.cascadeCount;
}
float getShadowCascadeMinDistance(int cascadeIndex) {
return shadow.cascades[cascadeIndex].minDistance;
float getShadowCascadeInvTransitionWidth(int cascadeIndex) {
return shadow.cascades[cascadeIndex].invTransitionWidth;
}
float getShadowCascadeMaxDistance(int cascadeIndex) {
return shadow.cascades[cascadeIndex].maxDistance;
}
mat4 getShadowReprojection(int cascadeIndex) {
@ -58,19 +62,18 @@ int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) {
return cascadeIndex;
}
float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) {
float evalCascadeIndicesAndMix(float viewDepth, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) {
int cascadeCount = getShadowCascadeCount();
#if 0
// Cascade selection based on :
// https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx
vec4 currentPixelDepth = viewPosition.zzzz;
vec4 cascadeDepthLimits = vec4(
getShadowCascadeMinDistance(0),
getShadowCascadeMinDistance(1),
getShadowCascadeMinDistance(2),
getShadowCascadeMinDistance(3)
);
bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits);
int cascadeCount = getShadowCascadeCount();
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 );
@ -79,8 +82,11 @@ float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], o
#endif
cascadeIndices.x = cascadeIndex;
cascadeIndices.y = cascadeIndex;
return 1.0;
cascadeIndices.y = cascadeIndex+1;
float maxDepth = getShadowCascadeMaxDistance(cascadeIndices.x);
float cascadeMixRatio = (maxDepth-viewDepth) * getShadowCascadeInvTransitionWidth(cascadeIndices.x);
return 1.0 - clamp(cascadeMixRatio, 0.0, 1.0);
}
<@endif@>

View file

@ -13,8 +13,8 @@ struct ShadowTransform {
MAT4 reprojection;
float bias;
float minDistance;
float maxDistance;
float invTransitionWidth;
float _padding;
};

View file

@ -28,7 +28,7 @@ void main(void) {
vec4 viewPos = vec4(frag.position.xyz, 1.0);
vec4 worldPos = getViewInverse() * viewPos;
float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos);
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z);
if (frag.mode == FRAG_MODE_UNLIT) {
discard;

View file

@ -28,7 +28,7 @@ void main(void) {
vec4 viewPos = vec4(frag.position.xyz, 1.0);
vec4 worldPos = getViewInverse() * viewPos;
float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos);
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z);
// Light mapped or not ?
if (frag.mode == FRAG_MODE_UNLIT) {