From 27a0fb62d31c8f0e68f1fb48309926c386abb03d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 09:35:38 +0200 Subject: [PATCH] Multiple gaussians as Unreal. First draft --- libraries/render-utils/src/BloomEffect.cpp | 145 +++++++++++++++++- libraries/render-utils/src/BloomEffect.h | 76 ++++++++- libraries/render-utils/src/BloomThreshold.slf | 40 +++++ .../render-utils/src/RenderDeferredTask.cpp | 2 +- scripts/developer/utilities/render/bloom.qml | 66 ++++++++ .../developer/utilities/render/debugBloom.js | 20 +++ 6 files changed, 332 insertions(+), 17 deletions(-) create mode 100644 libraries/render-utils/src/BloomThreshold.slf create mode 100644 scripts/developer/utilities/render/bloom.qml create mode 100644 scripts/developer/utilities/render/debugBloom.js diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 24cdd5fca3..f083bf7e6b 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -10,18 +10,145 @@ // #include "BloomEffect.h" +#include "gpu/Context.h" +#include "gpu/StandardShaderLib.h" + #include +#include "BloomThreshold_frag.h" + +ThresholdAndDownsampleJob::ThresholdAndDownsampleJob() { + +} + +void ThresholdAndDownsampleJob::configure(const Config& config) { + _threshold = config.threshold; +} + +void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + RenderArgs* args = renderContext->args; + + const auto frameTransform = inputs.get0(); + const auto inputFrameBuffer = inputs.get1(); + + assert(inputFrameBuffer->hasColor()); + + auto inputColor = inputFrameBuffer->getRenderBuffer(0); + auto sourceViewport = args->_viewport; + auto fullSize = glm::ivec2(inputColor->getDimensions()); + auto halfSize = fullSize / 2; + auto halfViewport = args->_viewport >> 1; + + if (!_downsampledBuffer || _downsampledBuffer->getSize().x != halfSize.x || _downsampledBuffer->getSize().y != halfSize.y) { + auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputColor->getTexelFormat(), halfSize.x, halfSize.y, + gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); + + _downsampledBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomBlur0")); + _downsampledBuffer->setRenderBuffer(0, colorTexture); + } + + static const int COLOR_MAP_SLOT = 0; + static const int THRESHOLD_SLOT = 1; + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + _pipeline = gpu::Pipeline::create(program, state); + } + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + +/* batch.setViewportTransform(halfViewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(inputFrameBuffer->getSize(), args->_viewport)); + batch.setPipeline(_pipeline);*/ + + batch.setFramebuffer(_downsampledBuffer); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, gpu::Vec4(1.0f, 0.2f, 0.9f, 1.f)); + /*batch.setResourceTexture(COLOR_MAP_SLOT, inputColor); + batch._glUniform1f(THRESHOLD_SLOT, _threshold); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + batch.setViewportTransform(args->_viewport); + batch.setResourceTexture(COLOR_MAP_SLOT, nullptr); + batch.setFramebuffer(nullptr);*/ + }); + + outputs = _downsampledBuffer; +} + +DebugBloom::DebugBloom() { +} + +DebugBloom::~DebugBloom() { +} + +void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + const auto frameBuffer = inputs.get0(); + const auto level0FB = inputs.get1(); + const gpu::TexturePointer levelTextures[1] = { + level0FB->getRenderBuffer(0) + }; + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + _pipeline = gpu::Pipeline::create(program, state); + } + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + //batch.setFramebuffer(frameBuffer); + batch.setViewportTransform(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, false); + batch.setPipeline(_pipeline); + batch.setResourceTexture(0, levelTextures[0]); + + batch.draw(gpu::TRIANGLE_STRIP, 4); + + batch.setResourceTexture(0, nullptr); + }); +} + void BloomConfig::setIntensity(float value) { - auto blurConfig = getConfig(); - blurConfig->setProperty("mix", value*0.5f); + /* auto blurConfig = getConfig(); + blurConfig->setProperty("mix", value*0.5f);*/ } void BloomConfig::setSize(float value) { - auto& blurJob = static_cast(_task)->_jobs.front(); + /* auto& blurJob = static_cast(_task)->_jobs.front(); auto& gaussianBlur = blurJob.edit(); auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); + gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f);*/ } Bloom::Bloom() { @@ -29,11 +156,13 @@ Bloom::Bloom() { } void Bloom::configure(const Config& config) { - auto blurConfig = config.getConfig(); - blurConfig->setProperty("filterScale", 3.0f); +/* auto blurConfig = config.getConfig(); + blurConfig->setProperty("filterScale", 3.0f);*/ } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto& blurInput = inputs; - task.addJob("Blur", blurInput); + const auto halfSizeBuffer = task.addJob("BloomThreshold", inputs); + + const auto debugInput = DebugBloom::Inputs(inputs.get().get1(), halfSizeBuffer).asVarying(); + task.addJob("DebugBloom", debugInput); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index c8588c7a15..ec14a6ad9f 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -14,27 +14,87 @@ #include +#include "DeferredFrameTransform.h" + class BloomConfig : public render::Task::Config { - Q_OBJECT - Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty) - Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) + Q_OBJECT + Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty) + Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) public: - float intensity{ 0.2f }; - float size{ 0.4f }; + BloomConfig() : render::Task::Config(true) {} + + float intensity{ 0.2f }; + float size{ 0.4f }; void setIntensity(float value); void setSize(float value); signals: - void dirty(); + void dirty(); +}; + +class ThresholdConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) + +public: + + float threshold{ 0.25f }; + +signals: + void dirty(); +}; + +class ThresholdAndDownsampleJob { +public: + using Inputs = render::VaryingSet2; + using Outputs = gpu::FramebufferPointer; + using Config = ThresholdConfig; + using JobModel = render::Job::ModelIO; + + ThresholdAndDownsampleJob(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + +private: + + gpu::FramebufferPointer _downsampledBuffer; + gpu::PipelinePointer _pipeline; + float _threshold; +}; + +class DebugBloomConfig : public render::Job::Config { + Q_OBJECT + +public: + + DebugBloomConfig() : render::Job::Config(true) {} + +}; + +class DebugBloom { +public: + using Inputs = render::VaryingSet2; + using Config = DebugBloomConfig; + using JobModel = render::Job::ModelI; + + DebugBloom(); + ~DebugBloom(); + + void configure(const Config& config) {} + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +private: + gpu::PipelinePointer _pipeline; }; class Bloom { public: - using Inputs = gpu::FramebufferPointer; - using Config = BloomConfig; + using Inputs = render::VaryingSet2; + using Config = BloomConfig; using JobModel = render::Task::ModelI; Bloom(); diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf new file mode 100644 index 0000000000..7c87ed9fcd --- /dev/null +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -0,0 +1,40 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// BloomThreshold.slf +// Perform a soft threshold on an input texture and downsample to half size in one go. +// +// Created by Olivier Prat on 09/26/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +uniform sampler2D colorMap; +uniform float threshold; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + // Gather 2 by 2 quads from texture, threshold and downsample + vec4 reds = textureGather(colorMap, varTexCoord0, 0); + vec4 greens = textureGather(colorMap, varTexCoord0, 1); + vec4 blues = textureGather(colorMap, varTexCoord0, 2); + + float hardness = 8; + vec4 rMask = clamp((reds-threshold) * hardness, 0, 1); + vec4 gMask = clamp((greens-threshold) * hardness, 0, 1); + vec4 bMask = clamp((blues-threshold) * hardness, 0, 1); + + reds = smoothstep(vec4(0,0,0,0), reds, rMask); + greens = smoothstep(vec4(0,0,0,0), greens, gMask); + blues = smoothstep(vec4(0,0,0,0), blues, bMask); + + vec3 texel0 = vec3(reds.x, greens.x, blues.x); + vec3 texel1 = vec3(reds.y, greens.y, blues.y); + vec3 texel2 = vec3(reds.z, greens.z, blues.z); + vec3 texel3 = vec3(reds.w, greens.w, blues.w); + + outFragColor = vec4((texel0+texel1+texel2+texel3)/4.0, 1.0); +} diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 682d5db435..c3e0fec3b1 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -170,7 +170,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); // Add bloom - const auto bloomInputs = lightingFramebuffer; + const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying(); task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping diff --git a/scripts/developer/utilities/render/bloom.qml b/scripts/developer/utilities/render/bloom.qml new file mode 100644 index 0000000000..7deeffc218 --- /dev/null +++ b/scripts/developer/utilities/render/bloom.qml @@ -0,0 +1,66 @@ +// +// bloom.qml +// developer/utilities/render +// +// Olivier Prat, created on 09/25/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "configSlider" + +Item { + id: root + property var config: Render.getConfig("RenderMainView.Bloom") + property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold") + property var configDebug: Render.getConfig("RenderMainView.DebugBloom") + + Column { + spacing: 8 + + CheckBox { + text: "Enable" + checked: root.config["enabled"] + onCheckedChanged: { + root.config["enabled"] = checked; + } + } + CheckBox { + text: "Debug" + checked: root.configDebug["enabled"] + onCheckedChanged: { + root.configDebug["enabled"] = checked; + } + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.config + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Size" + integral: false + config: root.config + property: "size" + max: 1.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Threshold" + integral: false + config: root.configThreshold + property: "threshold" + max: 1.0 + min: 0.0 + width: 280 + } + } +} diff --git a/scripts/developer/utilities/render/debugBloom.js b/scripts/developer/utilities/render/debugBloom.js new file mode 100644 index 0000000000..2328d524cf --- /dev/null +++ b/scripts/developer/utilities/render/debugBloom.js @@ -0,0 +1,20 @@ +// +// debugBloom.js +// developer/utilities/render +// +// Olivier Prat, created on 09/25/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Set up the qml ui +var qml = Script.resolvePath('bloom.qml'); +var window = new OverlayWindow({ + title: 'Bloom', + source: qml, + width: 285, + height: 170, +}); +window.closed.connect(function() { Script.stop(); }); \ No newline at end of file