941 lines · 36.6 KB · C++
RawDownload
////  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.