Added some tweaking parameters to try to limit silhouette AO

This commit is contained in:
Olivier Prat 2018-09-13 18:39:02 +02:00
parent 8006d7c052
commit 6b8f47c75a
5 changed files with 97 additions and 104 deletions

View file

@ -181,8 +181,12 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() :
#endif #endif
perspectiveScale{ 1.0f }, perspectiveScale{ 1.0f },
obscuranceLevel{ 0.5f }, obscuranceLevel{ 0.5f },
falloffBias{ 0.01f }, #if SSAO_USE_HORIZON_BASED
silhouetteRadius{ 0.3f }, falloffAngle{ 0.1f },
#else
falloffAngle{ 0.01f },
#endif
falloffDistance{ 0.3f },
edgeSharpness{ 1.0f }, edgeSharpness{ 1.0f },
blurDeviation{ 2.5f }, blurDeviation{ 2.5f },
numSpiralTurns{ 7.0f }, numSpiralTurns{ 7.0f },
@ -234,9 +238,9 @@ void AmbientOcclusionEffect::configure(const Config& config) {
current.w = config.obscuranceLevel; current.w = config.obscuranceLevel;
} }
if (config.falloffBias != _aoParametersBuffer->getFalloffBias()) { if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) {
auto& current = _aoParametersBuffer.edit().ditheringInfo; auto& current = _aoParametersBuffer.edit().ditheringInfo;
current.z = config.falloffBias; current.z = config.falloffAngle;
} }
if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) { if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) {
@ -297,9 +301,9 @@ void AmbientOcclusionEffect::configure(const Config& config) {
current.w = (float)config.borderingEnabled; current.w = (float)config.borderingEnabled;
} }
if (config.silhouetteRadius != _aoParametersBuffer->getSilhouetteRadius()) { if (config.falloffDistance != _aoParametersBuffer->getFalloffDistance()) {
auto& current = _aoParametersBuffer.edit().ditheringInfo; auto& current = _aoParametersBuffer.edit().ditheringInfo;
current.y = (float)config.silhouetteRadius; current.y = (float)config.falloffDistance;
} }
if (shouldUpdateGaussian) { if (shouldUpdateGaussian) {
@ -450,6 +454,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
#endif #endif
batch.popProfileRange(); batch.popProfileRange();
batch.pushProfileRange("Occlusion");
batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); batch.setUniformBuffer(render_utils::slot::buffer::DeferredFrameTransform, frameTransform->getFrameTransformBuffer());
batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer); batch.setUniformBuffer(render_utils::slot::buffer::SsaoParams, _aoParametersBuffer);
@ -459,10 +464,11 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
batch.setPipeline(occlusionPipeline); batch.setPipeline(occlusionPipeline);
batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, _framebuffer->getLinearDepthTexture()); batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, _framebuffer->getLinearDepthTexture());
batch.draw(gpu::TRIANGLE_STRIP, 4); batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.popProfileRange();
/* TEMPO OP if (_aoParametersBuffer->getBlurRadius() > 0)*/ { {
PROFILE_RANGE_BATCH(batch, "Blur"); PROFILE_RANGE_BATCH(batch, "Bilateral Blur");
// Blur 1st pass // Blur 1st pass
model.setScale(resolutionScale); model.setScale(resolutionScale);
batch.setModelTransform(model); batch.setModelTransform(model);
batch.setViewportTransform(firstBlurViewport); batch.setViewportTransform(firstBlurViewport);
@ -482,8 +488,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0)); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionBlurredFBO->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4); batch.draw(gpu::TRIANGLE_STRIP, 4);
} }
batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, nullptr); batch.setResourceTexture(render_utils::slot::texture::SsaoPyramid, nullptr);
batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, nullptr); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, nullptr);

View file

@ -62,8 +62,8 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent {
Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty)
Q_PROPERTY(float radius MEMBER radius WRITE setRadius) Q_PROPERTY(float radius MEMBER radius WRITE setRadius)
Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel)
Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias) Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle)
Q_PROPERTY(float silhouetteRadius MEMBER silhouetteRadius WRITE setSilhouetteRadius) Q_PROPERTY(float falloffDistance MEMBER falloffDistance WRITE setFalloffDistance)
Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness)
Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation)
Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns)
@ -75,12 +75,12 @@ public:
AmbientOcclusionEffectConfig(); AmbientOcclusionEffectConfig();
const int MAX_RESOLUTION_LEVEL = 4; const int MAX_RESOLUTION_LEVEL = 4;
const int MAX_BLUR_RADIUS = 6; const int MAX_BLUR_RADIUS = 15;
void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); }
void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); }
void setFalloffBias(float bias) { falloffBias = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); } void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 0.2f)); emit dirty(); }
void setSilhouetteRadius(float value) { silhouetteRadius = std::max(0.0f, value); emit dirty(); } void setFalloffDistance(float value) { falloffDistance = std::max(0.0f, value); emit dirty(); }
void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); }
void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); }
void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); }
@ -91,8 +91,8 @@ public:
float radius; float radius;
float perspectiveScale; float perspectiveScale;
float obscuranceLevel; // intensify or dim down the obscurance effect float obscuranceLevel; // intensify or dim down the obscurance effect
float falloffBias; float falloffAngle;
float silhouetteRadius; float falloffDistance;
float edgeSharpness; float edgeSharpness;
float blurDeviation; float blurDeviation;
float numSpiralTurns; // defining an angle span to distribute the samples ray directions float numSpiralTurns; // defining an angle span to distribute the samples ray directions
@ -134,7 +134,7 @@ public:
// Blurring info // Blurring info
glm::vec4 blurInfo; glm::vec4 blurInfo;
// gaussian distribution coefficients first is the sampling radius (max is 6) // gaussian distribution coefficients first is the sampling radius (max is 6)
const static int GAUSSIAN_COEFS_LENGTH = 8; const static int GAUSSIAN_COEFS_LENGTH = 16;
float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH]; float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH];
AOParameters(); AOParameters();
@ -143,10 +143,10 @@ public:
float getRadius() const { return radiusInfo.x; } float getRadius() const { return radiusInfo.x; }
float getPerspectiveScale() const { return resolutionInfo.z; } float getPerspectiveScale() const { return resolutionInfo.z; }
float getObscuranceLevel() const { return radiusInfo.w; } float getObscuranceLevel() const { return radiusInfo.w; }
float getFalloffBias() const { return (float)ditheringInfo.z; } float getFalloffAngle() const { return (float)ditheringInfo.z; }
float getFalloffDistance() const { return ditheringInfo.y; }
float getEdgeSharpness() const { return (float)blurInfo.x; } float getEdgeSharpness() const { return (float)blurInfo.x; }
float getBlurDeviation() const { return blurInfo.z; } float getBlurDeviation() const { return blurInfo.z; }
float getSilhouetteRadius() const { return ditheringInfo.y; }
float getNumSpiralTurns() const { return sampleInfo.z; } float getNumSpiralTurns() const { return sampleInfo.z; }
int getNumSamples() const { return (int)sampleInfo.x; } int getNumSamples() const { return (int)sampleInfo.x; }

View file

@ -41,7 +41,7 @@ struct AmbientOcclusionParams {
vec4 _ditheringInfo; vec4 _ditheringInfo;
vec4 _sampleInfo; vec4 _sampleInfo;
vec4 _blurInfo; vec4 _blurInfo;
float _gaussianCoefs[8]; float _gaussianCoefs[16];
}; };
layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer { layout(binding=RENDER_UTILS_BUFFER_SSAO_PARAMS) uniform ambientOcclusionParamsBuffer {
@ -75,14 +75,14 @@ float getObscuranceScaling() {
float isDitheringEnabled() { float isDitheringEnabled() {
return params._ditheringInfo.x; return params._ditheringInfo.x;
} }
float getSilhouetteRadius() { float getFalloffDistance() {
return params._ditheringInfo.y; return params._ditheringInfo.y;
} }
float isBorderingEnabled() { float isBorderingEnabled() {
return params._ditheringInfo.w; return params._ditheringInfo.w;
} }
float getFalloffBias() { float getFalloffAngle() {
return params._ditheringInfo.z; return params._ditheringInfo.z;
} }
@ -226,7 +226,7 @@ float getZEyeAtUV(vec2 texCoord, int level) {
return -texture(pyramidMap, texCoord, level).x; return -texture(pyramidMap, texCoord, level).x;
} }
const int LOG_MAX_OFFSET = 3; const int LOG_MAX_OFFSET = 1;
const int MAX_MIP_LEVEL = 5; const int MAX_MIP_LEVEL = 5;
int evalMipFromRadius(float radius) { int evalMipFromRadius(float radius) {
// mipLevel = floor(log(ssR / MAX_OFFSET)); // mipLevel = floor(log(ssR / MAX_OFFSET));
@ -249,7 +249,7 @@ vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
return P; return P;
} }
vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { vec4 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
int mipLevel = evalMipFromRadius(tap.z * float(doFetchMips())); int mipLevel = evalMipFromRadius(tap.z * float(doFetchMips()));
vec2 ssP = tap.xy + vec2(ssC); vec2 ssP = tap.xy + vec2(ssC);
@ -259,15 +259,13 @@ vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize; vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize;
vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y); vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y);
vec3 P; vec4 P;
P.xy = tapUV; P.xy = tapUV;
P.z = -textureLod(pyramidMap, fetchUV, float(mipLevel)).x; P.w = float(mipLevel);
P.z = -textureLod(pyramidMap, fetchUV, P.w).x;
return P; return P;
} }
<@endfunc@> <@endfunc@>
@ -289,59 +287,63 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t
// Fall off function as recommended in SSAO paper // Fall off function as recommended in SSAO paper
const float epsilon = 0.01; const float epsilon = 0.01;
float f = max(getRadius2() - vv, 0.0); float f = max(getRadius2() - vv, 0.0);
return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0);
} }
float computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES, float normalizedRadius) {
vec3 deltaVec = tapPositionES - fragPositionES;
float distance = abs(deltaVec.z);
float rawHorizon = dot(normalize(deltaVec), fragNormalES);
rawHorizon = (rawHorizon - getFalloffAngle()) / (1.0 - getFalloffAngle());
rawHorizon *= 1.0 - normalizedRadius * normalizedRadius;
rawHorizon *= 1.0 - smoothstep(getFalloffDistance()/2, getFalloffDistance(), distance);
return rawHorizon;
}
<@func computeHorizon()@>
vec3 tap = vec3(tapPixelPos, radius);
vec4 tapUVZ_mip = fetchTap(side, centerPixelPos, tap, imageSize);
vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ_mip.z, tapUVZ_mip.xy);
float rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES, radius / searchRadius);
<$horizon$> = max(<$horizon$>, rawHorizon);
<@endfunc@>
#define SSAO_LINEAR_SAMPLING 0
<@func updateHorizon(horizon, deltaPixelTap)@> <@func updateHorizon(horizon, deltaPixelTap)@>
{ {
int stepIndex;
vec2 tapPixelPos = vec2(0); vec2 tapPixelPos = vec2(0);
float radius = 0.0;
for (stepIndex=stepCount ; stepIndex>0 ; stepIndex--) { #if !SSAO_LINEAR_SAMPLING
tapPixelPos += deltaPixelTap; float radius = deltaRadius;
float mipLevel = evalMipFromRadius(radius * float(doFetchMips()));
while (radius<searchRadius) {
tapPixelPos += <$deltaPixelTap$>;
<$computeHorizon()$>
if (tapUVZ_mip.w != mipLevel) {
mipLevel = tapUVZ_mip.w;
deltaRadius *= 2;
<$deltaPixelTap$> *= 2;
}
radius += deltaRadius;
}
#else
float radius = 0.0;
int stepIndex;
for (stepIndex=0 ; stepIndex<stepCount ; stepIndex++) {
tapPixelPos += <$deltaPixelTap$>;
radius += deltaRadius; radius += deltaRadius;
vec3 tap = vec3(tapPixelPos, radius); <$computeHorizon()$>
vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize);
vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
vec3 deltaVec = tapPositionES - fragPositionES;
float deltaVecHeight = dot(deltaVec, fragNormalES);
float rawHorizon = dot(normalize(deltaVec), fragNormalES);
float falloff = max(0.0, 1.0 - deltaVecHeight / getSilhouetteRadius());
rawHorizon = rawHorizon < getFalloffBias() ? 0.0f : rawHorizon;
rawHorizon *= falloff * falloff;
<$horizon$> = max(<$horizon$>, rawHorizon);
} }
} #endif
<@endfunc@>
<@func searchBresenhamHorizon(deltaPixelCoord)@>
{
float epsilon = 1e-8;
vec2 deltaPixelTap = searchVec / absSearchVec.<$deltaPixelCoord$>;
vec2 absDeltaPixelTap = abs(deltaPixelTap);
bvec2 nullDelta = absDeltaPixelTap < epsilon;
pixelDelta1 = mix(pixelDelta1, vec2(1.0), pixelDelta1 < epsilon && nullDelta);
pixelDelta2 = mix(pixelDelta2, vec2(1.0), pixelDelta2 < epsilon && nullDelta);
pixelDelta1 = ceil(pixelDelta1 / absDeltaPixelTap);
pixelDelta2 = ceil(pixelDelta2 / absDeltaPixelTap);
int maxStepCount = max(0, int(ceil(absSearchVec.<$deltaPixelCoord$>)));
float deltaRadius = ssDiskRadius / maxStepCount;
int stepCount;
// Forward search for h1
stepCount = clamp(int(min(pixelDelta1.x, pixelDelta1.y)), 0, maxStepCount);
<$updateHorizon(horizons.x, deltaPixelTap)$>
// Backward search for h2
stepCount = clamp(int(min(pixelDelta2.x, pixelDelta2.y)), 0, maxStepCount);
<$updateHorizon(horizons.y, -deltaPixelTap)$>
} }
<@endfunc@> <@endfunc@>
@ -379,7 +381,8 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta
int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); int stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y));
if (stepCount>0) { if (stepCount>0) {
vec2 deltaPixelTap = clampedSearchVec / float(stepCount); vec2 deltaPixelTap = clampedSearchVec / float(stepCount);
float deltaRadius = length(deltaPixelTap); float searchRadius = length(clampedSearchVec);
float deltaRadius = searchRadius / float(stepCount);
<$updateHorizon(horizons.x, deltaPixelTap)$> <$updateHorizon(horizons.x, deltaPixelTap)$>
} }
// Backward search for h2 // Backward search for h2
@ -388,32 +391,11 @@ vec2 searchHorizons(ivec4 side, ivec2 centerPixelPos, vec2 imageSize, vec2 delta
stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y)); stepCount = int(max(absClampedSearchVec.x, absClampedSearchVec.y));
if (stepCount>0) { if (stepCount>0) {
vec2 deltaPixelTap = clampedSearchVec / float(stepCount); vec2 deltaPixelTap = clampedSearchVec / float(stepCount);
float deltaRadius = length(deltaPixelTap); float searchRadius = length(clampedSearchVec);
float deltaRadius = searchRadius / float(stepCount);
<$updateHorizon(horizons.y, deltaPixelTap)$> <$updateHorizon(horizons.y, deltaPixelTap)$>
} }
//
// <!
//
/* TEMPO OP
// Bresenham style walk along segment
if (absSearchVec.x > absSearchVec.y) {
<$searchBresenhamHorizon(x)$>
} else {
<$searchBresenhamHorizon(y)$>
}
vec3 angles = acos(vec3(horizons, fragNormalES.z)) - PI/2.0;
angles.x = -angles.x;
// Clamp to limit horizon defined by normal plane
horizons.xy = angles.zz + max(angles.xy - angles.zz, vec2(-PI/2.0, PI/2.0));
*/
//
// !>
//
return horizons; return horizons;
} }
@ -460,7 +442,7 @@ float fetchOcclusion(vec2 coords) {
} }
const float BLUR_WEIGHT_OFFSET = 0.01; const float BLUR_WEIGHT_OFFSET = 0.01;
const float BLUR_EDGE_SCALE = 10.0; const float BLUR_EDGE_SCALE = 300.0;
vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) { vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float key) {
ivec2 tapOffset = <$axis$> * r; ivec2 tapOffset = <$axis$> * r;
@ -479,7 +461,9 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTe
float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r)); float weight = BLUR_WEIGHT_OFFSET + getBlurCoef(abs(r));
// range domain (the "bilateral" weight). As depth difference increases, decrease weight. // range domain (the "bilateral" weight). As depth difference increases, decrease weight.
weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key)); // weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * abs(tapDepth - key));
float zDistance = tapDepth - key;
weight *= exp(-(getBlurEdgeSharpness() * BLUR_EDGE_SCALE) * zDistance * zDistance);
return vec2(tapOcclusion * weight, weight); return vec2(tapOcclusion * weight, weight);
} }

View file

@ -20,6 +20,7 @@
<$declarePackOcclusionDepth()$> <$declarePackOcclusionDepth()$>
#define SSAO_USE_HORIZON_BASED 1 #define SSAO_USE_HORIZON_BASED 1
#define SSAO_HBAO_MAX_RADIUS 100.0
layout(location=0) out vec4 outFragColor; layout(location=0) out vec4 outFragColor;
@ -46,6 +47,9 @@ void main(void) {
// Choose the screen-space sample radius // Choose the screen-space sample radius
float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize);
#if SSAO_USE_HORIZON_BASED
ssDiskRadius = min(ssDiskRadius, SSAO_HBAO_MAX_RADIUS);
#endif
// Let's make noise // Let's make noise
float randomPatternRotationAngle = getAngleDithering(centerPixelPos); float randomPatternRotationAngle = getAngleDithering(centerPixelPos);

View file

@ -37,10 +37,10 @@ Rectangle {
"Level:obscuranceLevel:1.0:false", "Level:obscuranceLevel:1.0:false",
"Num Taps:numSamples:32:true", "Num Taps:numSamples:32:true",
"Taps Spiral:numSpiralTurns:10.0:false", "Taps Spiral:numSpiralTurns:10.0:false",
"Falloff Bias:falloffBias:0.2:false", "Falloff Angle:falloffAngle:0.2:false",
"Silhouette Radius:silhouetteRadius:1.0:false", "Falloff Distance:falloffDistance:2.0:false",
"Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false",
"Blur Radius:blurRadius:10.0:false", "Blur Radius:blurRadius:15.0:false",
] ]
ConfigSlider { ConfigSlider {
label: qsTr(modelData.split(":")[0]) label: qsTr(modelData.split(":")[0])