mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 09:33:36 +02:00
Packed occlusion depth for faster bilateral filtering
This commit is contained in:
parent
1998096630
commit
135e10eaa2
6 changed files with 164 additions and 124 deletions
|
@ -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);
|
||||
|
|
|
@ -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@>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue