Finally, a clean HBAO!

This commit is contained in:
Olivier Prat 2018-09-27 10:44:06 +02:00
parent dee0a6afa2
commit 94a162893a
7 changed files with 134 additions and 105 deletions

View file

@ -222,7 +222,7 @@ AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() :
#else
numSamples{ 16 },
#endif
resolutionLevel{ 1 },
resolutionLevel{ 2 },
blurRadius{ 4 },
ditheringEnabled{ true },
borderingEnabled{ true },
@ -235,6 +235,9 @@ 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 };
}
AmbientOcclusionEffect::BlurParameters::BlurParameters() {
_blurInfo = { 1.0f, 3.0f, 2.0f, 0.0f };
}
@ -269,14 +272,11 @@ void AmbientOcclusionEffect::configure(const Config& config) {
current.y = 1.0f / (1.0f - config.falloffAngle);
}
if (config.edgeSharpness != _aoParametersBuffer->getEdgeSharpness()) {
auto& current = _aoParametersBuffer.edit()._blurInfo;
current.x = config.edgeSharpness;
}
if (config.blurDeviation != _aoParametersBuffer->getBlurDeviation()) {
auto& current = _aoParametersBuffer.edit()._blurInfo;
current.z = config.blurDeviation;
if (config.edgeSharpness != _hblurParametersBuffer.get().getEdgeSharpness()) {
auto& hblur = _hblurParametersBuffer.edit()._blurInfo;
auto& vblur = _vblurParametersBuffer.edit()._blurInfo;
hblur.x = config.edgeSharpness;
vblur.x = config.edgeSharpness;
}
if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) {
@ -327,9 +327,11 @@ void AmbientOcclusionEffect::configure(const Config& config) {
shouldUpdateBlurs = true;
}
if (config.blurRadius != _aoParametersBuffer.get().getBlurRadius()) {
auto& current = _aoParametersBuffer.edit()._blurInfo;
current.y = (float)config.blurRadius;
if (config.blurRadius != _hblurParametersBuffer.get().getBlurRadius()) {
auto& hblur = _hblurParametersBuffer.edit()._blurInfo;
auto& vblur = _vblurParametersBuffer.edit()._blurInfo;
hblur.y = (float)config.blurRadius;
vblur.y = (float)config.blurRadius;
}
if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) {
@ -353,31 +355,47 @@ void AmbientOcclusionEffect::updateBlurParameters() {
auto& vblur = _vblurParametersBuffer.edit();
auto& hblur = _hblurParametersBuffer.edit();
auto frameSize = _framebuffer->getSourceFrameSize();
if (_framebuffer->isStereo()) {
frameSize.x >>= 1;
}
const auto occlusionSize = divideRoundUp(frameSize, resolutionScale);
hblur.scaleHeight.x = 1.0f / frameSize.x;
hblur.scaleHeight.y = float(resolutionScale) / frameSize.x;
hblur.scaleHeight.z = frameSize.y / resolutionScale;
// Occlusion UV limit
hblur._blurInfo.z = occlusionSize.x / float(frameSize.x);
hblur._blurInfo.w = occlusionSize.y / float(frameSize.y);
vblur.scaleHeight.x = 1.0f / frameSize.y;
vblur.scaleHeight.y = float(resolutionScale) / frameSize.y;
vblur.scaleHeight.z = frameSize.y;
vblur._blurInfo.z = 1.0f;
vblur._blurInfo.w = occlusionSize.y / float(frameSize.y);
// Depth axis
hblur._blurAxis.x = 1.0f / occlusionSize.x;
hblur._blurAxis.y = 0.0f;
vblur._blurAxis.x = 0.0f;
vblur._blurAxis.y = 1.0f / occlusionSize.y;
// Occlusion axis
hblur._blurAxis.z = hblur._blurAxis.x * hblur._blurInfo.z;
hblur._blurAxis.w = 0.0f;
vblur._blurAxis.z = 0.0f;
vblur._blurAxis.w = vblur._blurAxis.y * vblur._blurInfo.w;
}
void AmbientOcclusionEffect::updateFramebufferSizes() {
auto& params = _aoParametersBuffer.edit();
const int widthScale = _framebuffer->isStereo() & 1;
auto sourceFrameSize = _framebuffer->getSourceFrameSize();
const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel();
const float resolutionScale = powf(0.5f, resolutionLevel);
// Depth is at maximum half depth
const int depthResolutionLevel = std::min(1, resolutionLevel);
const float depthResolutionScale = powf(2.0f, depthResolutionLevel);
auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions();
auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSize, depthResolutionLevel);
auto sourceFrameSideSize = _framebuffer->getSourceFrameSize();
sourceFrameSideSize.x >>= widthScale;
const int resolutionLevel = _aoParametersBuffer.get().getResolutionLevel();
// Depth is at maximum half depth
const int depthResolutionLevel = getDepthResolutionLevel();
const auto occlusionDepthFrameSize = divideRoundUp(sourceFrameSideSize, 1 << depthResolutionLevel);
const auto occlusionFrameSize = divideRoundUp(sourceFrameSideSize, 1 << resolutionLevel);
auto normalTextureSize = _framebuffer->getNormalTexture()->getDimensions();
sourceFrameSize.x >>= widthScale;
normalTextureSize.x >>= widthScale;
occlusionDepthFrameSize.x >>= widthScale;
params._sideSizes[0].x = normalTextureSize.x;
params._sideSizes[0].y = normalTextureSize.y;
@ -386,7 +404,7 @@ void AmbientOcclusionEffect::updateFramebufferSizes() {
params._sideSizes[1].x = params._sideSizes[0].x;
params._sideSizes[1].y = params._sideSizes[0].y;
auto occlusionSplitSize = divideRoundUp(sourceFrameSize, 1 << (resolutionLevel + SSAO_USE_QUAD_SPLIT));
auto occlusionSplitSize = divideRoundUp(occlusionFrameSize, SSAO_SPLIT_COUNT);
params._sideSizes[1].z = occlusionSplitSize.x;
params._sideSizes[1].w = occlusionSplitSize.y;
}
@ -491,7 +509,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
// We need to take the rounded up resolution.
auto occlusionViewport = divideRoundUp(sourceViewport, 1 << resolutionLevel);
auto firstBlurViewport = sourceViewport;
firstBlurViewport.w = divideRoundUp(firstBlurViewport.w, 1 << resolutionLevel);
firstBlurViewport.w = occlusionViewport.w;
if (!_gpuTimer) {
_gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__);
@ -550,9 +568,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
batch.resetViewTransform();
Transform model;
batch.setProjectionTransform(glm::mat4());
batch.setModelTransform(model);
batch.setModelTransform(Transform());
batch.pushProfileRange("Depth Mip Generation");
// We need this with the mips levels
@ -578,11 +595,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
postPixelOffset.y - prePixelOffset.y,
0.0f
);
Transform model;
model.setScale(uvScale);
model.setTranslation(uvTranslate);
batch.setModelTransform(model);
}
batch.setModelTransform(model);
// Build face normals pass
batch.setViewportTransform(normalViewport);
@ -608,11 +626,12 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture);
{
const auto uvScale = glm::vec3(
(splitSize.x * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale),
(splitSize.y * SSAO_SPLIT_COUNT * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale),
(splitSize.x * SSAO_SPLIT_COUNT) / float(occlusionViewport.z),
(splitSize.y * SSAO_SPLIT_COUNT) / float(occlusionViewport.w),
1.0f);
const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionDepthSize);
const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize.x, splitSize.y);
const auto postPixelOffset = glm::vec2(0.5f) / glm::vec2(occlusionViewport.z, occlusionViewport.w);
const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize);
Transform model;
batch.setViewportTransform(splitViewport);
@ -635,13 +654,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
}
#else
batch.setViewportTransform(occlusionViewport);
{
const auto uvScale = glm::vec3(
(occlusionViewport.z * depthResolutionScale) / (occlusionDepthSize.x * resolutionScale),
(occlusionViewport.w * depthResolutionScale) / (occlusionDepthSize.y * resolutionScale),
1.0f);
model.setScale(uvScale);
}
model.setIdentity();
batch.setModelTransform(model);
batch.setFramebuffer(occlusionFBO);
batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]);
@ -666,8 +679,15 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
PROFILE_RANGE_BATCH(batch, "Bilateral Blur");
// Blur 1st pass
batch.pushProfileRange("Horizontal");
model.setScale(resolutionScale);
batch.setModelTransform(model);
{
const auto uvScale = glm::vec3(
occlusionViewport.z / float(sourceViewport.z),
occlusionViewport.w / float(sourceViewport.w),
1.0f);
Transform model;
model.setScale(uvScale);
batch.setModelTransform(model);
}
batch.setViewportTransform(firstBlurViewport);
batch.setFramebuffer(occlusionBlurredFBO);
// Use full resolution depth and normal for bilateral upscaling and blur
@ -680,8 +700,16 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
// Blur 2nd pass
batch.pushProfileRange("Vertical");
model.setScale(glm::vec3(1.0f, resolutionScale, 1.0f));
batch.setModelTransform(model);
{
const auto uvScale = glm::vec3(
1.0f,
occlusionViewport.w / float(sourceViewport.w),
1.0f);
Transform model;
model.setScale(uvScale);
batch.setModelTransform(model);
}
batch.setViewportTransform(sourceViewport);
batch.setFramebuffer(occlusionFBO);
batch.setUniformBuffer(render_utils::slot::buffer::SsaoBlurParams, _vblurParametersBuffer);

View file

@ -149,14 +149,11 @@ public:
float getPerspectiveScale() const { return _resolutionInfo.z; }
float getObscuranceLevel() const { return _radiusInfo.w; }
float getFalloffAngle() const { return (float)_ditheringInfo.z; }
float getEdgeSharpness() const { return (float)_blurInfo.x; }
float getBlurDeviation() const { return _blurInfo.z; }
float getNumSpiralTurns() const { return _sampleInfo.z; }
int getNumSamples() const { return (int)_sampleInfo.x; }
bool isFetchMipsEnabled() const { return _sampleInfo.w; }
int getBlurRadius() const { return (int)_blurInfo.y; }
bool isDitheringEnabled() const { return _ditheringInfo.x; }
bool isBorderingEnabled() const { return _ditheringInfo.w; }
};
@ -165,11 +162,14 @@ public:
private:
// Class describing the uniform buffer with all the parameters common to the bilateral blur shaders
class BlurParameters {
class BlurParameters : public AmbientOcclusionBlurParams {
public:
glm::vec4 scaleHeight{ 0.0f };
BlurParameters() {}
BlurParameters();
float getEdgeSharpness() const { return (float)_blurInfo.x; }
int getBlurRadius() const { return (int)_blurInfo.y; }
};
using BlurParametersBuffer = gpu::StructBuffer<BlurParameters>;

View file

@ -115,14 +115,6 @@ int doFetchMips() {
return int(params._sampleInfo.w);
}
float getBlurEdgeSharpness() {
return params._blurInfo.x;
}
int getBlurRadius() {
return int(params._blurInfo.y);
}
<@endfunc@>
<@func declareSamplingDisk()@>
@ -247,6 +239,11 @@ float getZEyeAtUV(vec2 texCoord, int level) {
return -textureLod(depthPyramidTex, texCoord, level).x;
}
float getZEyeAtUV(ivec4 side, vec2 texCoord, int level) {
texCoord.x = mix(texCoord.x, (texCoord.x + getStereoSide(side)) * 0.5, isStereo());
return getZEyeAtUV(texCoord, level);
}
vec3 getNormalEyeAtUV(vec2 texCoord, int level) {
return normalize(textureLod(normalTex, texCoord, level).xyz*2.0 - vec3(1.0));
}
@ -417,37 +414,42 @@ float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2
<@endfunc@>
<@func declareBlurPass(axis)@>
<@func declareBlurPass()@>
<$declarePackOcclusionDepth()$>
<$declareAmbientOcclusion()$>
<$declareFetchDepthPyramidMap()$>
<$declarePackOcclusionDepth()$>
// the source occlusion texture
layout(binding=RENDER_UTILS_TEXTURE_SSAO_OCCLUSION) uniform sampler2D occlusionMap;
struct BlurParams {
vec4 scaleHeight;
};
layout(binding=RENDER_UTILS_BUFFER_SSAO_BLUR_PARAMS) uniform blurParamsBuffer {
BlurParams blurParams;
AmbientOcclusionBlurParams blurParams;
};
float getOcclusionBlurScale() {
return blurParams.scaleHeight.x;
vec2 getBlurOcclusionAxis() {
return blurParams._blurAxis.zw;
}
float getDepthBlurScale() {
return blurParams.scaleHeight.y;
vec2 getBlurDepthAxis() {
return blurParams._blurAxis.xy;
}
int getBlurImageHeight() {
return int(blurParams.scaleHeight.z);
vec2 getBlurOcclusionUVLimit() {
return blurParams._blurInfo.zw;
}
float fetchOcclusion(vec2 coords) {
vec3 raw = texture(occlusionMap, coords, 0).xyz;
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;
}
@ -458,19 +460,19 @@ float evalBlurCoefficient(vec2 blurScales, float radialDistance, float zDistance
return exp2(dot(blurScales, distances*distances));
}
vec2 evalTapWeightedValue(vec2 blurScales, ivec3 side, int r, ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord, float fragDepth) {
ivec2 tapOffset = <$axis$> * r;
ivec2 tapPixelCoord = destPixelCoord + ivec2(tapOffset);
vec2 evalTapWeightedValue(vec2 blurScales, ivec4 side, int r, vec2 occlusionTexCoord, vec2 depthTexCoord, float fragDepth) {
vec2 tapOcclusionTexCoord = getBlurOcclusionAxis() * r + occlusionTexCoord;
vec2 occlusionTexCoordLimits = getBlurOcclusionUVLimit();
if ((tapPixelCoord.x < side.y || tapPixelCoord.x >= side.z + side.y) || (tapPixelCoord.y < 0 || tapPixelCoord.y >= getBlurImageHeight())) {
if (tapOcclusionTexCoord.x < side.x || tapOcclusionTexCoord.x >= (side.x + occlusionTexCoordLimits.x)
|| tapOcclusionTexCoord.y < 0 || tapOcclusionTexCoord.y >= occlusionTexCoordLimits.y) {
return vec2(0.0);
}
vec2 tapTexCoord = scaledTexCoord + tapOffset * getOcclusionBlurScale();
float tapOcclusion = fetchOcclusion(tapTexCoord);
float tapOcclusion = fetchOcclusion(side, tapOcclusionTexCoord);
tapTexCoord = fullTexCoord + tapOffset * getDepthBlurScale();
float tapDepth = getZEyeAtUV(tapTexCoord, 0);
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;
@ -479,11 +481,11 @@ vec2 evalTapWeightedValue(vec2 blurScales, ivec3 side, int r, ivec2 destPixelCoo
return vec2(tapOcclusion * weight, weight);
}
vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTexCoord) {
vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 occlusionTexCoord, vec2 depthTexCoord) {
// Stereo side info
ivec4 side = getStereoSideInfo(destPixelCoord.x, 0);
float fragDepth = getZEyeAtUV(fullTexCoord, 0);
float fragDepth = getZEyeAtUV(side, depthTexCoord, 0);
vec2 weightedSums = vec2(0.0);
// Accumulate weighted contributions along the bluring axis in the [-radius, radius] range
@ -494,17 +496,17 @@ vec3 getBlurredOcclusion(ivec2 destPixelCoord, vec2 scaledTexCoord, vec2 fullTex
// negative side first
for (int r = -blurRadius; r <= -1; ++r) {
weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth);
weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth);
}
// Central pixel contribution
float mainWeight = 1.0;
float pixelOcclusion = fetchOcclusion(scaledTexCoord);
float pixelOcclusion = fetchOcclusion(side, occlusionTexCoord);
weightedSums += vec2(pixelOcclusion * mainWeight, mainWeight);
// then positive side
for (int r = 1; r <= blurRadius; ++r) {
weightedSums += evalTapWeightedValue(blurScales, side.xyz, r, destPixelCoord, scaledTexCoord, fullTexCoord, fragDepth);
weightedSums += evalTapWeightedValue(blurScales, side, r, occlusionTexCoord, depthTexCoord, fragDepth);
}
// Final normalization

View file

@ -28,11 +28,9 @@ void main(void) {
// Gather the four splits of the occlusion result back into an interleaved full size
// result (at the resolution level, of course)
ivec2 destPixelCoord = ivec2(gl_FragCoord.xy);
ivec2 sourcePixelCoord = destPixelCoord / SSAO_SPLIT_COUNT;
ivec2 splitImageSize = getWidthHeightRoundUp(getResolutionLevel()+SSAO_SPLIT_LOG2_COUNT);
ivec2 modPixelCoord = destPixelCoord % ivec2(SSAO_SPLIT_COUNT);
int occlusionMapIndex = modPixelCoord.x + modPixelCoord.y*SSAO_SPLIT_COUNT;
vec2 sourceUV = (sourcePixelCoord + vec2(0.5)) / splitImageSize;
ivec2 sourcePixelCoord = destPixelCoord >> SSAO_SPLIT_LOG2_COUNT;
ivec2 modPixelCoord = destPixelCoord & (SSAO_SPLIT_COUNT-1);
int occlusionMapIndex = modPixelCoord.x + (modPixelCoord.y << SSAO_SPLIT_LOG2_COUNT);
outFragColor = texture(occlusionMaps, vec3(sourceUV, occlusionMapIndex));
outFragColor = texelFetch(occlusionMaps, ivec3(sourcePixelCoord, occlusionMapIndex), 0);
}

View file

@ -15,8 +15,7 @@
// Hack comment
const ivec2 horizontal = ivec2(1,0);
<$declareBlurPass(horizontal)$>
<$declareBlurPass()$>
layout(location=0) in vec4 varTexCoord0;

View file

@ -14,14 +14,12 @@
// Hack comment
const ivec2 vertical = ivec2(0,1);
<$declareBlurPass(vertical)$>
<$declareBlurPass()$>
layout(location=0) in vec4 varTexCoord0;
layout(location=0) out vec4 outFragColor;
void main(void) {
float occlusion = getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw).x;
outFragColor = vec4(occlusion, 0.0, 0.0, occlusion);
outFragColor = vec4(getBlurredOcclusion(ivec2(gl_FragCoord.xy), varTexCoord0.xy, varTexCoord0.zw), 1.0);
}

View file

@ -27,10 +27,10 @@
// glsl / C++ compatible source as interface for ambient occlusion
#ifdef __cplusplus
# define SSAO_VEC4 glm::vec4
# define SSAO_IVEC4 glm::ivec4
# define SSAO_MAT4 glm::mat4
#else
# define SSAO_VEC4 vec4
# define SSAO_IVEC4 ivec4
# define SSAO_MAT4 mat4
#endif
struct AmbientOcclusionParams {
@ -38,7 +38,6 @@ struct AmbientOcclusionParams {
SSAO_VEC4 _radiusInfo;
SSAO_VEC4 _ditheringInfo;
SSAO_VEC4 _sampleInfo;
SSAO_VEC4 _blurInfo;
SSAO_VEC4 _sideSizes[2];
};
@ -46,6 +45,11 @@ struct AmbientOcclusionFrameParams {
SSAO_VEC4 _angleInfo;
};
struct AmbientOcclusionBlurParams {
SSAO_VEC4 _blurInfo;
SSAO_VEC4 _blurAxis;
};
#endif // RENDER_UTILS_SHADER_CONSTANTS_H
// <@if 1@>