From 5e9355235ccbd4e6a832dc8354e37e27aa48e490 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 19 Apr 2018 17:34:28 +0200 Subject: [PATCH] First step for HBAO. Not working of course --- libraries/render-utils/src/ssao.slh | 147 ++++++++++++++---- .../render-utils/src/ssao_debugOcclusion.slf | 8 +- .../render-utils/src/ssao_makeOcclusion.slf | 70 ++++----- 3 files changed, 151 insertions(+), 74 deletions(-) diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index c0039265fa..262d6a77a2 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -11,11 +11,13 @@ <@if not SSAO_SLH@> <@def SSAO_SLH@> +#define SSAO_USE_HORIZON_BASED 1 + <@func declarePackOcclusionDepth()@> const float FAR_PLANE_Z = -300.0; -float CSZToDephtKey(float z) { +float CSZToDepthKey(float z) { return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0); } vec3 packOcclusionDepth(float occlusion, float depth) { @@ -156,24 +158,25 @@ float evalDiskRadius(float Zeye, vec2 imageSize) { return ssDiskRadius; } -const float TWO_PI = 6.28; +const float PI = 3.1415926; +const float TWO_PI = 6.2831852; -vec3 getUnitTapLocation(int sampleNumber, float spinAngle){ +vec3 getUnitTapLocation(int sampleNumber, float spiralTurns, float spinAngle, float angleRange){ // Radius relative to ssR float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + float angle = alpha * (spiralTurns * angleRange) + spinAngle; return vec3(cos(angle), sin(angle), alpha); } -vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { - vec3 tap = getUnitTapLocation(sampleNumber, spinAngle); +vec3 getTapLocationSSAO(int sampleNumber, float spinAngle, float outerRadius) { + vec3 tap = getUnitTapLocation(sampleNumber, getNumSpiralTurns(), spinAngle, TWO_PI); tap.xy *= tap.z; tap *= outerRadius; return tap; } -vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { - vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius); +vec3 getTapLocationClampedSSAO(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { + vec3 tap = getTapLocationSSAO(sampleNumber, spinAngle, outerRadius); vec2 tapPos = pixelPos + tap.xy; if (!(isBorderingEnabled() > 0.0)) { @@ -196,24 +199,7 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, tapPos.y -= (imageSize.y - tapPos.y); redoTap = true; } -/* - if ((tapPos.x < 0.5)) { - tapPos.x = 0.5; - redoTap = true; - } else if ((tapPos.x > imageSize.x - 0.5)) { - tapPos.x = imageSize.x - 0.5; - redoTap = true; - } - if ((tapPos.y < 0.5)) { - tapPos.y = 0.5; - redoTap = true; - } else if ((tapPos.y > imageSize.y - 0.5)) { - tapPos.y = imageSize.y - 0.5; - redoTap = true; - } -*/ - if (redoTap) { tap.xy = tapPos - pixelPos; tap.z = length(tap.xy); @@ -228,14 +214,17 @@ vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, <@func declareFetchDepthPyramidMap()@> - // the depth pyramid texture uniform sampler2D pyramidMap; -float getZEye(ivec2 pixel, int level) { +float getZEyeAtPixel(ivec2 pixel, int level) { return -texelFetch(pyramidMap, pixel, level).x; } +float getZEyeAtUV(vec2 texCoord, int level) { + return -texture(pyramidMap, texCoord, level).x; +} + const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; int evalMipFromRadius(float radius) { @@ -243,7 +232,6 @@ int evalMipFromRadius(float radius) { return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); } - vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { ivec2 ssP = ivec2(tap.xy) + ssC; ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); @@ -292,10 +280,18 @@ vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) { <@func declareEvalObscurance()@> -float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { - vec3 v = Q - C; +vec3 fastAcos(vec3 x) { + // [Eberly2014] GPGPU Programming for Games and Science + vec3 absX = abs(x); + vec3 res = absX * (-0.156583) + vec3(PI / 2.0); + res *= sqrt(vec3(1.0) - absX); + return mix(res, vec3(PI) - res, greaterThanEqual(x, vec3(0))); +} + +float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 tapPosition) { + vec3 v = tapPosition - centerPosition; float vv = dot(v, v); - float vn = dot(v, n_C); + float vn = dot(v, centerNormal); // Fall off function as recommended in SAO paper const float epsilon = 0.01; @@ -303,6 +299,95 @@ float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); } +vec2 searchHorizons(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { + vec2 searchVec = deltaTap * ssDiskRadius; + vec2 absSearchVec = abs(searchVec); + vec2 horizons = vec2(-PI/2.0, -PI/2.0); + vec3 centerPoint = vec3(centerPixelUV, centerDepth); + int stepIndex; + vec2 tapPixelUV; + + if (absSearchVec.x > absSearchVec.y) { + int stepCount = int(ceil(absSearchVec.x)); + + // Positive search for h2 + tapPixelUV = centerPixelUV; + deltaTap = (searchVec / searchVec.x) / imageSize; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.x < 1.0) { + tapPixelUV += deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.y = max(horizons.y, normalize(deltaVec).z); + --stepIndex; + } + + // Negative search for h1 + tapPixelUV = centerPixelUV; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.x > 0.0) { + tapPixelUV -= deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.x = max(horizons.x, normalize(deltaVec).z); + --stepIndex; + } + } else { + int stepCount = int(ceil(absSearchVec.y)); + + // Positive search for h2 + tapPixelUV = centerPixelUV; + deltaTap = (searchVec / searchVec.y) / imageSize; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.y < 1.0) { + tapPixelUV += deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.y = max(horizons.y, normalize(deltaVec).z); + --stepIndex; + } + + // Negative search for h1 + tapPixelUV = centerPixelUV; + stepIndex = stepCount; + while (stepIndex>0 && tapPixelUV.y > 0.0) { + tapPixelUV -= deltaTap; + // Don't clamp for the moment + + float tapDepth = getZEyeAtUV(tapPixelUV, 0); + vec3 deltaVec = vec3(tapPixelUV, tapDepth) - centerPoint; + + horizons.x = max(horizons.x, normalize(deltaVec).z); + --stepIndex; + } + } + + 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; +} + +float integrateArc(float h1, float h2) { + vec2 cosh = cos(vec2(h1, h2)); + return 2.0 - cosh.x - cosh.y; +} + +float evalVisibilityHBAO(int side, float centerDepth, vec2 centerPixelUV, vec2 imageSize, vec2 deltaTap, float ssDiskRadius, vec3 fragNormalES) { + vec2 horizonAngles = searchHorizons(side, centerDepth, centerPixelUV, imageSize, deltaTap, ssDiskRadius, fragNormalES); + return integrateArc(horizonAngles.x, horizonAngles.y); +} + <@endfunc@> <@func declareBlurPass(axis)@> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 6af457db67..1ba99f0aa9 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -50,7 +50,7 @@ void main(void) { ivec2 ssC = ivec2(cursorPixelPos); // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC, 0); + float Zeye = getZEyeAtPixel(ssC, 0); // Stereo side info ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); @@ -84,7 +84,7 @@ void main(void) { bool keep = false; for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); // The occluding point in camera space vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy; @@ -97,7 +97,7 @@ void main(void) { vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); + sum += float(tap.z > 0.0) * evalVisibilitySSAO(Cp, Cn, Q); } @@ -114,7 +114,7 @@ void main(void) { } !> - outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); + outFragColor = vec4(packOcclusionDepth(A, CSZToDepthKey(Cp.z)), 1.0); if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) { // outFragColor = vec4(vec3(A), 1.0); diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 4c808342c5..3919b3e1d1 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -24,63 +24,55 @@ void main(void) { // Pixel being shaded vec2 fragCoord = gl_FragCoord.xy; - ivec2 ssC = ivec2(fragCoord.xy); + ivec2 centerPixelPos = ivec2(fragCoord.xy); // Fetch the z under the pixel (stereo or not) - float Zeye = getZEye(ssC, 0); + float Zeye = getZEyeAtPixel(centerPixelPos, 0); // Stereo side info - ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel()); + ivec4 side = getStereoSideInfo(centerPixelPos.x, getResolutionLevel()); - // From now on, ssC is the pixel pos in the side - ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; + // From now on, centerPixelPos is the pixel pos in the side + centerPixelPos.x -= side.y; + vec2 fragUVPos = (vec2(centerPixelPos) + vec2(0.5)) / imageSize; // The position and normal of the pixel fragment in Eye space - vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); - vec3 Cn = evalEyeNormal(Cp); + vec3 fragPositionES = evalEyePositionFromZeye(side.x, Zeye, fragUVPos); + vec3 fragNormalES = evalEyeNormal(fragPositionES); // Choose the screen-space sample radius - float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - + float ssDiskRadius = evalDiskRadius(fragPositionES.z, imageSize); +#if SSAO_USE_HORIZON_BASED + ssDiskRadius = min(ssDiskRadius, 3.0); +#endif // Let's make noise - float randomPatternRotationAngle = getAngleDithering(ssC); - //vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz; - //float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp); + float randomPatternRotationAngle = getAngleDithering(centerPixelPos); - // Accumulate the Obscurance for each samples - float sum = 0.0; + // Accumulate the visibility for each samples + float visibilitySum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - - vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); - - vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - - sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q); +#if SSAO_USE_HORIZON_BASED + vec3 deltaTap = getUnitTapLocation(i, 1, randomPatternRotationAngle, PI); + visibilitySum += evalVisibilityHBAO(side.x, Zeye, fragUVPos, imageSize, deltaTap.xy, ssDiskRadius, fragNormalES); +#else + vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, ssDiskRadius, centerPixelPos, imageSize); + vec3 tapUVZ = fetchTap(side, centerPixelPos, tap, imageSize); + vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); + visibilitySum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); +#endif } - float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); + float occlusion = max(0.0, 1.0 - visibilitySum * getObscuranceScaling() * 5.0 * getInvNumSamples()); // KEEP IT for Debugging // Bilateral box-filter over a quad for free, respecting depth edges // (the difference that this makes is subtle) - if (abs(dFdx(Cp.z)) < 0.02) { - A -= dFdx(A) * ((ssC.x & 1) - 0.5); +/* if (abs(dFdx(fragPositionES.z)) < 0.02) { + occlusion -= dFdx(occlusion) * ((centerPixelPos.x & 1) - 0.5); } - if (abs(dFdy(Cp.z)) < 0.02) { - A -= dFdy(A) * ((ssC.y & 1) - 0.5); - } - - - outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); - - /* { - vec3 tap = getTapLocationClamped(2, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); - vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize); - vec2 fetchUV = vec2(tapUVZ.x + side.w * 0.5 * (side.x - tapUVZ.x), tapUVZ.y); - vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy); - outFragColor = vec4(fetchUV, 0.0, 1.0); + if (abs(dFdy(fragPositionES.z)) < 0.02) { + occlusion -= dFdy(occlusion) * ((centerPixelPos.y & 1) - 0.5); }*/ - + + outFragColor = vec4(packOcclusionDepth(occlusion, CSZToDepthKey(fragPositionES.z)), 1.0); }