mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 19:01:09 +02:00
Working on better adaptive bias algorithm for shadow
This commit is contained in:
parent
b41129f81b
commit
6b0b17ff63
8 changed files with 77 additions and 19 deletions
|
@ -220,7 +220,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||||
float nearDepth, float farDepth) {
|
float nearDepth, float farDepth, float baseBias) {
|
||||||
assert(nearDepth < farDepth);
|
assert(nearDepth < farDepth);
|
||||||
assert(cascadeIndex < _cascades.size());
|
assert(cascadeIndex < _cascades.size());
|
||||||
|
|
||||||
|
@ -270,11 +270,7 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
|
||||||
// Update the buffer
|
// Update the buffer
|
||||||
auto& schema = _schemaBuffer.edit<Schema>();
|
auto& schema = _schemaBuffer.edit<Schema>();
|
||||||
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||||
// Adapt shadow bias to shadow resolution with a totally empirical formula
|
schema.cascades[cascadeIndex].bias = baseBias;
|
||||||
const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y));
|
|
||||||
const auto REFERENCE_TEXEL_DENSITY = 7.5f;
|
|
||||||
const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim;
|
|
||||||
schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
void setKeylightFrustum(const ViewFrustum& viewFrustum,
|
void setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
||||||
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||||
float nearDepth = 1.0f, float farDepth = 1000.0f);
|
float nearDepth = 1.0f, float farDepth = 1000.0f, float baseBias = 0.005f);
|
||||||
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||||
|
|
||||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||||
|
|
|
@ -216,7 +216,9 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
||||||
task.addJob<RenderShadowSetup>("ShadowSetup");
|
task.addJob<RenderShadowSetup>("ShadowSetup");
|
||||||
|
|
||||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||||
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>("ShadowCascadeSetup", i);
|
char jobName[64];
|
||||||
|
sprintf(jobName, "ShadowCascadeSetup%d", i);
|
||||||
|
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i);
|
||||||
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
|
||||||
|
|
||||||
// CPU jobs:
|
// CPU jobs:
|
||||||
|
@ -253,6 +255,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderShadowCascadeSetup::configure(const Config& configuration) {
|
||||||
|
_baseBias = configuration.bias * configuration.bias * 0.01f;
|
||||||
|
}
|
||||||
|
|
||||||
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
||||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||||
assert(lightStage);
|
assert(lightStage);
|
||||||
|
@ -266,7 +272,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
|
||||||
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
|
||||||
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
|
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered();
|
||||||
|
|
||||||
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR);
|
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, _baseBias);
|
||||||
|
|
||||||
// Set the keylight render args
|
// Set the keylight render args
|
||||||
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
||||||
|
|
|
@ -62,17 +62,31 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RenderShadowCascadeSetupConfig : public render::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float bias MEMBER bias NOTIFY dirty)
|
||||||
|
public:
|
||||||
|
|
||||||
|
float bias{ 0.5f };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
class RenderShadowCascadeSetup {
|
class RenderShadowCascadeSetup {
|
||||||
public:
|
public:
|
||||||
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
|
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
|
||||||
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>;
|
using Config = RenderShadowCascadeSetupConfig;
|
||||||
|
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs, Config>;
|
||||||
|
|
||||||
RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
|
RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
|
||||||
|
void configure(const Config& configuration);
|
||||||
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
void run(const render::RenderContextPointer& renderContext, Outputs& output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned int _cascadeIndex;
|
unsigned int _cascadeIndex;
|
||||||
|
float _baseBias{ 0.1f };
|
||||||
};
|
};
|
||||||
|
|
||||||
class RenderShadowCascadeTeardown {
|
class RenderShadowCascadeTeardown {
|
||||||
|
|
|
@ -83,27 +83,30 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
||||||
return shadowAttenuation;
|
return shadowAttenuation;
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) {
|
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) {
|
||||||
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
// Multiply bias if we are at a grazing angle with light
|
// Add fixed bias
|
||||||
float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal));
|
bias += getShadowBias(cascadeIndex);
|
||||||
float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor);
|
|
||||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) {
|
float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) {
|
||||||
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
||||||
vec4 cascadeShadowCoords[2];
|
vec4 cascadeShadowCoords[2];
|
||||||
ivec2 cascadeIndices;
|
ivec2 cascadeIndices;
|
||||||
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||||
|
|
||||||
|
// Adjust bias if we are at a grazing angle with light
|
||||||
|
float ndotl = dot(worldLightDir, worldNormal);
|
||||||
|
float bias = 0.5*(1.0/(ndotl*ndotl)-1.0);
|
||||||
|
bias *= getShadowScale();
|
||||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
||||||
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]);
|
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias);
|
||||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]);
|
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias);
|
||||||
}
|
}
|
||||||
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||||
// Falloff to max distance
|
// Falloff to max distance
|
||||||
|
|
|
@ -28,7 +28,9 @@ void main(void) {
|
||||||
|
|
||||||
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
||||||
vec4 worldPos = getViewInverse() * viewPos;
|
vec4 worldPos = getViewInverse() * viewPos;
|
||||||
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal);
|
Light shadowLight = getKeyLight();
|
||||||
|
vec3 worldLightDirection = getLightDirection(shadowLight);
|
||||||
|
float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal);
|
||||||
|
|
||||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||||
discard;
|
discard;
|
||||||
|
|
|
@ -28,7 +28,9 @@ void main(void) {
|
||||||
|
|
||||||
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
vec4 viewPos = vec4(frag.position.xyz, 1.0);
|
||||||
vec4 worldPos = getViewInverse() * viewPos;
|
vec4 worldPos = getViewInverse() * viewPos;
|
||||||
float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal);
|
Light shadowLight = getKeyLight();
|
||||||
|
vec3 worldLightDirection = getLightDirection(shadowLight);
|
||||||
|
float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal);
|
||||||
|
|
||||||
// Light mapped or not ?
|
// Light mapped or not ?
|
||||||
if (frag.mode == FRAG_MODE_UNLIT) {
|
if (frag.mode == FRAG_MODE_UNLIT) {
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
//
|
//
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import "qrc:///qml/styles-uit"
|
||||||
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
|
import "configSlider"
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: root
|
id: root
|
||||||
|
@ -68,4 +71,36 @@ Column {
|
||||||
font.italic: true
|
font.italic: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr("Cascade 0 bias")
|
||||||
|
integral: false
|
||||||
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup0")
|
||||||
|
property: "bias"
|
||||||
|
max: 1.0
|
||||||
|
min: 0.01
|
||||||
|
}
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr("Cascade 1 bias")
|
||||||
|
integral: false
|
||||||
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup1")
|
||||||
|
property: "bias"
|
||||||
|
max: 1.0
|
||||||
|
min: 0.01
|
||||||
|
}
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr("Cascade 2 bias")
|
||||||
|
integral: false
|
||||||
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup2")
|
||||||
|
property: "bias"
|
||||||
|
max: 1.0
|
||||||
|
min: 0.01
|
||||||
|
}
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr("Cascade 3 bias")
|
||||||
|
integral: false
|
||||||
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup3")
|
||||||
|
property: "bias"
|
||||||
|
max: 1.0
|
||||||
|
min: 0.01
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue