From 7f6c9a6cc1fb1677a3e7be8d61ea492545e9467d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 1 Oct 2018 19:22:54 +0200 Subject: [PATCH] Switched to sin based HBAO --- .../src/AmbientOcclusionEffect.cpp | 20 ++- .../render-utils/src/AmbientOcclusionEffect.h | 2 +- libraries/render-utils/src/ssao.slh | 137 +++++++++++++----- .../render-utils/src/ssao_makeOcclusion.slf | 25 +++- libraries/render-utils/src/ssao_shared.h | 1 + .../utilities/render/ambientOcclusionPass.qml | 2 +- 6 files changed, 135 insertions(+), 52 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 86de3cec04..54edfad3ec 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -206,10 +206,10 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), - radius{ 0.3f }, + radius{ 0.5f }, perspectiveScale{ 1.0f }, - obscuranceLevel{ 0.5f }, - falloffAngle{ 0.45f }, + obscuranceLevel{ 0.25f }, + falloffAngle{ 0.7f }, edgeSharpness{ 1.0f }, blurDeviation{ 2.5f }, numSpiralTurns{ 7.0f }, @@ -227,6 +227,7 @@ AmbientOcclusionEffect::AOParameters::AOParameters() { _radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f }; _ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f }; _sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f }; + _falloffInfo = { 0.5f, 2.0f, 0.866f, 1.1547f }; } AmbientOcclusionEffect::BlurParameters::BlurParameters() { @@ -263,10 +264,13 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = config.obscuranceLevel; } - if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.z = config.falloffAngle; - current.y = 1.0f / (1.0f - config.falloffAngle); + if (config.falloffAngle != _aoParametersBuffer->getFalloffCosAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.falloffAngle; + current.y = 1.0f / (1.0f - current.x); + // Compute sin from cos + current.z = sqrtf(1.0f - config.falloffAngle * config.falloffAngle); + current.w = 1.0f / current.z; } // Update bilateral blur @@ -535,7 +539,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto& sample = _aoFrameParametersBuffer[splitId].edit(); sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; } - _frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; + //_frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT; gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "SSAO"); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 50b35f7941..8d5eef42a5 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -150,7 +150,7 @@ public: float getRadius() const { return _radiusInfo.x; } float getPerspectiveScale() const { return _resolutionInfo.z; } float getObscuranceLevel() const { return _radiusInfo.w; } - float getFalloffAngle() const { return (float)_ditheringInfo.z; } + float getFalloffCosAngle() const { return (float)_falloffInfo.x; } float getNumSpiralTurns() const { return _sampleInfo.z; } int getNumSamples() const { return (int)_sampleInfo.x; } diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index a5bb689d06..a289654486 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -115,11 +115,18 @@ float isBorderingEnabled() { return params._ditheringInfo.w; } -float getFalloffAngle() { - return params._ditheringInfo.z; +float getFalloffCosAngle() { + return params._falloffInfo.x; } -float getFalloffAngleScale() { - return params._ditheringInfo.y; +float getFalloffCosAngleScale() { + return params._falloffInfo.y; +} + +float getFalloffSinAngle() { + return params._falloffInfo.z; +} +float getFalloffSinAngleScale() { + return params._falloffInfo.w; } float getNumSamples() { @@ -330,10 +337,27 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); - vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); - vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); + vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); + vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); - return normalize( cross(fragPositionDx, fragPositionDy) ); + return normalize( cross(fragDeltaDx, fragDeltaDy) ); +} + +void buildTangentBinormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec3 fragNormal, vec2 deltaDepthUV, + out vec3 fragTangent, out vec3 fragBinormal) { + vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV); + vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV); + vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV); + vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV); + + vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg); + vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg); + + //fragTangent = normalize( cross(fragDeltaDy, fragNormal) ); + //fragBinormal = normalize( cross(fragNormal, fragDeltaDx) ); + + fragTangent = fragDeltaDx; + fragBinormal = fragDeltaDy; } <@endfunc@> @@ -341,6 +365,12 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU <@func declareEvalObscurance()@> +struct TBNFrame { + vec3 tangent; + vec3 binormal; + vec3 normal; +}; + vec3 fastAcos(vec3 x) { // [Eberly2014] GPGPU Programming for Games and Science vec3 absX = abs(x); @@ -357,29 +387,25 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t // Falloff function as recommended in SSAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); - return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0); + return f * f * f * max((vn - getFalloffCosAngle()) / (epsilon + vv), 0.0); } -vec2 computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { - const float epsilon = 0.001; +#define HBAO_USE_COS_ANGLE 0 - vec3 deltaVec = tapPositionES - fragPositionES; - float distance = length(deltaVec); - float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon); +float computeWeightedHorizon(float horizonLimit, float distanceSquared) { + float radiusFalloff = distanceSquared / getRadius2(); - return vec2(cosHorizonAngle, distance); -} + radiusFalloff = max(0.0, 1.0 - radiusFalloff); -float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) { - vec2 rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); - float distance = rawHorizon.y; - float cosHorizonAngle = rawHorizon.x; - float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2())); +#if !HBAO_USE_COS_ANGLE + horizonLimit = getFalloffSinAngle() - horizonLimit; +#endif + horizonLimit *= radiusFalloff; +#if !HBAO_USE_COS_ANGLE + horizonLimit = getFalloffSinAngle() - horizonLimit; +#endif - cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale()); - cosHorizonAngle *= radiusFalloff; - - return cosHorizonAngle; + return horizonLimit; } <@func computeHorizon()@> @@ -389,24 +415,46 @@ float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec } vec2 tapMipZ = fetchTap(side, fragUVPos, radius); vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos); - float tapCosHorizonAngle = computeWeightedHorizonFromTap(tapPositionES, fragPositionES, fragNormalES); + vec3 deltaVec = tapPositionES - fragPositionES; + float distanceSquared = dot(deltaVec, deltaVec); + float distance = sqrt(distanceSquared); + float deltaDotNormal = dot(deltaVec, fragFrameES.normal); +#if HBAO_USE_COS_ANGLE + float tapHorizonLimit = deltaDotNormal; +#else + float tapHorizonLimit = dot(deltaVec, fragFrameES.tangent); +#endif + float epsilon = 0.0001; + tapHorizonLimit /= (distance + epsilon); - cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle); - + if (distanceSquared < getRadius2() && deltaDotNormal>0.0 && +#if HBAO_USE_COS_ANGLE + tapHorizonLimit > horizonLimit +#else + tapHorizonLimit < horizonLimit +#endif + ) { + tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared); +#if HBAO_USE_COS_ANGLE + horizonLimit = max(horizonLimit, tapHorizonLimit); +#else + horizonLimit = min(horizonLimit, tapHorizonLimit); +#endif + } <@endfunc@> #define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0 -float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec, - float searchRadius) { +float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchVec, float searchRadius, int stepCount) { vec2 absSearchVec = abs(searchVec); - pixelSearchVec = abs(pixelSearchVec); - int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); - float cosHorizonAngle = 0.0; +#if HBAO_USE_COS_ANGLE + float horizonLimit = getFalloffCosAngle(); +#else + float horizonLimit = getFalloffSinAngle(); +#endif if (stepCount>0) { vec2 deltaTapUV = searchVec / float(stepCount); - float deltaRadius = searchRadius / float(stepCount); #if HBAO_HORIZON_SEARCH_CONSTANT_STEP @@ -439,22 +487,33 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragN #endif } - return cosHorizonAngle; + return horizonLimit; } float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius, - vec3 fragPositionES, vec3 fragNormalES) { + vec3 fragPositionES, TBNFrame fragFrameES) { vec2 pixelSearchVec = deltaTap * diskPixelRadius; vec2 searchVec = pixelSearchVec * invSideImageSize; - float obscurance = 0.0; + float obscuranceH1 = 0.0; + float obscuranceH2 = 0.0; + +#if !HBAO_USE_COS_ANGLE + fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y); +#endif + + pixelSearchVec = abs(pixelSearchVec); + int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y))); // Forward search for h1 - obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, pixelSearchVec, diskPixelRadius); + obscuranceH1 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, searchVec, diskPixelRadius, stepCount); // Backward search for h2 - obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, pixelSearchVec, diskPixelRadius); +#if !HBAO_USE_COS_ANGLE + fragFrameES.tangent = -fragFrameES.tangent; +#endif + obscuranceH2 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, -searchVec, diskPixelRadius, stepCount); - return obscurance; + return obscuranceH1 + obscuranceH2; } <@endfunc@> diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index cfccf32938..76357ffbb9 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -55,6 +55,15 @@ void main(void) { diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS); } + TBNFrame fragFrameES; + + fragFrameES.tangent = vec3(0.0); + fragFrameES.binormal = vec3(0.0); + fragFrameES.normal = fragNormalES; +#if !HBAO_USE_COS_ANGLE + buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal); +#endif + // Let's make noise float randomPatternRotationAngle = getAngleDithering(fragPixelPos); @@ -66,9 +75,18 @@ void main(void) { if (isHorizonBased()) { for (int i = 0; i < numSamples; ++i) { vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); - obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); + obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragFrameES); } + obscuranceSum *= invNumSamples; +#if HBAO_USE_COS_ANGLE obscuranceSum *= 0.5 / PI; + obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); +#else + obscuranceSum *= 0.5; + obscuranceSum += 1.0 - getFalloffSinAngle(); + + obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); +#endif } else { // Steps are in the depth texture resolution vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; @@ -80,10 +98,11 @@ void main(void) { vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV); obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); } + obscuranceSum *= invNumSamples; + obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); } - obscuranceSum *= getObscuranceScaling() * invNumSamples; - float occlusion = clamp(1.0 - obscuranceSum * obscuranceSum, 0.0, 1.0); + float occlusion = clamp(obscuranceSum, 0.0, 1.0); outFragColor = packOcclusionOutput(occlusion, fragPositionES.z, fragNormalES); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 1552a38947..46e373c073 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -40,6 +40,7 @@ struct AmbientOcclusionParams { SSAO_VEC4 _radiusInfo; SSAO_VEC4 _ditheringInfo; SSAO_VEC4 _sampleInfo; + SSAO_VEC4 _falloffInfo; SSAO_VEC4 _sideSizes[2]; }; diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 5197fc312d..2ea3a6c416 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -37,7 +37,7 @@ Rectangle { "Level:obscuranceLevel:1.0:false", "Num Taps:numSamples:16:true", "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Angle:falloffAngle:0.5:false", + "Falloff Angle:falloffAngle:1.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:true", "Resolution Downscale:resolutionLevel:2:true",