diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 9110285fc9..9df3c86d41 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -206,23 +206,87 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false), - radius{ 0.7f }, perspectiveScale{ 1.0f }, - obscuranceLevel{ 0.15f }, - falloffAngle{ 0.1f }, edgeSharpness{ 1.0f }, - blurDeviation{ 2.5f }, - numSpiralTurns{ 7.0f }, - numSamples{ 1 }, resolutionLevel{ 2 }, blurRadius{ 4 }, + + ssaoRadius{ 1.0f }, + ssaoObscuranceLevel{ 0.4f }, + ssaoFalloffAngle{ 0.1f }, + ssaoNumSamples{ 32 }, + ssaoNumSpiralTurns{ 7.0f }, + + hbaoRadius{ 0.7f }, + hbaoObscuranceLevel{ 0.75f }, + hbaoFalloffAngle{ 0.3f }, + hbaoNumSamples{ 1 }, + ditheringEnabled{ true }, borderingEnabled{ true }, fetchMipsEnabled{ true }, - horizonBased{ true }, + horizonBased{ false }, jitterEnabled{ false }{ } +void AmbientOcclusionEffectConfig::setSSAORadius(float newRadius) { + ssaoRadius = std::max(0.01f, newRadius); emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAOObscuranceLevel(float level) { + ssaoObscuranceLevel = std::max(0.01f, level); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAOFalloffAngle(float bias) { + ssaoFalloffAngle = std::max(0.0f, std::min(bias, 1.0f)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAONumSpiralTurns(float turns) { + ssaoNumSpiralTurns = std::max(0.0f, (float)turns); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setSSAONumSamples(int samples) { + ssaoNumSamples = std::max(1.0f, (float)samples); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAORadius(float newRadius) { + hbaoRadius = std::max(0.01f, newRadius); emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAOObscuranceLevel(float level) { + hbaoObscuranceLevel = std::max(0.01f, level); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAOFalloffAngle(float bias) { + hbaoFalloffAngle = std::max(0.0f, std::min(bias, 1.0f)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setHBAONumSamples(int samples) { + hbaoNumSamples = std::max(1.0f, (float)samples); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setEdgeSharpness(float sharpness) { + edgeSharpness = std::max(0.0f, (float)sharpness); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setResolutionLevel(int level) { + resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); + emit dirty(); +} + +void AmbientOcclusionEffectConfig::setBlurRadius(int radius) { + blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); + emit dirty(); +} + AmbientOcclusionEffect::AOParameters::AOParameters() { _resolutionInfo = glm::vec4{ 0.0f }; _radiusInfo = glm::vec4{ 0.0f }; @@ -242,42 +306,17 @@ void AmbientOcclusionEffect::configure(const Config& config) { DependencyManager::get()->setAmbientOcclusionEnabled(config.enabled); bool shouldUpdateBlurs = false; + bool shouldUpdateTechnique = false; _isJitterEnabled = config.jitterEnabled; - const double RADIUS_POWER = 6.0; - const auto& radius = config.radius; - if (radius != _aoParametersBuffer->getRadius() || config.horizonBased != _aoParametersBuffer->isHorizonBased()) { - auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.x = radius; - current.y = radius * radius; - current.z = 10.0f; - if (!config.horizonBased) { - current.z *= (float)(1.0 / pow((double)radius, RADIUS_POWER)); - } - } - - if (config.horizonBased != _aoParametersBuffer->isHorizonBased()) { - auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.y = config.horizonBased & 1; - } - - if (config.obscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { - auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.obscuranceLevel; - } - - if (config.falloffAngle != _aoParametersBuffer->getFalloffCosAngle()) { - auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.falloffAngle; - current.y = 1.0f / (1.0f - current.x); - // Compute sin from cos - current.z = sqrtf(1.0f - config.falloffAngle * config.falloffAngle); - current.w = 1.0f / current.z; + if (!_framebuffer) { + _framebuffer = std::make_shared(); + shouldUpdateBlurs = true; } // Update bilateral blur - { + if (config.blurRadius != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != config.edgeSharpness) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; @@ -287,6 +326,8 @@ void AmbientOcclusionEffect::configure(const Config& config) { float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * config.edgeSharpness); + _blurEdgeSharpness = config.edgeSharpness; + hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; @@ -298,33 +339,10 @@ void AmbientOcclusionEffect::configure(const Config& config) { vblur.w = (float)config.blurRadius; } - if (config.numSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.z = config.numSpiralTurns; - } - - if (config.numSamples != _aoParametersBuffer->getNumSamples()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.numSamples; - current.y = 1.0f / config.numSamples; - - // Regenerate offsets - const int B = 3; - const float invB = 1.0f / (float)B; - - for (int i = 0; i < _randomSamples.size(); i++) { - int index = i+1; // Indices start at 1, not 0 - float f = 1.0f; - float r = 0.0f; - - while (index > 0) { - f = f * invB; - r = r + f * (float)(index % B); - index = index / B; - } - _randomSamples[i] = r * 2.0f * M_PI / config.numSamples; - } - updateJitterSamples(); + if (_aoParametersBuffer->isHorizonBased() != config.horizonBased) { + auto& current = _aoParametersBuffer.edit()._resolutionInfo; + current.y = config.horizonBased & 1; + shouldUpdateTechnique = true; } if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { @@ -332,11 +350,6 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.fetchMipsEnabled; } - if (!_framebuffer) { - _framebuffer = std::make_shared(); - shouldUpdateBlurs = true; - } - if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { _aoParametersBuffer.edit()._resolutionInfo.z = config.perspectiveScale; } @@ -357,11 +370,102 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.w = (float)config.borderingEnabled; } + if (config.horizonBased) { + // Configure for HBAO + const auto& radius = config.hbaoRadius; + if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.x = radius; + current.y = radius * radius; + current.z = 1.0f / current.y; + } + + if (shouldUpdateTechnique || config.hbaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.w = config.hbaoObscuranceLevel; + } + + if (shouldUpdateTechnique || config.hbaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.hbaoFalloffAngle; + current.y = 1.0f / (1.0f - current.x); + // Compute sin from cos + current.z = sqrtf(1.0f - config.hbaoFalloffAngle * config.hbaoFalloffAngle); + current.w = 1.0f / current.z; + } + + if (shouldUpdateTechnique || config.hbaoNumSamples != _aoParametersBuffer->getNumSamples()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.x = config.hbaoNumSamples; + current.y = 1.0f / config.hbaoNumSamples; + updateRandomSamples(); + updateJitterSamples(); + } + } else { + // Configure for SSAO + const double RADIUS_POWER = 6.0; + const auto& radius = config.ssaoRadius; + if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.x = radius; + current.y = radius * radius; + current.z = (float)(10.0 / pow((double)radius, RADIUS_POWER)); + } + + if (shouldUpdateTechnique || config.ssaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + auto& current = _aoParametersBuffer.edit()._radiusInfo; + current.w = config.ssaoObscuranceLevel; + } + + if (shouldUpdateTechnique || config.ssaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + auto& current = _aoParametersBuffer.edit()._falloffInfo; + current.x = config.ssaoFalloffAngle; + } + + if (shouldUpdateTechnique || config.ssaoNumSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.z = config.ssaoNumSpiralTurns; + } + + if (shouldUpdateTechnique || config.ssaoNumSamples != _aoParametersBuffer->getNumSamples()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.x = config.ssaoNumSamples; + current.y = 1.0f / config.ssaoNumSamples; + updateRandomSamples(); + updateJitterSamples(); + } + } + if (shouldUpdateBlurs) { updateBlurParameters(); } } +void AmbientOcclusionEffect::updateRandomSamples() { + // Regenerate offsets + if (_aoParametersBuffer->isHorizonBased()) { + const int B = 3; + const float invB = 1.0f / (float)B; + float sampleScale = 2.0f * M_PI / _aoParametersBuffer->getNumSamples(); + + for (int i = 0; i < _randomSamples.size(); i++) { + int index = i + 1; // Indices start at 1, not 0 + float f = 1.0f; + float r = 0.0f; + + while (index > 0) { + f = f * invB; + r = r + f * (float)(index % B); + index = index / B; + } + _randomSamples[i] = r * sampleScale; + } + } else { + for (int i = 0; i < _randomSamples.size(); i++) { + _randomSamples[i] = randFloat() * 2.0f * M_PI; + } + } +} void AmbientOcclusionEffect::updateBlurParameters() { const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); auto& vblur = _vblurParametersBuffer.edit(); @@ -477,9 +581,14 @@ int AmbientOcclusionEffect::getDepthResolutionLevel() const { } void AmbientOcclusionEffect::updateJitterSamples() { - for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { - auto& sample = _aoFrameParametersBuffer[splitId].edit(); - sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + if (_aoParametersBuffer->isHorizonBased()) { + for (int splitId = 0; splitId < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; splitId++) { + auto& sample = _aoFrameParametersBuffer[splitId].edit(); + sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId]; + } + } else { + auto& sample = _aoFrameParametersBuffer[0].edit(); + sample._angleInfo.x = _randomSamples[_frameId]; } } @@ -594,57 +703,69 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte batch.setPipeline(occlusionPipeline); batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, occlusionDepthTexture); + if (_aoParametersBuffer->isHorizonBased()) { #if SSAO_USE_QUAD_SPLIT - batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); - { - const auto uvScale = glm::vec3( - (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(occlusionViewport.z, occlusionViewport.w); - const auto prePixelOffset = glm::vec2(0.5f * uvScale.x, 0.5f * uvScale.y) / glm::vec2(splitSize); - Transform model; + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); + { + const auto uvScale = glm::vec3( + (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(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); + batch.setViewportTransform(splitViewport); - model.setScale(uvScale); - for (int y = 0; y < SSAO_SPLIT_COUNT; y++) { - for (int x = 0; x < SSAO_SPLIT_COUNT; x++) { - const int splitIndex = x + y * SSAO_SPLIT_COUNT; - const auto uvTranslate = glm::vec3( - postPixelOffset.x * (2 * x + 1) - prePixelOffset.x, - postPixelOffset.y * (2 * y + 1) - prePixelOffset.y, - 0.0f + model.setScale(uvScale); + for (int y = 0; y < SSAO_SPLIT_COUNT; y++) { + for (int x = 0; x < SSAO_SPLIT_COUNT; x++) { + const int splitIndex = x + y * SSAO_SPLIT_COUNT; + const auto uvTranslate = glm::vec3( + postPixelOffset.x * (2 * x + 1) - prePixelOffset.x, + postPixelOffset.y * (2 * y + 1) - prePixelOffset.y, + 0.0f ); - model.setTranslation(uvTranslate); - batch.setModelTransform(model); - batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(splitIndex)); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[splitIndex]); - batch.draw(gpu::TRIANGLE_STRIP, 4); + model.setTranslation(uvTranslate); + batch.setModelTransform(model); + batch.setFramebuffer(_framebuffer->getOcclusionSplitFramebuffer(splitIndex)); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[splitIndex]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } } } - } #else - batch.setViewportTransform(occlusionViewport); - model.setIdentity(); - batch.setModelTransform(model); - batch.setFramebuffer(occlusionFBO); - batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); #endif + } else { +#if SSAO_USE_QUAD_SPLIT + batch.setResourceTexture(render_utils::slot::texture::SsaoNormal, occlusionNormalTexture); +#endif + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setUniformBuffer(render_utils::slot::buffer::SsaoFrameParams, _aoFrameParametersBuffer[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } batch.popProfileRange(); #if SSAO_USE_QUAD_SPLIT - // Gather back the four separate renders into one interleaved one - batch.pushProfileRange("Gather"); - batch.setViewportTransform(occlusionViewport); - batch.setModelTransform(Transform()); - batch.setFramebuffer(occlusionFBO); - batch.setPipeline(gatherPipeline); - batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, _framebuffer->getOcclusionSplitTexture()); - batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.popProfileRange(); + if (_aoParametersBuffer->isHorizonBased()) { + // Gather back the four separate renders into one interleaved one + batch.pushProfileRange("Gather"); + batch.setViewportTransform(occlusionViewport); + batch.setModelTransform(Transform()); + batch.setFramebuffer(occlusionFBO); + batch.setPipeline(gatherPipeline); + batch.setResourceTexture(render_utils::slot::texture::SsaoOcclusion, _framebuffer->getOcclusionSplitTexture()); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.popProfileRange(); + } #endif { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 29dc665961..a5b3ec1fdb 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -84,42 +84,61 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty) Q_PROPERTY(bool jitterEnabled MEMBER jitterEnabled NOTIFY dirty) - Q_PROPERTY(float radius MEMBER radius WRITE setRadius) - Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel) - Q_PROPERTY(float falloffAngle MEMBER falloffAngle WRITE setFalloffAngle) - Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) - Q_PROPERTY(float blurDeviation MEMBER blurDeviation WRITE setBlurDeviation) - Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns WRITE setNumSpiralTurns) - Q_PROPERTY(int numSamples MEMBER numSamples WRITE setNumSamples) + Q_PROPERTY(int resolutionLevel MEMBER resolutionLevel WRITE setResolutionLevel) + Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness WRITE setEdgeSharpness) Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) + // SSAO + Q_PROPERTY(float ssaoRadius MEMBER ssaoRadius WRITE setSSAORadius) + Q_PROPERTY(float ssaoObscuranceLevel MEMBER ssaoObscuranceLevel WRITE setSSAOObscuranceLevel) + Q_PROPERTY(float ssaoFalloffAngle MEMBER ssaoFalloffAngle WRITE setSSAOFalloffAngle) + Q_PROPERTY(float ssaoNumSpiralTurns MEMBER ssaoNumSpiralTurns WRITE setSSAONumSpiralTurns) + Q_PROPERTY(int ssaoNumSamples MEMBER ssaoNumSamples WRITE setSSAONumSamples) + + // HBAO + Q_PROPERTY(float hbaoRadius MEMBER hbaoRadius WRITE setHBAORadius) + Q_PROPERTY(float hbaoObscuranceLevel MEMBER hbaoObscuranceLevel WRITE setHBAOObscuranceLevel) + Q_PROPERTY(float hbaoFalloffAngle MEMBER hbaoFalloffAngle WRITE setHBAOFalloffAngle) + Q_PROPERTY(int hbaoNumSamples MEMBER hbaoNumSamples WRITE setHBAONumSamples) + public: AmbientOcclusionEffectConfig(); const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 15; - void setRadius(float newRadius) { radius = std::max(0.01f, newRadius); emit dirty(); } - void setObscuranceLevel(float level) { obscuranceLevel = std::max(0.01f, level); emit dirty(); } - void setFalloffAngle(float bias) { falloffAngle = std::max(0.0f, std::min(bias, 1.0f)); emit dirty(); } - void setEdgeSharpness(float sharpness) { edgeSharpness = std::max(0.0f, (float)sharpness); emit dirty(); } - void setBlurDeviation(float deviation) { blurDeviation = std::max(0.0f, deviation); emit dirty(); } - void setNumSpiralTurns(float turns) { numSpiralTurns = std::max(0.0f, (float)turns); emit dirty(); } - void setNumSamples(int samples) { numSamples = std::max(1.0f, (float)samples); emit dirty(); } - void setResolutionLevel(int level) { resolutionLevel = std::max(0, std::min(level, MAX_RESOLUTION_LEVEL)); emit dirty(); } - void setBlurRadius(int radius) { blurRadius = std::max(0, std::min(MAX_BLUR_RADIUS, radius)); emit dirty(); } + void setEdgeSharpness(float sharpness); + void setResolutionLevel(int level); + void setBlurRadius(int radius); + + void setSSAORadius(float newRadius); + void setSSAOObscuranceLevel(float level); + void setSSAOFalloffAngle(float bias); + void setSSAONumSpiralTurns(float turns); + void setSSAONumSamples(int samples); + + void setHBAORadius(float newRadius); + void setHBAOObscuranceLevel(float level); + void setHBAOFalloffAngle(float bias); + void setHBAONumSamples(int samples); - float radius; float perspectiveScale; - float obscuranceLevel; // intensify or dim down the obscurance effect - float falloffAngle; float edgeSharpness; - float blurDeviation; - float numSpiralTurns; // defining an angle span to distribute the samples ray directions - int numSamples; - int resolutionLevel; int blurRadius; // 0 means no blurring + int resolutionLevel; + + float ssaoRadius; + float ssaoObscuranceLevel; // intensify or dim down the obscurance effect + float ssaoFalloffAngle; + float ssaoNumSpiralTurns; // defining an angle span to distribute the samples ray directions + int ssaoNumSamples; + + float hbaoRadius; + float hbaoObscuranceLevel; // intensify or dim down the obscurance effect + float hbaoFalloffAngle; + int hbaoNumSamples; + bool horizonBased; // Use horizon based AO bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true @@ -154,7 +173,7 @@ public: float getRadius() const { return _radiusInfo.x; } float getPerspectiveScale() const { return _resolutionInfo.z; } float getObscuranceLevel() const { return _radiusInfo.w; } - float getFalloffCosAngle() const { return (float)_falloffInfo.x; } + float getFalloffAngle() const { return (float)_falloffInfo.x; } float getNumSpiralTurns() const { return _sampleInfo.z; } int getNumSamples() const { return (int)_sampleInfo.x; } @@ -176,7 +195,7 @@ private: BlurParameters(); float getEdgeSharpness() const { return (float)_blurInfo.x; } - int getBlurRadius() const { return (int)_blurInfo.y; } + int getBlurRadius() const { return (int)_blurInfo.w; } }; using BlurParametersBuffer = gpu::StructBuffer; @@ -185,6 +204,7 @@ private: void updateBlurParameters(); void updateFramebufferSizes(); + void updateRandomSamples(); void updateJitterSamples(); int getDepthResolutionLevel() const; @@ -193,6 +213,7 @@ private: FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; + float _blurEdgeSharpness{ 0.0f }; static const gpu::PipelinePointer& getOcclusionPipeline(); static const gpu::PipelinePointer& getBilateralBlurPipeline(); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 59d2198daa..4917cd31e1 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -86,7 +86,7 @@ void LinearDepthFramebuffer::allocate() { const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; // Point sampling of the depth, as well as the clamp to edge, are needed for the AmbientOcclusionEffect with HBAO const auto depthSamplerHalf = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_POINT, gpu::Sampler::WRAP_CLAMP); - _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, + _halfLinearDepthTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, depthSamplerHalf); _halfNormalTexture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 09c3cd3a66..2a80850eca 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -107,11 +107,14 @@ float getRadius2() { return params._radiusInfo.y; } float getInvRadius6() { + return mix(params._radiusInfo.z, 1.0, isHorizonBased()); +} +float getInvRadius2() { return params._radiusInfo.z; } float getObscuranceScaling() { - return params._radiusInfo.z * params._radiusInfo.w; + return getInvRadius6() * params._radiusInfo.w; } float isDitheringEnabled() { @@ -161,15 +164,24 @@ float getAngleDitheringWorldPos(in vec3 pixelWorldPos) { return isDitheringEnabled() * float(((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10); } +float getAngleDitheringSplit() { + return isDitheringEnabled() * frameParams._angleInfo.x; +} + float getAngleDithering(in ivec2 pixelPos) { #if SSAO_USE_QUAD_SPLIT - return isDitheringEnabled() * frameParams._angleInfo.x; + return getAngleDitheringSplit(); #else // Hash function used in the AlchemyAO paper - return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); + return getAngleDitheringPixelPos(pixelPos); #endif } +float getAngleDitheringPixelPos(in ivec2 pixelPos) { + // Hash function used in the AlchemyAO paper + return isDitheringEnabled() * float((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10); +} + float evalDiskRadius(float Zeye, vec2 sideImageSize) { // Choose the screen-space sample radius // proportional to the projected area of the sphere @@ -413,7 +425,7 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t #define HBAO_USE_OVERHANG_HACK 0 float computeWeightForHorizon(float horizonLimit, float distanceSquared) { - return max(0.0, 1.0 - distanceSquared / getRadius2()); + return max(0.0, 1.0 - distanceSquared * getInvRadius2()); } float computeWeightedHorizon(float horizonLimit, float distanceSquared) { diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index aabc28a326..ec747062e8 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -37,13 +37,18 @@ void main(void) { // Stereo side info based on the real viewport size of this pass vec2 sideDepthSize = getDepthTextureSideSize(0); - ivec2 sideOcclusionSize = ivec2( getOcclusionSplitSideSize() ); + ivec2 sideOcclusionSize; + if (isHorizonBased()) { + sideOcclusionSize = ivec2( getOcclusionSplitSideSize() ); + } else { + sideOcclusionSize = ivec2( getOcclusionSideSize() ); + } ivec4 side = getStereoSideInfoFromWidth(fragPixelPos.x, sideOcclusionSize.x); // From now on, fragUVPos is the UV pos in the side fragUVPos.x = mix(fragUVPos.x, fragUVPos.x * 2.0 - getStereoSide(side), isStereo()); fragUVPos = snapToTexel(fragUVPos, sideDepthSize); - + // The position and normal of the pixel fragment in Eye space vec2 deltaDepthUV = vec2(2.0) / sideDepthSize; vec3 fragPositionES = buildPosition(side, fragUVPos); @@ -58,7 +63,7 @@ void main(void) { } // Let's make noise - float randomPatternRotationAngle = getAngleDithering(fragPixelPos); + float randomPatternRotationAngle = 0.0; // Accumulate the obscurance for each samples float obscuranceSum = 0.0; @@ -66,22 +71,24 @@ void main(void) { float invNumSamples = getInvNumSamples(); if (isHorizonBased()) { + randomPatternRotationAngle = getAngleDithering(fragPixelPos); + for (int i = 0; i < numSamples; ++i) { vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI); obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES); } obscuranceSum *= invNumSamples; #if HBAO_USE_COS_ANGLE - obscuranceSum *= 0.5; obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); #else - obscuranceSum *= 0.5; obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling()); #endif } else { // Steps are in the depth texture resolution vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize; + randomPatternRotationAngle = getAngleDitheringPixelPos(fragPixelPos) + getAngleDitheringSplit(); + for (int i = 0; i < numSamples; ++i) { vec3 tap = getTapLocationClampedSSAO(i, randomPatternRotationAngle, diskPixelRadius, depthTexFragPixelPos, sideDepthSize); vec2 tapUV = fragUVPos + tap.xy * deltaDepthUV; @@ -90,7 +97,7 @@ void main(void) { obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES); } obscuranceSum *= invNumSamples; - obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling(); + obscuranceSum = 1.0 - obscuranceSum * getObscuranceScaling(); } float occlusion = clamp(obscuranceSum, 0.0, 1.0); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 91e385edb8..d6323c351a 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -33,11 +33,6 @@ Rectangle { Repeater { model: [ - "Radius:radius:2.0:false", - "Level:obscuranceLevel:1.0:false", - "Num Taps:numSamples:16:true", - "Taps Spiral:numSpiralTurns:10.0:false", - "Falloff Angle:falloffAngle:1.0:false", "Blur Edge Sharpness:edgeSharpness:1.0:false", "Blur Radius:blurRadius:15.0:true", "Resolution Downscale:resolutionLevel:2:true", @@ -102,5 +97,81 @@ Rectangle { } ] } + + + + TabView { + anchors.left: parent.left + anchors.right: parent.right + height: 400 + + Tab { + title: "SSAO" + + Rectangle { + color: hifi.colors.baseGray; + + Column { + spacing: 8 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + Repeater { + model: [ + "Radius:ssaoRadius:2.0:false", + "Level:ssaoObscuranceLevel:1.0:false", + "Num Taps:ssaoNumSamples:64:true", + "Taps Spiral:ssaoNumSpiralTurns:10.0:false", + "Falloff Angle:ssaoFalloffAngle:1.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("RenderMainView.AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + height:38 + } + } + } + } + } + + Tab { + title: "HBAO" + + Rectangle { + color: hifi.colors.baseGray; + + Column { + spacing: 8 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + Repeater { + model: [ + "Radius:hbaoRadius:2.0:false", + "Level:hbaoObscuranceLevel:1.0:false", + "Num Taps:hbaoNumSamples:6:true", + "Falloff Angle:hbaoFalloffAngle:1.0:false", + ] + ConfigSlider { + label: qsTr(modelData.split(":")[0]) + integral: (modelData.split(":")[3] == 'true') + config: Render.getConfig("RenderMainView.AmbientOcclusion") + property: modelData.split(":")[1] + max: modelData.split(":")[2] + min: 0.0 + height:38 + } + } + } + } + } + } + } }