diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf new file mode 100644 index 0000000000..953258e8ab --- /dev/null +++ b/libraries/render-utils/src/BloomApply.slf @@ -0,0 +1,27 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// BloomApply.slf +// Mix the three gaussian blur textures. +// +// Created by Olivier Prat on 10/09/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 blurMap0; +uniform sampler2D blurMap1; +uniform sampler2D blurMap2; +uniform float intensity; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + vec4 blur0 = texture(blurMap0, varTexCoord0); + vec4 blur1 = texture(blurMap1, varTexCoord0); + vec4 blur2 = texture(blurMap2, varTexCoord0); + + outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)*intensity, 1.0f); +} diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp new file mode 100644 index 0000000000..9d9367a6d5 --- /dev/null +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -0,0 +1,359 @@ +// +// BloomEffect.cpp +// render-utils/src/ +// +// Created by Olivier Prat on 09/25/17. +// 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 +// +#include "BloomEffect.h" + +#include "gpu/Context.h" +#include "gpu/StandardShaderLib.h" + +#include +#include + +#include "BloomThreshold_frag.h" +#include "BloomApply_frag.h" + +#define BLOOM_BLUR_LEVEL_COUNT 3 + +BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) : + _downsamplingFactor(downsamplingFactor) { + assert(downsamplingFactor > 0); +} + +void BloomThreshold::configure(const Config& config) { + _threshold = config.threshold; +} + +void BloomThreshold::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 inputBuffer = inputFrameBuffer->getRenderBuffer(0); + auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions()); + + // Downsample resolution + bufferSize.x /= _downsamplingFactor; + bufferSize.y /= _downsamplingFactor; + + if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) { + auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y, + gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + + _outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold")); + _outputBuffer->setRenderBuffer(0, colorTexture); + } + + static const int COLOR_MAP_SLOT = 0; + static const int THRESHOLD_SLOT = 1; + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + 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); + } + + glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setViewportTransform(viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); + batch.setPipeline(_pipeline); + + batch.setFramebuffer(_outputBuffer); + batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer); + batch._glUniform1f(THRESHOLD_SLOT, _threshold); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + + outputs = _outputBuffer; +} + +BloomApply::BloomApply() { + +} + +void BloomApply::configure(const Config& config) { + _intensity = config.intensity; +} + +void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + static auto BLUR0_SLOT = 0; + static auto BLUR1_SLOT = 1; + static auto BLUR2_SLOT = 2; + static auto INTENSITY_SLOT = 3; + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT)); + slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT)); + slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT)); + slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false, false)); + _pipeline = gpu::Pipeline::create(program, state); + } + + const auto frameBuffer = inputs.get0(); + const auto framebufferSize = frameBuffer->getSize(); + const auto blur0FB = inputs.get1(); + const auto blur1FB = inputs.get2(); + const auto blur2FB = inputs.get3(); + const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y }; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setFramebuffer(frameBuffer); + + batch.setViewportTransform(viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport)); + batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0)); + batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0)); + batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0)); + batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} + +void BloomDraw::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 bloomFrameBuffer = inputs.get1(); + + if (frameBuffer && bloomFrameBuffer) { + const auto framebufferSize = frameBuffer->getSize(); + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + 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, false)); + state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::ZERO, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _pipeline = gpu::Pipeline::create(program, state); + } + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setFramebuffer(frameBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); + batch.setResourceTexture(0, bloomFrameBuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + } +} + +DebugBloom::DebugBloom() { +} + +void DebugBloom::configure(const Config& config) { + _mode = static_cast(config.mode); + assert(_mode < DebugBloomConfig::MODE_COUNT); +} + +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 combinedBlurBuffer = inputs.get4(); + const auto framebufferSize = frameBuffer->getSize(); + const auto level0FB = inputs.get1(); + const auto level1FB = inputs.get2(); + const auto level2FB = inputs.get3(); + const gpu::TexturePointer levelTextures[BLOOM_BLUR_LEVEL_COUNT] = { + level0FB->getRenderBuffer(0), + level1FB->getRenderBuffer(0), + level2FB->getRenderBuffer(0) + }; + + static auto TEXCOORD_RECT_SLOT = 1; + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS(); + auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("texcoordRect"), TEXCOORD_RECT_SLOT)); + 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); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + Transform modelTransform; + if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) { + batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f); + + modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2); + modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f)); + batch.setModelTransform(modelTransform); + batch.setResourceTexture(0, levelTextures[0]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f)); + batch.setModelTransform(modelTransform); + batch.setResourceTexture(0, levelTextures[1]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + modelTransform.postTranslate(glm::vec3(-2.0f, -2.0f, 0.0f)); + batch.setModelTransform(modelTransform); + batch.setResourceTexture(0, levelTextures[2]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f)); + batch.setModelTransform(modelTransform); + batch.setResourceTexture(0, combinedBlurBuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } else { + auto viewport = args->_viewport; + auto blurLevel = _mode - DebugBloomConfig::MODE_LEVEL0; + + viewport.z /= 2; + + batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f); + + modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport); + modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f)); + batch.setModelTransform(modelTransform); + batch.setResourceTexture(0, levelTextures[blurLevel]); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } + }); +} + +void BloomConfig::setIntensity(float value) { + auto task = static_cast(_task); + auto blurJobIt = task->editJob("BloomApply"); + assert(blurJobIt != task->_jobs.end()); + blurJobIt->getConfiguration()->setProperty("intensity", value); +} + +float BloomConfig::getIntensity() const { + auto task = static_cast(_task); + auto blurJobIt = task->getJob("BloomApply"); + assert(blurJobIt != task->_jobs.end()); + return blurJobIt->getConfiguration()->property("intensity").toFloat(); +} + +void BloomConfig::setSize(float value) { + std::string blurName{ "BloomBlurN" }; + auto sigma = 0.5f+value*3.5f; + + for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { + blurName.back() = '0' + i; + auto task = static_cast(_task); + auto blurJobIt = task->editJob(blurName); + assert(blurJobIt != task->_jobs.end()); + auto& gaussianBlur = blurJobIt->edit(); + auto gaussianBlurParams = gaussianBlur.getParameters(); + gaussianBlurParams->setFilterGaussianTaps(5, sigma); + // Gaussian blur increases at each level to have a slower rolloff on the edge + // of the response + sigma *= 1.5f; + } +} + +Bloom::Bloom() { + +} + +void Bloom::configure(const Config& config) { + std::string blurName{ "BloomBlurN" }; + + for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { + blurName.back() = '0' + i; + auto blurConfig = config.getConfig(blurName); + blurConfig->setProperty("filterScale", 1.0f); + } +} + +void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { + // Start by computing threshold of color buffer input at quarter resolution + const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U); + + // Multi-scale blur, each new blur is half resolution of the previous pass + const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer, true); + const auto blurFB1 = task.addJob("BloomBlur1", blurFB0, true, 2U); + const auto blurFB2 = task.addJob("BloomBlur2", blurFB1, true, 2U); + + const auto& input = inputs.get(); + const auto& frameBuffer = input[1]; + + // Mix all blur levels at quarter resolution + const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + task.addJob("BloomApply", applyInput); + // And them blend result in additive manner on top of final color buffer + const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying(); + task.addJob("BloomDraw", drawInput); + + const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying(); + task.addJob("DebugBloom", debugInput); +} diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h new file mode 100644 index 0000000000..5352c65e4d --- /dev/null +++ b/libraries/render-utils/src/BloomEffect.h @@ -0,0 +1,166 @@ +// +// BloomEffect.h +// render-utils/src/ +// +// Created by Olivier Prat on 09/25/17. +// 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 +// + +#ifndef hifi_render_utils_BloomEffect_h +#define hifi_render_utils_BloomEffect_h + +#include + +#include "DeferredFrameTransform.h" + +class BloomConfig : public render::Task::Config { + Q_OBJECT + Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) + Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) + +public: + + BloomConfig() : render::Task::Config(false) {} + + float size{ 0.8f }; + + void setIntensity(float value); + float getIntensity() const; + void setSize(float value); + +signals: + void dirty(); +}; + +class BloomThresholdConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) + +public: + + float threshold{ 1.25f }; + +signals: + void dirty(); +}; + +class BloomThreshold { +public: + using Inputs = render::VaryingSet2; + using Outputs = gpu::FramebufferPointer; + using Config = BloomThresholdConfig; + using JobModel = render::Job::ModelIO; + + BloomThreshold(unsigned int downsamplingFactor); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + +private: + + gpu::FramebufferPointer _outputBuffer; + gpu::PipelinePointer _pipeline; + float _threshold; + unsigned int _downsamplingFactor; +}; + + +class BloomApplyConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) + +public: + + float intensity{ 0.8f }; + +signals: + void dirty(); +}; + +class BloomApply { +public: + using Inputs = render::VaryingSet4; + using Config = BloomApplyConfig; + using JobModel = render::Job::ModelI; + + BloomApply(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +private: + + gpu::PipelinePointer _pipeline; + float _intensity{ 1.0f }; +}; + +class BloomDraw { +public: + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; + + BloomDraw() {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +private: + + gpu::PipelinePointer _pipeline; +}; + +class DebugBloomConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) + +public: + + enum Mode { + MODE_LEVEL0 = 0, + MODE_LEVEL1, + MODE_LEVEL2, + MODE_ALL_LEVELS, + + MODE_COUNT + }; + + DebugBloomConfig() : render::Job::Config(false) {} + + int mode{ MODE_ALL_LEVELS }; + +signals: + void dirty(); +}; + +class DebugBloom { +public: + using Inputs = render::VaryingSet5; + using Config = DebugBloomConfig; + using JobModel = render::Job::ModelI; + + DebugBloom(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +private: + gpu::PipelinePointer _pipeline; + DebugBloomConfig::Mode _mode; +}; + +class Bloom { +public: + using Inputs = render::VaryingSet2; + using Config = BloomConfig; + using JobModel = render::Task::ModelI; + + Bloom(); + + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + +}; + +#endif // hifi_render_utils_BloomEffect_h diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf new file mode 100644 index 0000000000..e4b96618df --- /dev/null +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -0,0 +1,45 @@ +<@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; + +#define DOWNSAMPLING_FACTOR 4 +#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2) + +void main(void) { + vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT; + vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT; + vec2 startUv = varTexCoord0; + vec4 maskedColor = vec4(0,0,0,0); + + for (int y=0 ; ysetDepthStencilBuffer(_primaryDepthTexture, depthFormat); - auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); - _lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler); + _lightingTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, smoothSampler); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 4261b14a9b..a395136978 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -42,6 +42,7 @@ #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" #include "DrawHaze.h" +#include "BloomEffect.h" #include "HighlightEffect.h" #include @@ -166,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying(); task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); - // LIght Cluster Grid Debuging job + // Light Cluster Grid Debuging job { const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).asVarying(); task.addJob("DebugLightClusters", debugLightClustersInputs); @@ -177,6 +178,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); + // Add bloom + const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying(); + task.addJob("Bloom", bloomInputs); + // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 73a8e0a0dd..2be6f8fad2 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -29,11 +29,10 @@ enum BlurShaderMapSlots { BlurTask_DepthSlot, }; -const float BLUR_NUM_SAMPLES = 7.0f; - BlurParams::BlurParams() { Params params; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Params), (const gpu::Byte*) ¶ms)); + setFilterGaussianTaps(3); } void BlurParams::setWidthHeight(int width, int height, bool isStereo) { @@ -49,10 +48,10 @@ void BlurParams::setWidthHeight(int width, int height, bool isStereo) { } } -void BlurParams::setTexcoordTransform(const glm::vec4 texcoordTransformViewport) { - auto texcoordTransform = _parametersBuffer.get().texcoordTransform; - if (texcoordTransformViewport != texcoordTransform) { - _parametersBuffer.edit().texcoordTransform = texcoordTransform; +void BlurParams::setTexcoordTransform(glm::vec4 texcoordTransformViewport) { + auto& params = _parametersBuffer.get(); + if (texcoordTransformViewport != params.texcoordTransform) { + _parametersBuffer.edit().texcoordTransform = texcoordTransformViewport; } } @@ -60,7 +59,58 @@ void BlurParams::setFilterRadiusScale(float scale) { auto filterInfo = _parametersBuffer.get().filterInfo; if (scale != filterInfo.x) { _parametersBuffer.edit().filterInfo.x = scale; - _parametersBuffer.edit().filterInfo.y = scale / BLUR_NUM_SAMPLES; + } +} + +void BlurParams::setFilterNumTaps(int count) { + assert(count <= BLUR_MAX_NUM_TAPS); + auto filterInfo = _parametersBuffer.get().filterInfo; + if (count != (int)filterInfo.y) { + _parametersBuffer.edit().filterInfo.y = count; + } +} + +void BlurParams::setFilterTap(int index, float offset, float value) { + auto filterTaps = _parametersBuffer.edit().filterTaps; + assert(index < BLUR_MAX_NUM_TAPS); + filterTaps[index].x = offset; + filterTaps[index].y = value; +} + +void BlurParams::setFilterGaussianTaps(int numHalfTaps, float sigma) { + auto& params = _parametersBuffer.edit(); + const int numTaps = 2 * numHalfTaps + 1; + assert(numTaps <= BLUR_MAX_NUM_TAPS); + assert(sigma > 0.0f); + const float inverseTwoSigmaSquared = float(0.5 / double(sigma*sigma)); + float totalWeight = 1.0f; + float weight; + float offset; + int i; + + params.filterInfo.y = numTaps; + params.filterTaps[0].x = 0.0f; + params.filterTaps[0].y = 1.0f; + + for (i = 0; i < numHalfTaps; i++) { + offset = i + 1; + weight = (float)exp(-offset*offset * inverseTwoSigmaSquared); + params.filterTaps[i + 1].x = offset; + params.filterTaps[i + 1].y = weight; + params.filterTaps[i + 1 + numHalfTaps].x = -offset; + params.filterTaps[i + 1 + numHalfTaps].y = weight; + totalWeight += 2 * weight; + } + + // Tap weights will be normalized in shader because side cases on edges of screen + // won't have the same number of taps as in the center. +} + +void BlurParams::setOutputAlpha(float value) { + value = glm::clamp(value, 0.0f, 1.0f); + auto filterInfo = _parametersBuffer.get().filterInfo; + if (value != filterInfo.z) { + _parametersBuffer.edit().filterInfo.z = value; } } @@ -86,17 +136,23 @@ void BlurParams::setLinearDepthPosFar(float farPosDepth) { } -BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer) : -_generateOutputFramebuffer(generateOutputFramebuffer) -{ - +BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor) : + _downsampleFactor(downsampleFactor), + _generateOutputFramebuffer(generateOutputFramebuffer) { + assert(downsampleFactor > 0); } bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFramebuffer, Resources& blurringResources) { if (!sourceFramebuffer) { return false; } - if (_blurredFramebuffer && _blurredFramebuffer->getSize() != sourceFramebuffer->getSize()) { + + auto blurBufferSize = sourceFramebuffer->getSize(); + + blurBufferSize.x /= _downsampleFactor; + blurBufferSize.y /= _downsampleFactor; + + if (_blurredFramebuffer && _blurredFramebuffer->getSize() != blurBufferSize) { _blurredFramebuffer.reset(); } @@ -108,7 +164,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra // _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); //} auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); - auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler); + auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler); _blurredFramebuffer->setRenderBuffer(0, blurringTarget); } @@ -117,7 +173,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0); if (_generateOutputFramebuffer) { - if (_outputFramebuffer && _outputFramebuffer->getSize() != sourceFramebuffer->getSize()) { + if (_outputFramebuffer && _outputFramebuffer->getSize() != blurBufferSize) { _outputFramebuffer.reset(); } @@ -131,7 +187,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra _outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); }*/ auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); - auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler); + auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler); _outputFramebuffer->setRenderBuffer(0, blurringTarget); } @@ -145,8 +201,8 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra return true; } -BlurGaussian::BlurGaussian(bool generateOutputFramebuffer) : - _inOutResources(generateOutputFramebuffer) +BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsampleFactor) : + _inOutResources(generateOutputFramebuffer, downsampleFactor) { _parameters = std::make_shared(); } @@ -196,7 +252,16 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() { } void BlurGaussian::configure(const Config& config) { + auto state = getBlurHPipeline()->getState(); + _parameters->setFilterRadiusScale(config.filterScale); + _parameters->setOutputAlpha(config.mix); + if (config.mix < 1.0f) { + state->setBlendFunction(config.mix < 1.0f, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + } else { + state->setBlendFunction(false); + } } @@ -206,7 +271,6 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra RenderArgs* args = renderContext->args; - BlurInOutResource::Resources blurringResources; if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) { // early exit if no valid blurring resources @@ -216,14 +280,15 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra auto blurVPipeline = getBlurVPipeline(); auto blurHPipeline = getBlurHPipeline(); + glm::ivec4 viewport { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() }; - _parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->isStereo()); - glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); - _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport)); + glm::ivec2 textureSize = blurredFramebuffer->getSize(); + _parameters->setWidthHeight(blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight(), args->isStereo()); + _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, viewport)); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); + batch.setViewportTransform(viewport); batch.setUniformBuffer(BlurTask_ParamsSlot, _parameters->_parametersBuffer); @@ -251,7 +316,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, const BlurParamsPointer& params) : - _inOutResources(generateOutputFramebuffer), + _inOutResources(generateOutputFramebuffer, 1U), _parameters((params ? params : std::make_shared())) { } diff --git a/libraries/render/src/render/BlurTask.h b/libraries/render/src/render/BlurTask.h index f023aabfe7..e8d268dc63 100644 --- a/libraries/render/src/render/BlurTask.h +++ b/libraries/render/src/render/BlurTask.h @@ -14,6 +14,8 @@ #include "Engine.h" +#include "BlurTask_shared.slh" + namespace render { @@ -25,6 +27,11 @@ public: void setTexcoordTransform(const glm::vec4 texcoordTransformViewport); void setFilterRadiusScale(float scale); + void setFilterNumTaps(int count); + // Tap 0 is considered the center of the kernel + void setFilterTap(int index, float offset, float value); + void setFilterGaussianTaps(int numHalfTaps, float sigma = 1.47f); + void setOutputAlpha(float value); void setDepthPerspective(float oneOverTan2FOV); void setDepthThreshold(float threshold); @@ -40,7 +47,7 @@ public: // Viewport to Texcoord info, if the region of the blur (viewport) is smaller than the full frame glm::vec4 texcoordTransform{ 0.0f, 0.0f, 1.0f, 1.0f }; - // Filter info (radius scale + // Filter info (radius scale, number of taps, output alpha) glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f }; // Depth info (radius scale @@ -52,6 +59,9 @@ public: // LinearDepth info is { f } glm::vec4 linearDepthInfo{ 0.0f }; + // Taps (offset, weight) + glm::vec2 filterTaps[BLUR_MAX_NUM_TAPS]; + Params() {} }; gpu::BufferView _parametersBuffer; @@ -62,7 +72,7 @@ using BlurParamsPointer = std::shared_ptr; class BlurInOutResource { public: - BlurInOutResource(bool generateOutputFramebuffer = false); + BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor); struct Resources { gpu::TexturePointer sourceTexture; @@ -75,8 +85,9 @@ public: gpu::FramebufferPointer _blurredFramebuffer; - // the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in th einput buffer + // the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in the input buffer gpu::FramebufferPointer _outputFramebuffer; + unsigned int _downsampleFactor{ 1U }; bool _generateOutputFramebuffer{ false }; }; @@ -84,12 +95,15 @@ public: class BlurGaussianConfig : public Job::Config { Q_OBJECT Q_PROPERTY(bool enabled WRITE setEnabled READ isEnabled NOTIFY dirty) // expose enabled flag - Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty) // expose enabled flag + Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty) + Q_PROPERTY(float mix MEMBER mix NOTIFY dirty) public: BlurGaussianConfig() : Job::Config(true) {} float filterScale{ 0.2f }; + float mix{ 1.0f }; + signals : void dirty(); @@ -102,11 +116,13 @@ public: using Config = BlurGaussianConfig; using JobModel = Job::ModelIO; - BlurGaussian(bool generateOutputFramebuffer = false); + BlurGaussian(bool generateOutputFramebuffer = false, unsigned int downsampleFactor = 1U); void configure(const Config& config); void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer); + BlurParamsPointer getParameters() const { return _parameters; } + protected: BlurParamsPointer _parameters; diff --git a/libraries/render/src/render/BlurTask.slh b/libraries/render/src/render/BlurTask.slh index de2614eb51..37f29496bd 100644 --- a/libraries/render/src/render/BlurTask.slh +++ b/libraries/render/src/render/BlurTask.slh @@ -9,17 +9,7 @@ <@func declareBlurUniforms()@> -#define NUM_TAPS 7 -#define NUM_TAPS_OFFSET 3.0f - -float uniformFilterWidth = 0.05f; - -const float gaussianDistributionCurve[NUM_TAPS] = float[]( - 0.383f, 0.006f, 0.061f, 0.242f, 0.242f, 0.061f, 0.006f -); -const float gaussianDistributionOffset[NUM_TAPS] = float[]( - 0.0f, -3.0f, -2.0f, -1.0f, 1.0f, 2.0f, 3.0f -); +<@include BlurTask_shared.slh@> struct BlurParameters { vec4 resolutionInfo; @@ -28,6 +18,7 @@ struct BlurParameters { vec4 depthInfo; vec4 stereoInfo; vec4 linearDepthInfo; + vec2 taps[BLUR_MAX_NUM_TAPS]; }; uniform blurParamsBuffer { @@ -46,6 +37,25 @@ float getFilterScale() { return parameters.filterInfo.x; } +int getFilterNumTaps() { + return int(parameters.filterInfo.y); +} + +float getOutputAlpha() { + return parameters.filterInfo.z; +} + +vec2 getFilterTap(int index) { + return parameters.taps[index]; +} + +float getFilterTapOffset(vec2 tap) { + return tap.x; +} + +float getFilterTapWeight(vec2 tap) { + return tap.y; +} float getDepthThreshold() { return parameters.depthInfo.x; @@ -70,19 +80,29 @@ uniform sampler2D sourceMap; vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { texcoord = evalTexcoordTransformed(texcoord); - vec4 sampleCenter = texture(sourceMap, texcoord); vec2 finalStep = getFilterScale() * direction * pixelStep; vec4 srcBlurred = vec4(0.0); + float totalWeight = 0.f; + int numTaps = getFilterNumTaps(); - for(int i = 0; i < NUM_TAPS; i++) { - // Fetch color and depth for current sample. - vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep); - vec4 srcSample = texture(sourceMap, sampleCoord); - // Accumulate. - srcBlurred += gaussianDistributionCurve[i] * srcSample; + for(int i = 0; i < numTaps; i++) { + vec2 tapInfo = getFilterTap(i); + // Fetch color for current sample. + vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); + if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) { + vec4 srcSample = texture(sourceMap, sampleCoord); + float weight = getFilterTapWeight(tapInfo); + // Accumulate. + srcBlurred += srcSample * weight; + totalWeight += weight; + } } + if (totalWeight>0.0) { + srcBlurred /= totalWeight; + } + srcBlurred.a = getOutputAlpha(); return srcBlurred; } @@ -95,15 +115,6 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { uniform sampler2D sourceMap; uniform sampler2D depthMap; -#define NUM_HALF_TAPS 4 - -const float gaussianDistributionCurveHalf[NUM_HALF_TAPS] = float[]( - 0.383f, 0.242f, 0.061f, 0.006f -); -const float gaussianDistributionOffsetHalf[NUM_HALF_TAPS] = float[]( - 0.0f, 1.0f, 2.0f, 3.0f -); - vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep) { texcoord = evalTexcoordTransformed(texcoord); float sampleDepth = texture(depthMap, texcoord).x; @@ -122,45 +133,36 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep float scale = distanceToProjectionWindow / sampleDepth; vec2 finalStep = filterScale * scale * direction * pixelStep; + int numTaps = getFilterNumTaps(); // Accumulate the center sample - vec4 srcBlurred = gaussianDistributionCurve[0] * sampleCenter; + vec2 tapInfo = getFilterTap(0); + float totalWeight = getFilterTapWeight(tapInfo); + vec4 srcBlurred = sampleCenter * totalWeight; + + for(int i = 1; i < numTaps; i++) { + tapInfo = getFilterTap(i); - for(int i = 1; i < NUM_TAPS; i++) { // Fetch color and depth for current sample. - vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep); - float srcDepth = texture(depthMap, sampleCoord).x; - vec4 srcSample = texture(sourceMap, sampleCoord); + vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); + if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) { + float srcDepth = texture(depthMap, sampleCoord).x; + vec4 srcSample = texture(sourceMap, sampleCoord); + float weight = getFilterTapWeight(tapInfo); - // If the difference in depth is huge, we lerp color back. - float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0); - srcSample = mix(srcSample, sampleCenter, s); + // If the difference in depth is huge, we lerp color back. + float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0); + srcSample = mix(srcSample, sampleCenter, s); - // Accumulate. - srcBlurred += gaussianDistributionCurve[i] * srcSample; + // Accumulate. + srcBlurred += srcSample * weight; + totalWeight += weight; + } } - - /* - for(int i = 1; i < NUM_HALF_TAPS; i++) { - // Fetch color and depth for current sample. - vec2 texcoordOffset = (gaussianDistributionOffsetHalf[i] * finalStep); - - float srcDepthN = texture(depthMap, texcoord - texcoordOffset).x; - float srcDepthP = texture(depthMap, texcoord + texcoordOffset).x; - vec4 srcSampleN = texture(sourceMap, texcoord - texcoordOffset); - vec4 srcSampleP = texture(sourceMap, texcoord + texcoordOffset); - - // If the difference in depth is huge, we lerp color back. - float sN = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthN - sampleDepth), 0.0, 1.0); - float sP = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthP - sampleDepth), 0.0, 1.0); - - srcSampleN = mix(srcSampleN, sampleCenter, sN); - srcSampleP = mix(srcSampleP, sampleCenter, sP); - - // Accumulate. - srcBlurred += gaussianDistributionCurveHalf[i] * (srcSampleP + srcSampleN); - }*/ + if (totalWeight>0.0) { + srcBlurred /= totalWeight; + } return srcBlurred; } diff --git a/libraries/render/src/render/BlurTask_shared.slh b/libraries/render/src/render/BlurTask_shared.slh new file mode 100644 index 0000000000..beca32c1be --- /dev/null +++ b/libraries/render/src/render/BlurTask_shared.slh @@ -0,0 +1,10 @@ +// Generated on <$_SCRIBE_DATE$> +// +// Created by Olivier Prat on 09/25/17. +// 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 +// + +#define BLUR_MAX_NUM_TAPS 33 diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp new file mode 100644 index 0000000000..65c0ff45b9 --- /dev/null +++ b/libraries/render/src/render/ResampleTask.cpp @@ -0,0 +1,83 @@ +// +// ResampleTask.cpp +// render/src/render +// +// Various to upsample or downsample textures into framebuffers. +// +// Created by Olivier Prat on 10/09/17. +// 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 +// +#include "ResampleTask.h" + +#include "gpu/Context.h" +#include "gpu/StandardShaderLib.h" + +using namespace render; + +gpu::PipelinePointer HalfDownsample::_pipeline; + +HalfDownsample::HalfDownsample() { + +} + +void HalfDownsample::configure(const Config& config) { + +} + +gpu::FramebufferPointer HalfDownsample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) { + auto resampledFramebufferSize = sourceFramebuffer->getSize(); + + resampledFramebufferSize.x /= 2U; + resampledFramebufferSize.y /= 2U; + + if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) { + _destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("HalfOutput")); + + auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); + auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler); + _destinationFrameBuffer->setRenderBuffer(0, target); + } + return _destinationFrameBuffer; +} + +void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer); + + if (!_pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + 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, false)); + _pipeline = gpu::Pipeline::create(program, state); + } + + const auto bufferSize = resampledFrameBuffer->getSize(); + glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + + batch.setFramebuffer(resampledFrameBuffer); + + batch.setViewportTransform(viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setPipeline(_pipeline); + + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); + batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h new file mode 100644 index 0000000000..da2b7b3537 --- /dev/null +++ b/libraries/render/src/render/ResampleTask.h @@ -0,0 +1,41 @@ +// +// ResampleTask.h +// render/src/render +// +// Various to upsample or downsample textures into framebuffers. +// +// Created by Olivier Prat on 10/09/17. +// 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 +// + +#ifndef hifi_render_ResampleTask_h +#define hifi_render_ResampleTask_h + +#include "Engine.h" + +namespace render { + + class HalfDownsample { + public: + using Config = JobConfig; + using JobModel = Job::ModelIO; + + HalfDownsample(); + + void configure(const Config& config); + void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer); + + protected: + + static gpu::PipelinePointer _pipeline; + + gpu::FramebufferPointer _destinationFrameBuffer; + + gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); + }; +} + +#endif // hifi_render_ResampleTask_h diff --git a/libraries/render/src/task/Task.h b/libraries/render/src/task/Task.h index a8137fd239..63bda7bafa 100644 --- a/libraries/render/src/task/Task.h +++ b/libraries/render/src/task/Task.h @@ -171,6 +171,8 @@ public: _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); } + const std::string& getName() const { return _name; } + protected: ConceptPointer _concept; std::string _name = ""; @@ -206,6 +208,24 @@ public: const Varying getInput() const override { return _input; } const Varying getOutput() const override { return _output; } + typename Jobs::iterator editJob(std::string name) { + typename Jobs::iterator jobIt; + for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) { + if (jobIt->getName() == name) { + return jobIt; + } + } + return jobIt; + } + typename Jobs::const_iterator getJob(std::string name) const { + typename Jobs::const_iterator jobIt; + for (jobIt = _jobs.begin(); jobIt != _jobs.end(); ++jobIt) { + if (jobIt->getName() == name) { + return jobIt; + } + } + return jobIt; + } TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} diff --git a/scripts/developer/utilities/render/bloom.qml b/scripts/developer/utilities/render/bloom.qml new file mode 100644 index 0000000000..66e92e0eff --- /dev/null +++ b/scripts/developer/utilities/render/bloom.qml @@ -0,0 +1,119 @@ +// +// 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; + } + } + GroupBox { + title: "Debug" + Row { + ExclusiveGroup { id: debugGroup } + RadioButton { + text : "Off" + checked : !root.configDebug["enabled"] + onCheckedChanged: { + if (checked) { + root.configDebug["enabled"] = false + } + } + exclusiveGroup : debugGroup + } + RadioButton { + text : "Lvl 0" + checked :root.configDebug["enabled"] && root.configDebug["mode"]==0 + onCheckedChanged: { + if (checked) { + root.configDebug["enabled"] = true + root.configDebug["mode"] = 0 + } + } + exclusiveGroup : debugGroup + } + RadioButton { + text : "Lvl 1" + checked : root.configDebug["enabled"] && root.configDebug["mode"]==1 + onCheckedChanged: { + if (checked) { + root.configDebug["enabled"] = true + root.configDebug["mode"] = 1 + } + } + exclusiveGroup : debugGroup + } + RadioButton { + text : "Lvl 2" + checked : root.configDebug["enabled"] && root.configDebug["mode"]==2 + onCheckedChanged: { + if (checked) { + root.configDebug["enabled"] = true + root.configDebug["mode"] = 2 + } + } + exclusiveGroup : debugGroup + } + RadioButton { + text : "All" + checked : root.configDebug["enabled"] && root.configDebug["mode"]==3 + onCheckedChanged: { + if (checked) { + root.configDebug["enabled"] = true + root.configDebug["mode"] = 3 + } + } + exclusiveGroup : debugGroup + } + } + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.config + property: "intensity" + max: 5.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: 2.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