Packed occlusion depth for faster bilateral filtering

This commit is contained in:
Olivier Prat 2018-09-27 19:51:01 +02:00
parent 1998096630
commit 135e10eaa2
6 changed files with 164 additions and 124 deletions

View file

@ -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);

View file

@ -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@>

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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