From 135e10eaa276b8472c361ff5b1ade79a0d3379a0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Sep 2018 19:51:01 +0200 Subject: [PATCH] Packed occlusion depth for faster bilateral filtering --- .../src/AmbientOcclusionEffect.cpp | 23 ++- libraries/render-utils/src/ssao.slh | 145 ++++-------------- .../render-utils/src/ssao_bilateralBlur.slf | 115 +++++++++++++- .../render-utils/src/ssao_debugOcclusion.slf | 2 +- .../render-utils/src/ssao_makeOcclusion.slf | 2 +- libraries/render-utils/src/ssao_shared.h | 1 + 6 files changed, 164 insertions(+), 124 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 6084fc8f14..6a702b1640 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -82,17 +82,22 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { } void AmbientOcclusionFramebuffer::allocate() { +#if SSAO_BILATERAL_BLUR_USE_NORMAL + const auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA }; +#else + const auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB }; +#endif + // Full frame { auto width = _frameSize.x; auto height = _frameSize.y; - auto format = gpu::Element::COLOR_R_8; - _occlusionTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); + _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } @@ -128,9 +133,8 @@ void AmbientOcclusionFramebuffer::allocate() { } auto width = splitSize.x; auto height = splitSize.y; - auto format = gpu::Element::COLOR_R_8; - _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(format, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, + _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(occlusionformat, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP)); for (int i = 0; i < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); @@ -665,10 +669,15 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setModelTransform(model); } batch.setPipeline(bilateralBlurPipeline); - batch.setViewportTransform(firstBlurViewport); - batch.setFramebuffer(occlusionBlurredFBO); // Use full resolution depth and normal for bilateral upscaling and blur batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, linearDepthTexture); +#if SSAO_USE_QUAD_SPLIT + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); +#else + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, nullptr); +#endif + batch.setViewportTransform(firstBlurViewport); + batch.setFramebuffer(occlusionBlurredFBO); batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _hblurParametersBuffer); batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, occlusionFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index bc7473a3ba..a02554a385 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -16,20 +16,39 @@ <@func declarePackOcclusionDepth()@> -const float FAR_PLANE_Z = -300.0; +#define SSAO_DEPTH_KEY_SCALE 300.0 float CSZToDepthKey(float z) { - return clamp(z * (1.0 / FAR_PLANE_Z), 0.0, 1.0); + return clamp(z * (-1.0 / SSAO_DEPTH_KEY_SCALE), 0.0, 1.0); } -vec3 packOcclusionDepth(float occlusion, float depth) { + +vec4 packOcclusionOutput(float occlusion, float depth, vec3 eyeNormal) { + depth = CSZToDepthKey(depth); +#if SSAO_BILATERAL_BLUR_USE_NORMAL + return vec4(occlusion, depth, eyeNormal.xy / eyeNormal.z); +#else // Round to the nearest 1/256.0 - float temp = floor(depth * 256.0); - return vec3(occlusion, temp * (1.0 / 256.0), depth * 256.0 - temp); + depth *= 256; + float temp = floor(depth); + return vec4(occlusion, temp * (1.0 / 256.0), depth - temp, 0.0); +#endif } -vec2 unpackOcclusionDepth(vec3 raw) { - float z = raw.y * (256.0 / 257.0) + raw.z * (1.0 / 257.0); - return vec2(raw.x, z); + +void unpackOcclusionOutput(vec4 raw, out float occlusion, out float depth, out vec3 eyeNormal) { + occlusion = raw.x; +#if SSAO_BILATERAL_BLUR_USE_NORMAL + depth = raw.y; + eyeNormal = normalize(vec3(raw.zw, 1.0)); +#else + depth = (raw.y + raw.z / 256.0); + eyeNormal = vec3(0,0,1); +#endif } + +float unpackOcclusion(vec4 raw) { + return raw.x; +} + <@endfunc@> <@func declareAmbientOcclusion()@> @@ -252,6 +271,11 @@ vec3 getNormalEyeAtUV(vec2 texCoord, int level) { return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0)); } +vec3 getNormalEyeAtUV(ivec4 side, vec2 texCoord, int level) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return getNormalEyeAtUV(texCoord, level); +} + vec3 getNormalEyeAtPixel(ivec2 pixel, int level) { return normalize(texelFetch(normalTex, pixel, level).xyz*2.0 - vec3(1.0)); } @@ -418,109 +442,4 @@ float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 <@endfunc@> -<@func declareBlurPass()@> - -<$declareAmbientOcclusion()$> -<$declareFetchDepthPyramidMap()$> -<$declarePackOcclusionDepth()$> - -// the source occlusion texture -layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; - -layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { - AmbientOcclusionBlurParams blurParams; -}; - -vec2 getBlurOcclusionAxis() { - return blurParams._blurAxis.zw; -} - -vec2 getBlurDepthAxis() { - return blurParams._blurAxis.xy; -} - -vec2 getBlurOcclusionUVLimit() { - return blurParams._blurInfo.zw; -} - -float getBlurEdgeSharpness() { - return blurParams._blurInfo.x; -} - -int getBlurRadius() { - return int(blurParams._blurInfo.y); -} - -float fetchOcclusion(ivec4 side, vec2 texCoord) { - texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); - vec3 raw = textureLod(occlusionMap, texCoord, 0).xyz; - return raw.x; -} - -const float BLUR_EDGE_DISTANCE_SCALE = 1000.0; - -float evalBlurCoefficient(vec2 blurScales, float radialDistance, float zDistance) { - vec2 distances = vec2(radialDistance, zDistance); - return exp2(dot(blurScales, distances*distances)); -} - -vec2 evalTapWeightedValue(vec2 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth) { - vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; - vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); - - if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) - || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { - return vec2(0.0); - } - - float tapOcclusion = fetchOcclusion(side, tapOcclusionTexCoord); - - vec2 tapDepthTexCoord = getBlurDepthAxis() * r + depthTexCoord; - float tapDepth = getZEyeAtUV(side, tapDepthTexCoord, 0); - - // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - float zDistance = tapDepth - fragDepth; - float weight = evalBlurCoefficient(blurScales, abs(r), zDistance); - - return vec2(tapOcclusion * weight, weight); -} - -vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { - // Stereo side info - ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); - - float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); - vec2 weightedSums = vec2(0.0); - - // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range - int blurRadius = getBlurRadius(); - float blurRadialSigma = float(blurRadius) * 0.5; - float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); - vec2 blurScales = -vec2(blurRadialScale, BLUR_EDGE_DISTANCE_SCALE * getBlurEdgeSharpness()); - - // negative side first - for (int r = -blurRadius; r <= -1; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); - } - - // Central pixel contribution - float mainWeight = 1.0; - float pixelOcclusion = fetchOcclusion(side, occlusionTexCoord); - weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); - - // then positive side - for (int r = 1; r <= blurRadius; ++r) { - weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth); - } - - // Final normalization - const float epsilon = 0.0001; - float result = weightedSums.x / (weightedSums.y + epsilon); - - return vec3(result); -} - -<@endfunc@> - - <@endif@> diff --git a/libraries/render-utils/src/ssao_bilateralBlur.slf b/libraries/render-utils/src/ssao_bilateralBlur.slf index cadf00bf14..e85fc42b9e 100644 --- a/libraries/render-utils/src/ssao_bilateralBlur.slf +++ b/libraries/render-utils/src/ssao_bilateralBlur.slf @@ -15,12 +15,123 @@ // Hack comment -<$declareBlurPass()$> +<$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declarePackOcclusionDepth()$> + +// the source occlusion texture +layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap; + +layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer { + AmbientOcclusionBlurParams blurParams; +}; + +vec2 getBlurOcclusionAxis() { + return blurParams._blurAxis.zw; +} + +vec2 getBlurDepthAxis() { + return blurParams._blurAxis.xy; +} + +vec2 getBlurOcclusionUVLimit() { + return blurParams._blurInfo.zw; +} + +float getBlurEdgeSharpness() { + return blurParams._blurInfo.x; +} + +int getBlurRadius() { + return int(blurParams._blurInfo.y); +} + +vec4 fetchOcclusionPacked(ivec4 side, vec2 texCoord) { + texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo()); + return textureLod(occlusionMap, texCoord, 0); +} + +float evalBlurCoefficient(vec3 blurScales, float radialDistance, float zDistance, float normalDistance) { + vec3 distances = vec3(radialDistance, zDistance, normalDistance); + return exp2(dot(blurScales, distances*distances)); +} + +const float BLUR_EDGE_DISTANCE_SCALE = 1000 * SSAO_DEPTH_KEY_SCALE; +const float BLUR_EDGE_NORMAL_SCALE = 2.0; +const float BLUR_EDGE_NORMAL_LIMIT = 0.25; + +vec2 evalTapWeightedValue(vec3 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth, vec3 fragNormal) { + vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord; + vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit(); + + if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x) + || tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) { + return vec2(0.0); + } + + vec4 tapOcclusionPacked = fetchOcclusionPacked(side, tapOcclusionTexCoord); + float tapOcclusion; + float tapDepth; + vec3 tapNormal; + unpackOcclusionOutput(tapOcclusionPacked, tapOcclusion, tapDepth, tapNormal); + + // range domain (the "bilateral" weight). As depth difference increases, decrease weight. + float zDistance = tapDepth - fragDepth; +#if SSAO_BILATERAL_BLUR_USE_NORMAL + float normalDistance = BLUR_EDGE_NORMAL_LIMIT - min(BLUR_EDGE_NORMAL_LIMIT, dot(tapNormal, fragNormal)); +#else + float normalDistance = 0.0; +#endif + float weight = evalBlurCoefficient(blurScales, abs(r), zDistance, normalDistance); + + return vec2(tapOcclusion * weight, weight); +} + +vec4 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) { + // Stereo side info + ivec4 side = getStereoSideInfo(destPixelCoord.x, 0); + + float fragDepth = getZEyeAtUV(side, depthTexCoord, 0); + float fragDepthKey = CSZToDepthKey(fragDepth); +#if SSAO_BILATERAL_BLUR_USE_NORMAL + vec3 fragNormal = getNormalEyeAtUV(side, depthTexCoord, 0); +#else + vec3 fragNormal = vec3(0, 0, 1); +#endif + vec2 weightedSums = vec2(0.0); + + // Accumulate weighted contributions along the bluring axis in the [-radius, radius] range + int blurRadius = getBlurRadius(); + float blurRadialSigma = float(blurRadius) * 0.5; + float blurRadialScale = 1.0 / (2.0*blurRadialSigma*blurRadialSigma); + vec3 blurScales = -vec3(blurRadialScale, vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * getBlurEdgeSharpness()); + + // negative side first + for (int r = -blurRadius; r <= -1; ++r) { + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + } + + // Central pixel contribution + float mainWeight = 1.0; + float pixelOcclusion = unpackOcclusion(fetchOcclusionPacked(side, occlusionTexCoord)); + weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight); + + // then positive side + for (int r = 1; r <= blurRadius; ++r) { + weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepthKey, fragNormal); + } + + // Final normalization + const float epsilon = 0.0001; + float result = weightedSums.x / (weightedSums.y + epsilon); + + return packOcclusionOutput(result, fragDepth, fragNormal); +} layout(location=0) in vec4 varTexCoord0; layout(location=0) out vec4 outFragColor; void main(void) { - outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0); + outFragColor = getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw); } diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 2c1824dce7..efa6a02730 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -116,7 +116,7 @@ void main(void) { } !> - outFragColor = vec4(packOcclusionDepth(A, CSZToDepthKey(Cp.z)), 1.0); + outFragColor = packOcclusionOutput(A, Cp.z, vec3(0,0,1)); 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 e2a94be134..74617cc4bb 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -84,5 +84,5 @@ void main(void) { float occlusion = clamp(1.0 - obscuranceSum * getObscuranceScaling() * invNumSamples, 0.0, 1.0); - outFragColor = vec4(vec3(occlusion), 1.0); + outFragColor = packOcclusionOutput(occlusion * occlusion, fragPositionES.z, fragNormalES); } diff --git a/libraries/render-utils/src/ssao_shared.h b/libraries/render-utils/src/ssao_shared.h index 9c9e57337c..667dd702ef 100644 --- a/libraries/render-utils/src/ssao_shared.h +++ b/libraries/render-utils/src/ssao_shared.h @@ -15,6 +15,7 @@ #define RENDER_UTILS_SSAO_SHARED_H #define SSAO_USE_QUAD_SPLIT 1 +#define SSAO_BILATERAL_BLUR_USE_NORMAL 0 #if SSAO_USE_QUAD_SPLIT #define SSAO_SPLIT_LOG2_COUNT 2