//// AmbientOcclusionEffect.cpp// libraries/render-utils/src///// Created by Niraj Venkat on 7/15/15.// Copyright 2015 High Fidelity, Inc.// Copyright 2024 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "AmbientOcclusionEffect.h" #include <algorithm> //min max and more #include <glm/gtc/random.hpp> #include <PathUtils.h>#include <SharedUtil.h>#include <gpu/Context.h>#include <shaders/Shaders.h>#include <render/ShapePipeline.h>#include <MathUtils.h> #include "RenderUtilsLogging.h" #include "render-utils/ShaderConstants.h"#include "DeferredLightingEffect.h"#include "TextureCache.h"#include "FramebufferCache.h"#include "DependencyManager.h"#include "ViewFrustum.h" gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline;gpu::PipelinePointer AmbientOcclusionEffect::_bilateralBlurPipeline;gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline;gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline;gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; #define MAX_SSAO_SAMPLES 64.0f#define MAX_HBAO_SAMPLES 6.0f AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() {} bool AmbientOcclusionFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, int depthResolutionLevel, bool isStereo) { // If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if (_linearDepthTexture != linearDepthBuffer) { _linearDepthTexture = linearDepthBuffer; reset = true; } if (_resolutionLevel != resolutionLevel || isStereo != _isStereo || _depthResolutionLevel != depthResolutionLevel) { _resolutionLevel = resolutionLevel; _depthResolutionLevel = depthResolutionLevel; _isStereo = isStereo; reset = true; } if (_linearDepthTexture) { auto newFrameSize = glm::ivec2(_linearDepthTexture->getDimensions()); if (_frameSize != newFrameSize) { _frameSize = newFrameSize; reset = true; } } if (reset) { clear(); } return reset;} void AmbientOcclusionFramebuffer::clear() { _occlusionFramebuffer.reset(); _occlusionTexture.reset(); _occlusionBlurredFramebuffer.reset(); _occlusionBlurredTexture.reset(); _normalFramebuffer.reset(); _normalTexture.reset();} gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { return _linearDepthTexture;} void AmbientOcclusionFramebuffer::allocate() {#if SSAO_BILATERAL_BLUR_USE_NORMAL auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA };#else // Format used on OpenGL is typically not supported on Vulkan and framebuffer creation will fail.#ifdef USE_GL auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB };#else auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::NUINT8, gpu::RGBA };#endif#endif // Full frame { auto width = _frameSize.x; auto height = _frameSize.y; auto sampler = Sampler(Sampler::FILTER_MIN_MAG_LINEAR, Sampler::WRAP_CLAMP); _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } // Lower res frame { auto sideSize = _frameSize; if (_isStereo) { sideSize.x >>= 1; } sideSize >>= _resolutionLevel; if (_isStereo) { sideSize.x <<= 1; } auto width = sideSize.x; auto height = sideSize.y; // Format used on OpenGL is typically not supported on Vulkan and framebuffer creation will fail.#ifdef USE_GL auto format = gpu::Element{ gpu::VEC4, gpu::NINT2_10_10_10, gpu::RGBA };#else auto format = gpu::Element{ gpu::VEC4, gpu::NINT8, gpu::RGBA };#endif _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_POINT, Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } #if SSAO_USE_QUAD_SPLIT { auto splitSize = _frameSize; if (_isStereo) { splitSize.x >>= 1; } splitSize = divideRoundUp(_frameSize >> _resolutionLevel, SSAO_SPLIT_COUNT); if (_isStereo) { splitSize.x <<= 1; } auto width = splitSize.x; auto height = splitSize.y; _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(occlusionformat, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_LINEAR, Sampler::WRAP_CLAMP)); for (int i = 0; i < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionSplitFramebuffers[i]->setRenderBuffer(0, _occlusionSplitTexture, i); } }#endif} #if SSAO_USE_QUAD_SPLITgpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionSplitFramebuffer(int index) { assert(index < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT); if (!_occlusionSplitFramebuffers[index]) { allocate(); } return _occlusionSplitFramebuffers[index];} gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionSplitTexture() { if (!_occlusionSplitTexture) { allocate(); } return _occlusionSplitTexture;}#endif gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { if (!_occlusionFramebuffer) { allocate(); } return _occlusionFramebuffer;} gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionTexture() { if (!_occlusionTexture) { allocate(); } return _occlusionTexture;} gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionBlurredFramebuffer() { if (!_occlusionBlurredFramebuffer) { allocate(); } return _occlusionBlurredFramebuffer;} gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { if (!_occlusionBlurredTexture) { allocate(); } return _occlusionBlurredTexture;} gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer() { if (!_normalFramebuffer) { allocate(); } return _normalFramebuffer;} gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { if (!_normalTexture) { allocate(); } return _normalTexture;} AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion") {} 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); _ditheringInfo = glm::vec4(0.0f); _sampleInfo = glm::vec4(0.0f); _falloffInfo = glm::vec4(0.0f);} AmbientOcclusionEffect::BlurParameters::BlurParameters() { _blurInfo = { 1.0f, 2.0f, 0.0f, 3.0f };} void AmbientOcclusionEffect::configure(const Config& config) { _debug = config.debug; _debugAmbientOcclusion->setTechnique(config.horizonBased ? AmbientOcclusionTechnique::HBAO : AmbientOcclusionTechnique::SSAO); _debugAmbientOcclusion->setJitter(config.jitterEnabled); _debugAmbientOcclusion->setResolutionLevel(config.resolutionLevel); _debugAmbientOcclusion->setEdgeSharpness(config.edgeSharpness); _debugAmbientOcclusion->setBlurRadius(config.blurRadius); _debugAmbientOcclusion->setAORadius(config.horizonBased ? config.hbaoRadius : config.ssaoRadius); _debugAmbientOcclusion->setAOObscuranceLevel(config.horizonBased ? config.hbaoObscuranceLevel : config.ssaoObscuranceLevel); _debugAmbientOcclusion->setAOFalloffAngle(config.horizonBased ? config.hbaoFalloffAngle : config.ssaoFalloffAngle); _debugAmbientOcclusion->setAOSamplingAmount(config.horizonBased ? (config.hbaoNumSamples / MAX_HBAO_SAMPLES) : (config.ssaoNumSamples / MAX_SSAO_SAMPLES)); _debugAmbientOcclusion->setSSAONumSpiralTurns(config.ssaoNumSpiralTurns); _perspectiveScale = config.perspectiveScale; _ditheringEnabled = config.ditheringEnabled; _borderingEnabled = config.borderingEnabled; _fetchMipsEnabled = config.fetchMipsEnabled;} void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion) { bool shouldUpdateBlurs = false; bool shouldUpdateTechnique = false; if (!_framebuffer) { _framebuffer = std::make_shared<AmbientOcclusionFramebuffer>(); shouldUpdateBlurs = true; } // Update bilateral blur if (ambientOcclusion->getBlurRadius() != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != ambientOcclusion->getEdgeSharpness()) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; auto& hblur = _hblurParametersBuffer.edit()._blurInfo; auto& vblur = _vblurParametersBuffer.edit()._blurInfo; float blurRadialSigma = float(ambientOcclusion->getBlurRadius()) * 0.5f; float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * ambientOcclusion->getEdgeSharpness()); _blurEdgeSharpness = ambientOcclusion->getEdgeSharpness(); hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; hblur.w = (float)ambientOcclusion->getBlurRadius(); vblur.x = blurScales.x; vblur.y = blurScales.y; vblur.z = blurScales.z; vblur.w = (float)ambientOcclusion->getBlurRadius(); } if (_perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { _aoParametersBuffer.edit()._resolutionInfo.z = _perspectiveScale; } if (_ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.x = (float)_ditheringEnabled; } if (_borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.w = (float)_borderingEnabled; } if (_fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.w = (float)_fetchMipsEnabled; } bool horizonBased = ambientOcclusion->getTechnique() == AmbientOcclusionTechnique::HBAO; if (_aoParametersBuffer->isHorizonBased() != horizonBased) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.y = horizonBased & 1; shouldUpdateTechnique = true; } if (ambientOcclusion->getResolutionLevel() != _aoParametersBuffer->getResolutionLevel()) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.x = (float)ambientOcclusion->getResolutionLevel(); shouldUpdateBlurs = true; } if (horizonBased) { // Configure for HBAO const auto& radius = ambientOcclusion->getAORadius(); 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 || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.w = ambientOcclusion->getAOObscuranceLevel(); } const float falloffAngle = std::min(1.0f - EPSILON, ambientOcclusion->getAOFalloffAngle()); if (shouldUpdateTechnique || falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; current.x = falloffAngle; current.y = 1.0f / (1.0f - current.x); // Compute sin from cos current.z = sqrtf(1.0f - current.x * current.x); current.w = 1.0f / current.z; } const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } } else { // Configure for SSAO const double RADIUS_POWER = 6.0; const auto& radius = ambientOcclusion->getAORadius(); 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 || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.w = ambientOcclusion->getAOObscuranceLevel(); } if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; current.x = ambientOcclusion->getAOFalloffAngle(); } if (shouldUpdateTechnique || ambientOcclusion->getSSAONumSpiralTurns() != _aoParametersBuffer->getNumSpiralTurns()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.z = ambientOcclusion->getSSAONumSpiralTurns(); } const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; current.y = 1.0f / numSamples; 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 = float(2.0 * M_PI / double(_aoParametersBuffer->getNumSamples())); for (size_t i = 0; i < _randomSamples.size(); i++) { auto 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 (size_t i = 0; i < _randomSamples.size(); i++) { _randomSamples[i] = randFloat() * float(2.0 * M_PI); } }}void AmbientOcclusionEffect::updateBlurParameters() { const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); auto& vblur = _vblurParametersBuffer.edit(); auto& hblur = _hblurParametersBuffer.edit(); auto frameSize = _framebuffer->getSourceFrameSize(); if (_framebuffer->isStereo()) { frameSize.x >>= 1; } const auto occlusionSize = frameSize >> resolutionLevel; if (occlusionSize.x == 0 || occlusionSize.y == 0) { return; } // Occlusion UV limit hblur._blurAxis.z = occlusionSize.x / float(frameSize.x); hblur._blurAxis.w = occlusionSize.y / float(frameSize.y); vblur._blurAxis.z = 1.0f; vblur._blurAxis.w = occlusionSize.y / float(frameSize.y); // Occlusion axis hblur._blurAxis.x = hblur._blurAxis.z / occlusionSize.x;Lines 1–500 of 941. Load more
// // AmbientOcclusionEffect.cpp // libraries/render-utils/src/ // // Created by Niraj Venkat on 7/15/15. // Copyright 2015 High Fidelity, Inc. // Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "AmbientOcclusionEffect.h" #include <algorithm> //min max and more #include <glm/gtc/random.hpp> #include <PathUtils.h> #include <SharedUtil.h> #include <gpu/Context.h> #include <shaders/Shaders.h> #include <render/ShapePipeline.h> #include <MathUtils.h> #include "RenderUtilsLogging.h" #include "render-utils/ShaderConstants.h" #include "DeferredLightingEffect.h" #include "TextureCache.h" #include "FramebufferCache.h" #include "DependencyManager.h" #include "ViewFrustum.h" gpu::PipelinePointer AmbientOcclusionEffect::_occlusionPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_bilateralBlurPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; #define MAX_SSAO_SAMPLES 64.0f #define MAX_HBAO_SAMPLES 6.0f AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } bool AmbientOcclusionFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer, int resolutionLevel, int depthResolutionLevel, bool isStereo) { // If the depth buffer or size changed, we need to delete our FBOs bool reset = false; if (_linearDepthTexture != linearDepthBuffer) { _linearDepthTexture = linearDepthBuffer; reset = true; } if (_resolutionLevel != resolutionLevel || isStereo != _isStereo || _depthResolutionLevel != depthResolutionLevel) { _resolutionLevel = resolutionLevel; _depthResolutionLevel = depthResolutionLevel; _isStereo = isStereo; reset = true; } if (_linearDepthTexture) { auto newFrameSize = glm::ivec2(_linearDepthTexture->getDimensions()); if (_frameSize != newFrameSize) { _frameSize = newFrameSize; reset = true; } } if (reset) { clear(); } return reset; } void AmbientOcclusionFramebuffer::clear() { _occlusionFramebuffer.reset(); _occlusionTexture.reset(); _occlusionBlurredFramebuffer.reset(); _occlusionBlurredTexture.reset(); _normalFramebuffer.reset(); _normalTexture.reset(); } gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() { return _linearDepthTexture; } void AmbientOcclusionFramebuffer::allocate() { #if SSAO_BILATERAL_BLUR_USE_NORMAL auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::HALF, gpu::RGBA }; #else // Format used on OpenGL is typically not supported on Vulkan and framebuffer creation will fail. #ifdef USE_GL auto occlusionformat = gpu::Element{ gpu::VEC3, gpu::NUINT8, gpu::RGB }; #else auto occlusionformat = gpu::Element{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; #endif #endif // Full frame { auto width = _frameSize.x; auto height = _frameSize.y; auto sampler = Sampler(Sampler::FILTER_MIN_MAG_LINEAR, Sampler::WRAP_CLAMP); _occlusionTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); _occlusionBlurredTexture = gpu::Texture::createRenderBuffer(occlusionformat, width, height, gpu::Texture::SINGLE_MIP, sampler); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } // Lower res frame { auto sideSize = _frameSize; if (_isStereo) { sideSize.x >>= 1; } sideSize >>= _resolutionLevel; if (_isStereo) { sideSize.x <<= 1; } auto width = sideSize.x; auto height = sideSize.y; // Format used on OpenGL is typically not supported on Vulkan and framebuffer creation will fail. #ifdef USE_GL auto format = gpu::Element{ gpu::VEC4, gpu::NINT2_10_10_10, gpu::RGBA }; #else auto format = gpu::Element{ gpu::VEC4, gpu::NINT8, gpu::RGBA }; #endif _normalTexture = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_POINT, Sampler::WRAP_CLAMP)); _normalFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ssaoNormals")); _normalFramebuffer->setRenderBuffer(0, _normalTexture); } #if SSAO_USE_QUAD_SPLIT { auto splitSize = _frameSize; if (_isStereo) { splitSize.x >>= 1; } splitSize = divideRoundUp(_frameSize >> _resolutionLevel, SSAO_SPLIT_COUNT); if (_isStereo) { splitSize.x <<= 1; } auto width = splitSize.x; auto height = splitSize.y; _occlusionSplitTexture = gpu::Texture::createRenderBufferArray(occlusionformat, width, height, SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT, gpu::Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_LINEAR, Sampler::WRAP_CLAMP)); for (int i = 0; i < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT; i++) { _occlusionSplitFramebuffers[i] = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionSplitFramebuffers[i]->setRenderBuffer(0, _occlusionSplitTexture, i); } } #endif } #if SSAO_USE_QUAD_SPLIT gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionSplitFramebuffer(int index) { assert(index < SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT); if (!_occlusionSplitFramebuffers[index]) { allocate(); } return _occlusionSplitFramebuffers[index]; } gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionSplitTexture() { if (!_occlusionSplitTexture) { allocate(); } return _occlusionSplitTexture; } #endif gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() { if (!_occlusionFramebuffer) { allocate(); } return _occlusionFramebuffer; } gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionTexture() { if (!_occlusionTexture) { allocate(); } return _occlusionTexture; } gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionBlurredFramebuffer() { if (!_occlusionBlurredFramebuffer) { allocate(); } return _occlusionBlurredFramebuffer; } gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() { if (!_occlusionBlurredTexture) { allocate(); } return _occlusionBlurredTexture; } gpu::FramebufferPointer AmbientOcclusionFramebuffer::getNormalFramebuffer() { if (!_normalFramebuffer) { allocate(); } return _normalFramebuffer; } gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { if (!_normalTexture) { allocate(); } return _normalTexture; } AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion") {} 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); _ditheringInfo = glm::vec4(0.0f); _sampleInfo = glm::vec4(0.0f); _falloffInfo = glm::vec4(0.0f); } AmbientOcclusionEffect::BlurParameters::BlurParameters() { _blurInfo = { 1.0f, 2.0f, 0.0f, 3.0f }; } void AmbientOcclusionEffect::configure(const Config& config) { _debug = config.debug; _debugAmbientOcclusion->setTechnique(config.horizonBased ? AmbientOcclusionTechnique::HBAO : AmbientOcclusionTechnique::SSAO); _debugAmbientOcclusion->setJitter(config.jitterEnabled); _debugAmbientOcclusion->setResolutionLevel(config.resolutionLevel); _debugAmbientOcclusion->setEdgeSharpness(config.edgeSharpness); _debugAmbientOcclusion->setBlurRadius(config.blurRadius); _debugAmbientOcclusion->setAORadius(config.horizonBased ? config.hbaoRadius : config.ssaoRadius); _debugAmbientOcclusion->setAOObscuranceLevel(config.horizonBased ? config.hbaoObscuranceLevel : config.ssaoObscuranceLevel); _debugAmbientOcclusion->setAOFalloffAngle(config.horizonBased ? config.hbaoFalloffAngle : config.ssaoFalloffAngle); _debugAmbientOcclusion->setAOSamplingAmount(config.horizonBased ? (config.hbaoNumSamples / MAX_HBAO_SAMPLES) : (config.ssaoNumSamples / MAX_SSAO_SAMPLES)); _debugAmbientOcclusion->setSSAONumSpiralTurns(config.ssaoNumSpiralTurns); _perspectiveScale = config.perspectiveScale; _ditheringEnabled = config.ditheringEnabled; _borderingEnabled = config.borderingEnabled; _fetchMipsEnabled = config.fetchMipsEnabled; } void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion) { bool shouldUpdateBlurs = false; bool shouldUpdateTechnique = false; if (!_framebuffer) { _framebuffer = std::make_shared<AmbientOcclusionFramebuffer>(); shouldUpdateBlurs = true; } // Update bilateral blur if (ambientOcclusion->getBlurRadius() != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != ambientOcclusion->getEdgeSharpness()) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; auto& hblur = _hblurParametersBuffer.edit()._blurInfo; auto& vblur = _vblurParametersBuffer.edit()._blurInfo; float blurRadialSigma = float(ambientOcclusion->getBlurRadius()) * 0.5f; float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * ambientOcclusion->getEdgeSharpness()); _blurEdgeSharpness = ambientOcclusion->getEdgeSharpness(); hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; hblur.w = (float)ambientOcclusion->getBlurRadius(); vblur.x = blurScales.x; vblur.y = blurScales.y; vblur.z = blurScales.z; vblur.w = (float)ambientOcclusion->getBlurRadius(); } if (_perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { _aoParametersBuffer.edit()._resolutionInfo.z = _perspectiveScale; } if (_ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.x = (float)_ditheringEnabled; } if (_borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { auto& current = _aoParametersBuffer.edit()._ditheringInfo; current.w = (float)_borderingEnabled; } if (_fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.w = (float)_fetchMipsEnabled; } bool horizonBased = ambientOcclusion->getTechnique() == AmbientOcclusionTechnique::HBAO; if (_aoParametersBuffer->isHorizonBased() != horizonBased) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.y = horizonBased & 1; shouldUpdateTechnique = true; } if (ambientOcclusion->getResolutionLevel() != _aoParametersBuffer->getResolutionLevel()) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; current.x = (float)ambientOcclusion->getResolutionLevel(); shouldUpdateBlurs = true; } if (horizonBased) { // Configure for HBAO const auto& radius = ambientOcclusion->getAORadius(); 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 || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.w = ambientOcclusion->getAOObscuranceLevel(); } const float falloffAngle = std::min(1.0f - EPSILON, ambientOcclusion->getAOFalloffAngle()); if (shouldUpdateTechnique || falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; current.x = falloffAngle; current.y = 1.0f / (1.0f - current.x); // Compute sin from cos current.z = sqrtf(1.0f - current.x * current.x); current.w = 1.0f / current.z; } const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } } else { // Configure for SSAO const double RADIUS_POWER = 6.0; const auto& radius = ambientOcclusion->getAORadius(); 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 || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.w = ambientOcclusion->getAOObscuranceLevel(); } if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; current.x = ambientOcclusion->getAOFalloffAngle(); } if (shouldUpdateTechnique || ambientOcclusion->getSSAONumSpiralTurns() != _aoParametersBuffer->getNumSpiralTurns()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.z = ambientOcclusion->getSSAONumSpiralTurns(); } const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES)); if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; current.x = numSamples; current.y = 1.0f / numSamples; 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 = float(2.0 * M_PI / double(_aoParametersBuffer->getNumSamples())); for (size_t i = 0; i < _randomSamples.size(); i++) { auto 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 (size_t i = 0; i < _randomSamples.size(); i++) { _randomSamples[i] = randFloat() * float(2.0 * M_PI); } } } void AmbientOcclusionEffect::updateBlurParameters() { const auto resolutionLevel = _aoParametersBuffer->getResolutionLevel(); auto& vblur = _vblurParametersBuffer.edit(); auto& hblur = _hblurParametersBuffer.edit(); auto frameSize = _framebuffer->getSourceFrameSize(); if (_framebuffer->isStereo()) { frameSize.x >>= 1; } const auto occlusionSize = frameSize >> resolutionLevel; if (occlusionSize.x == 0 || occlusionSize.y == 0) { return; } // Occlusion UV limit hblur._blurAxis.z = occlusionSize.x / float(frameSize.x); hblur._blurAxis.w = occlusionSize.y / float(frameSize.y); vblur._blurAxis.z = 1.0f; vblur._blurAxis.w = occlusionSize.y / float(frameSize.y); // Occlusion axis hblur._blurAxis.x = hblur._blurAxis.z / occlusionSize.x;