From bcec3680b6ac0676262d755f588bfd0c6f0455b8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 25 Sep 2017 11:51:23 +0200 Subject: [PATCH 01/16] First draft --- libraries/render-utils/src/BloomEffect.cpp | 36 ++++++++++++++ libraries/render-utils/src/BloomEffect.h | 47 +++++++++++++++++++ .../render-utils/src/RenderDeferredTask.cpp | 5 ++ 3 files changed, 88 insertions(+) create mode 100644 libraries/render-utils/src/BloomEffect.cpp create mode 100644 libraries/render-utils/src/BloomEffect.h diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp new file mode 100644 index 0000000000..a0a36a2194 --- /dev/null +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -0,0 +1,36 @@ +// +// 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 + +void BloomConfig::setMix(float value) { + +} + +void BloomConfig::setSize(float value) { + auto blurConfig = getConfig("Blur"); + assert(blurConfig); + blurConfig->setProperty("filterScale", value*10.0f); +} + +Bloom::Bloom() { + +} + +void Bloom::configure(const Config& config) { + +} + +void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { + const auto& blurInput = inputs; + task.addJob("Blur", blurInput); +} diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h new file mode 100644 index 0000000000..278236d2e9 --- /dev/null +++ b/libraries/render-utils/src/BloomEffect.h @@ -0,0 +1,47 @@ +// +// 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 + +class BloomConfig : public render::Task::Config { + Q_OBJECT + Q_PROPERTY(float mix MEMBER mix WRITE setMix NOTIFY dirty) + Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) + +public: + + float mix{ 0.2f }; + float size{ 0.1f }; + + void setMix(float value); + void setSize(float value); + +signals: + void dirty(); +}; + +class Bloom { +public: + using Inputs = gpu::FramebufferPointer; + 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/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c67a1c7875..682d5db435 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -41,6 +41,7 @@ #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" #include "OutlineEffect.h" +#include "BloomEffect.h" #include @@ -168,6 +169,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto toneAndPostRangeTimer = task.addJob("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing"); + // Add bloom + const auto bloomInputs = lightingFramebuffer; + task.addJob("Bloom", bloomInputs); + // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying(); task.addJob("ToneMapping", toneMappingInputs); From 443d6dfacb1716d45ae8a03bd1a8e7987cb209fa Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 25 Sep 2017 14:24:40 +0200 Subject: [PATCH 02/16] Added user definable taps in blur task --- libraries/render/src/render/BlurTask.cpp | 56 ++++++++++++- libraries/render/src/render/BlurTask.h | 11 ++- libraries/render/src/render/BlurTask.slh | 83 ++++++++----------- .../render/src/render/BlurTask_shared.slh | 10 +++ 4 files changed, 107 insertions(+), 53 deletions(-) create mode 100644 libraries/render/src/render/BlurTask_shared.slh diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 73a8e0a0dd..2d51fd1271 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)); + setGaussianFilterTaps(3); } void BlurParams::setWidthHeight(int width, int height, bool isStereo) { @@ -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::setGaussianFilterTaps(int numHalfTaps, float sigma, bool normalize) { + 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 / (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; + } + + float normalizer; + if (normalize) { + normalizer = float(1.0 / totalWeight); + } else { + normalizer = float(1.0 / (sqrt(2 * M_PI)*sigma)); + } + + for (i = 0; i < numTaps; i++) { + params.filterTaps[i].y *= normalizer; } } diff --git a/libraries/render/src/render/BlurTask.h b/libraries/render/src/render/BlurTask.h index f023aabfe7..011da6ab68 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,10 @@ 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 setGaussianFilterTaps(int numHalfTaps, float sigma = 1.47f, bool normalize = true); void setDepthPerspective(float oneOverTan2FOV); void setDepthThreshold(float threshold); @@ -40,7 +46,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, mix) glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f }; // Depth info (radius scale @@ -52,6 +58,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; diff --git a/libraries/render/src/render/BlurTask.slh b/libraries/render/src/render/BlurTask.slh index de2614eb51..b871b59bed 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 getFilterMix() { + 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,17 +80,18 @@ 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); + int numTaps = getFilterNumTaps(); - for(int i = 0; i < NUM_TAPS; i++) { + for(int i = 0; i < numTaps; i++) { + vec2 tapInfo = getFilterTap(i); // Fetch color and depth for current sample. - vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep); + vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); vec4 srcSample = texture(sourceMap, sampleCoord); // Accumulate. - srcBlurred += gaussianDistributionCurve[i] * srcSample; + srcBlurred += getFilterTapWeight(tapInfo) * srcSample; } return srcBlurred; @@ -95,15 +106,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,13 +124,17 @@ 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); + vec4 srcBlurred = getFilterTapWeight(tapInfo) * sampleCenter; + + 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); + vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); float srcDepth = texture(depthMap, sampleCoord).x; vec4 srcSample = texture(sourceMap, sampleCoord); @@ -137,29 +143,8 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep srcSample = mix(srcSample, sampleCenter, s); // Accumulate. - srcBlurred += gaussianDistributionCurve[i] * srcSample; + srcBlurred += getFilterTapWeight(tapInfo) * srcSample; } - - /* - 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); - }*/ 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..94417fa939 --- /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 21 From 22b1507597e1aafea88ece8beb5d2d18c6e6f1eb Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 25 Sep 2017 16:41:20 +0200 Subject: [PATCH 03/16] Fixed blur to work correctly on four sides of screen --- .../src/RenderableModelEntityItem.cpp | 4 +- libraries/render-utils/src/BloomEffect.cpp | 10 +++-- libraries/render/src/render/BlurTask.cpp | 16 ++------ libraries/render/src/render/BlurTask.h | 4 +- libraries/render/src/render/BlurTask.slh | 40 +++++++++++++------ .../render/src/render/BlurTask_shared.slh | 2 +- 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 799a84aaee..b88ef5f8fb 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -319,7 +319,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // should never fall in here when collision model not fully loaded // hence we assert that all geometries exist and are loaded - assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); + assert(model && model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded()); const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry(); ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection(); @@ -408,7 +408,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { // should never fall in here when model not fully loaded - assert(_model && _model->isLoaded()); + assert(model && model->isLoaded()); updateModelBounds(); model->updateGeometry(); diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index a0a36a2194..eb02c4fa57 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -17,9 +17,10 @@ void BloomConfig::setMix(float value) { } void BloomConfig::setSize(float value) { - auto blurConfig = getConfig("Blur"); - assert(blurConfig); - blurConfig->setProperty("filterScale", value*10.0f); + auto& blurJob = static_cast(_task)->_jobs.front(); + auto& gaussianBlur = blurJob.edit(); + auto gaussianBlurParams = gaussianBlur.getParameters(); + gaussianBlurParams->setGaussianFilterTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); } Bloom::Bloom() { @@ -27,7 +28,8 @@ Bloom::Bloom() { } void Bloom::configure(const Config& config) { - + auto blurConfig = config.getConfig(); + blurConfig->setProperty("filterScale", 2.5f); } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 2d51fd1271..2e3cf8138f 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -77,7 +77,7 @@ void BlurParams::setFilterTap(int index, float offset, float value) { filterTaps[index].y = value; } -void BlurParams::setGaussianFilterTaps(int numHalfTaps, float sigma, bool normalize) { +void BlurParams::setGaussianFilterTaps(int numHalfTaps, float sigma) { auto& params = _parametersBuffer.edit(); const int numTaps = 2 * numHalfTaps + 1; assert(numTaps <= BLUR_MAX_NUM_TAPS); @@ -102,16 +102,8 @@ void BlurParams::setGaussianFilterTaps(int numHalfTaps, float sigma, bool normal totalWeight += 2 * weight; } - float normalizer; - if (normalize) { - normalizer = float(1.0 / totalWeight); - } else { - normalizer = float(1.0 / (sqrt(2 * M_PI)*sigma)); - } - - for (i = 0; i < numTaps; i++) { - params.filterTaps[i].y *= normalizer; - } + // 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::setDepthPerspective(float oneOverTan2FOV) { @@ -158,7 +150,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(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler); _blurredFramebuffer->setRenderBuffer(0, blurringTarget); } diff --git a/libraries/render/src/render/BlurTask.h b/libraries/render/src/render/BlurTask.h index 011da6ab68..4df5a09576 100644 --- a/libraries/render/src/render/BlurTask.h +++ b/libraries/render/src/render/BlurTask.h @@ -30,7 +30,7 @@ public: void setFilterNumTaps(int count); // Tap 0 is considered the center of the kernel void setFilterTap(int index, float offset, float value); - void setGaussianFilterTaps(int numHalfTaps, float sigma = 1.47f, bool normalize = true); + void setGaussianFilterTaps(int numHalfTaps, float sigma = 1.47f); void setDepthPerspective(float oneOverTan2FOV); void setDepthThreshold(float threshold); @@ -116,6 +116,8 @@ public: 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 b871b59bed..5a41a0b1c6 100644 --- a/libraries/render/src/render/BlurTask.slh +++ b/libraries/render/src/render/BlurTask.slh @@ -83,17 +83,25 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { vec2 finalStep = getFilterScale() * direction * pixelStep; vec4 srcBlurred = vec4(0.0); + float totalWeight = 0.f; int numTaps = getFilterNumTaps(); for(int i = 0; i < numTaps; i++) { vec2 tapInfo = getFilterTap(i); - // Fetch color and depth for current sample. + // Fetch color for current sample. vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); - vec4 srcSample = texture(sourceMap, sampleCoord); - // Accumulate. - srcBlurred += getFilterTapWeight(tapInfo) * srcSample; + 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; + } return srcBlurred; } @@ -128,24 +136,32 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep // Accumulate the center sample vec2 tapInfo = getFilterTap(0); - vec4 srcBlurred = getFilterTapWeight(tapInfo) * sampleCenter; + float totalWeight = getFilterTapWeight(tapInfo); + vec4 srcBlurred = sampleCenter * totalWeight; for(int i = 1; i < numTaps; i++) { tapInfo = getFilterTap(i); // Fetch color and depth for current sample. vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep); - float srcDepth = texture(depthMap, sampleCoord).x; - vec4 srcSample = texture(sourceMap, sampleCoord); + 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 += getFilterTapWeight(tapInfo) * srcSample; + // Accumulate. + srcBlurred += srcSample * weight; + totalWeight += weight; + } } + 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 index 94417fa939..84c633d74c 100644 --- a/libraries/render/src/render/BlurTask_shared.slh +++ b/libraries/render/src/render/BlurTask_shared.slh @@ -7,4 +7,4 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#define BLUR_MAX_NUM_TAPS 21 +#define BLUR_MAX_NUM_TAPS 25 From 27b9f3516df5dc2395a4d71b69bfa91b94279f23 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 25 Sep 2017 18:05:30 +0200 Subject: [PATCH 04/16] Added mix parameter to blur and bloom --- libraries/render-utils/src/BloomEffect.cpp | 9 +++++---- libraries/render-utils/src/BloomEffect.h | 8 ++++---- libraries/render/src/render/BlurTask.cpp | 19 ++++++++++++++++--- libraries/render/src/render/BlurTask.h | 10 +++++++--- libraries/render/src/render/BlurTask.slh | 3 ++- 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index eb02c4fa57..24cdd5fca3 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -12,15 +12,16 @@ #include -void BloomConfig::setMix(float value) { - +void BloomConfig::setIntensity(float value) { + auto blurConfig = getConfig(); + blurConfig->setProperty("mix", value*0.5f); } void BloomConfig::setSize(float value) { auto& blurJob = static_cast(_task)->_jobs.front(); auto& gaussianBlur = blurJob.edit(); auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setGaussianFilterTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); + gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); } Bloom::Bloom() { @@ -29,7 +30,7 @@ Bloom::Bloom() { void Bloom::configure(const Config& config) { auto blurConfig = config.getConfig(); - blurConfig->setProperty("filterScale", 2.5f); + blurConfig->setProperty("filterScale", 3.0f); } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 278236d2e9..c8588c7a15 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -16,15 +16,15 @@ class BloomConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float mix MEMBER mix WRITE setMix NOTIFY dirty) + Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty) Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) public: - float mix{ 0.2f }; - float size{ 0.1f }; + float intensity{ 0.2f }; + float size{ 0.4f }; - void setMix(float value); + void setIntensity(float value); void setSize(float value); signals: diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 2e3cf8138f..66f098302d 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -32,7 +32,7 @@ enum BlurShaderMapSlots { BlurParams::BlurParams() { Params params; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Params), (const gpu::Byte*) ¶ms)); - setGaussianFilterTaps(3); + setFilterGaussianTaps(3); } void BlurParams::setWidthHeight(int width, int height, bool isStereo) { @@ -77,7 +77,7 @@ void BlurParams::setFilterTap(int index, float offset, float value) { filterTaps[index].y = value; } -void BlurParams::setGaussianFilterTaps(int numHalfTaps, float sigma) { +void BlurParams::setFilterGaussianTaps(int numHalfTaps, float sigma) { auto& params = _parametersBuffer.edit(); const int numTaps = 2 * numHalfTaps + 1; assert(numTaps <= BLUR_MAX_NUM_TAPS); @@ -106,6 +106,14 @@ void BlurParams::setGaussianFilterTaps(int numHalfTaps, float sigma) { // 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; + } +} + void BlurParams::setDepthPerspective(float oneOverTan2FOV) { auto depthInfo = _parametersBuffer.get().depthInfo; if (oneOverTan2FOV != depthInfo.w) { @@ -238,7 +246,13 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() { } void BlurGaussian::configure(const Config& config) { + auto blurHPipeline = getBlurHPipeline(); + _parameters->setFilterRadiusScale(config.filterScale); + _parameters->setOutputAlpha(config.mix); + + blurHPipeline->getState()->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); } @@ -248,7 +262,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 diff --git a/libraries/render/src/render/BlurTask.h b/libraries/render/src/render/BlurTask.h index 4df5a09576..cbee3e35f1 100644 --- a/libraries/render/src/render/BlurTask.h +++ b/libraries/render/src/render/BlurTask.h @@ -30,7 +30,8 @@ public: void setFilterNumTaps(int count); // Tap 0 is considered the center of the kernel void setFilterTap(int index, float offset, float value); - void setGaussianFilterTaps(int numHalfTaps, float sigma = 1.47f); + void setFilterGaussianTaps(int numHalfTaps, float sigma = 1.47f); + void setOutputAlpha(float value); void setDepthPerspective(float oneOverTan2FOV); void setDepthThreshold(float threshold); @@ -46,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, number of taps, mix) + // Filter info (radius scale, number of taps, output alpha) glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f }; // Depth info (radius scale @@ -93,12 +94,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(); diff --git a/libraries/render/src/render/BlurTask.slh b/libraries/render/src/render/BlurTask.slh index 5a41a0b1c6..37f29496bd 100644 --- a/libraries/render/src/render/BlurTask.slh +++ b/libraries/render/src/render/BlurTask.slh @@ -41,7 +41,7 @@ int getFilterNumTaps() { return int(parameters.filterInfo.y); } -float getFilterMix() { +float getOutputAlpha() { return parameters.filterInfo.z; } @@ -102,6 +102,7 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) { if (totalWeight>0.0) { srcBlurred /= totalWeight; } + srcBlurred.a = getOutputAlpha(); return srcBlurred; } From 27a0fb62d31c8f0e68f1fb48309926c386abb03d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 09:35:38 +0200 Subject: [PATCH 05/16] 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 From 50ab73009a89a00f23459760bca4f24dc2d7d83f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 11:09:51 +0200 Subject: [PATCH 06/16] Working threshold mask and debug on Bloom --- libraries/render-utils/src/BloomEffect.cpp | 31 +++++++++---------- libraries/render-utils/src/BloomThreshold.slf | 26 ++++++++++------ .../render-utils/src/RenderDeferredTask.cpp | 2 +- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index f083bf7e6b..1c62d031b7 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -70,21 +70,20 @@ void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderCo gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); -/* batch.setViewportTransform(halfViewport); + batch.setViewportTransform(halfViewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(inputFrameBuffer->getSize(), args->_viewport)); - batch.setPipeline(_pipeline);*/ + 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.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);*/ + batch.setFramebuffer(nullptr); }); outputs = _downsampledBuffer; @@ -102,13 +101,14 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In RenderArgs* args = renderContext->args; const auto frameBuffer = inputs.get0(); + const auto framebufferSize = frameBuffer->getSize(); const auto level0FB = inputs.get1(); const gpu::TexturePointer levelTextures[1] = { level0FB->getRenderBuffer(0) }; if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); @@ -123,19 +123,16 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); - //batch.setFramebuffer(frameBuffer); - batch.setViewportTransform(args->_viewport); + batch.setFramebuffer(frameBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(glm::ivec2(framebufferSize.x, framebufferSize.y), 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); }); } @@ -162,7 +159,9 @@ void Bloom::configure(const Config& config) { void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto halfSizeBuffer = task.addJob("BloomThreshold", inputs); + const auto& input = inputs.get(); + const auto& frameBuffer = input[1]; - const auto debugInput = DebugBloom::Inputs(inputs.get().get1(), halfSizeBuffer).asVarying(); + const auto debugInput = DebugBloom::Inputs(frameBuffer, halfSizeBuffer).asVarying(); task.addJob("DebugBloom", debugInput); } diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index 7c87ed9fcd..dbc16d892d 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -22,19 +22,25 @@ void main(void) { 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); + vec4 luminances; + vec3 luminanceWeights = vec3(0.3,0.5,0.2); + + luminances.x = dot(texel0, luminanceWeights); + luminances.y = dot(texel1, luminanceWeights); + luminances.z = dot(texel2, luminanceWeights); + luminances.w = dot(texel0, luminanceWeights); + + float hardness = 8; + vec4 mask = clamp((luminances-threshold) * hardness, 0, 1); + vec3 color; + + color.x = dot(mask, reds); + color.y = dot(mask, greens); + color.z = dot(mask, blues); + outFragColor = vec4(color/4.0, 1.0); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index a27b8f0ab5..f2afae7531 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -161,7 +161,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); From 0d3d309bceb56c4f212e27c7727af301b3fefbfb Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 11:49:29 +0200 Subject: [PATCH 07/16] Quarter size debug rendering of bloom --- libraries/render-utils/src/BloomEffect.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 1c62d031b7..6ec20d53c7 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -108,7 +108,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In }; if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); @@ -128,7 +128,10 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(glm::ivec2(framebufferSize.x, framebufferSize.y), args->_viewport)); + + Transform modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2); + modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f)); + batch.setModelTransform(modelTransform); batch.setPipeline(_pipeline); batch.setResourceTexture(0, levelTextures[0]); From cda48cbf67e539b1fed6a7aa677351b2228d1134 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 15:13:32 +0200 Subject: [PATCH 08/16] Three blurs working --- libraries/render-utils/src/BloomEffect.cpp | 55 ++++++++++++++++------ libraries/render-utils/src/BloomEffect.h | 2 +- libraries/render/src/render/BlurTask.cpp | 20 ++++---- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 6ec20d53c7..e292199bef 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -17,6 +17,8 @@ #include "BloomThreshold_frag.h" +#define BLOOM_BLUR_LEVEL_COUNT 3 + ThresholdAndDownsampleJob::ThresholdAndDownsampleJob() { } @@ -103,8 +105,12 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In const auto frameBuffer = inputs.get0(); const auto framebufferSize = frameBuffer->getSize(); const auto level0FB = inputs.get1(); - const gpu::TexturePointer levelTextures[1] = { - level0FB->getRenderBuffer(0) + 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) }; if (!_pipeline) { @@ -128,27 +134,36 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); + batch.setPipeline(_pipeline); Transform modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2); modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f)); batch.setModelTransform(modelTransform); - - batch.setPipeline(_pipeline); 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); }); } void BloomConfig::setIntensity(float value) { - /* auto blurConfig = getConfig(); - blurConfig->setProperty("mix", value*0.5f);*/ } void BloomConfig::setSize(float value) { - /* 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);*/ + for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { + auto& blurJob = static_cast(_task)->_jobs[i+1]; + auto& gaussianBlur = blurJob.edit(); + auto gaussianBlurParams = gaussianBlur.getParameters(); + gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); + } } Bloom::Bloom() { @@ -156,15 +171,25 @@ Bloom::Bloom() { } void Bloom::configure(const Config& config) { -/* auto blurConfig = config.getConfig(); - blurConfig->setProperty("filterScale", 3.0f);*/ + 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", 3.0f); + } } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto halfSizeBuffer = task.addJob("BloomThreshold", inputs); + const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs); + + // Level 0 blur + const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer); + const auto blurFB1 = task.addJob("BloomBlur1", blurFB0, true); + const auto blurFB2 = task.addJob("BloomBlur2", blurFB1, true); + const auto& input = inputs.get(); const auto& frameBuffer = input[1]; - - const auto debugInput = DebugBloom::Inputs(frameBuffer, halfSizeBuffer).asVarying(); + const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying(); task.addJob("DebugBloom", debugInput); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index ec14a6ad9f..bd74939ea4 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -77,7 +77,7 @@ public: class DebugBloom { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet4; using Config = DebugBloomConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 66f098302d..b42bae3950 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -181,7 +181,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(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler); _outputFramebuffer->setRenderBuffer(0, blurringTarget); } @@ -246,13 +246,16 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() { } void BlurGaussian::configure(const Config& config) { - auto blurHPipeline = getBlurHPipeline(); + auto state = getBlurHPipeline()->getState(); _parameters->setFilterRadiusScale(config.filterScale); _parameters->setOutputAlpha(config.mix); - - blurHPipeline->getState()->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); + 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); + } } @@ -271,14 +274,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()); + _parameters->setWidthHeight(viewport.z, viewport.w, args->isStereo()); glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); - _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport)); + _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); From 02612653306c4dcb459aa5c40bbe770c1ce7cf4a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 9 Oct 2017 19:17:51 +0200 Subject: [PATCH 09/16] Working bloom but still visually unstable with geometry aliasing --- libraries/render-utils/src/BloomApply.slf | 27 +++++ libraries/render-utils/src/BloomEffect.cpp | 109 ++++++++++++++++-- libraries/render-utils/src/BloomEffect.h | 40 ++++++- libraries/render-utils/src/BloomThreshold.slf | 5 +- .../render-utils/src/DeferredFramebuffer.cpp | 4 +- libraries/render/src/render/BlurTask.cpp | 10 +- .../render/src/render/BlurTask_shared.slh | 2 +- libraries/render/src/render/ResampleTask.cpp | 88 ++++++++++++++ libraries/render/src/render/ResampleTask.h | 40 +++++++ libraries/render/src/task/Task.h | 20 ++++ scripts/developer/utilities/render/bloom.qml | 4 +- 11 files changed, 320 insertions(+), 29 deletions(-) create mode 100644 libraries/render-utils/src/BloomApply.slf create mode 100644 libraries/render/src/render/ResampleTask.cpp create mode 100644 libraries/render/src/render/ResampleTask.h diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf new file mode 100644 index 0000000000..17f13b1187 --- /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)/3.0, intensity); +} diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index e292199bef..f289f59faa 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -14,8 +14,10 @@ #include "gpu/StandardShaderLib.h" #include +#include #include "BloomThreshold_frag.h" +#include "BloomApply_frag.h" #define BLOOM_BLUR_LEVEL_COUNT 3 @@ -46,9 +48,9 @@ void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderCo 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))); + gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _downsampledBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomBlur0")); + _downsampledBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold")); _downsampledBuffer->setRenderBuffer(0, colorTexture); } @@ -91,6 +93,67 @@ void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderCo outputs = _downsampledBuffer; } +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->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _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(); + + 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(BLUR0_SLOT, blur0FB->getRenderBuffer(0)); + batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0)); + batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0)); + batch._glUniform1f(INTENSITY_SLOT, _intensity); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); +} + DebugBloom::DebugBloom() { } @@ -155,14 +218,34 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In } 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 = value*3.0f; + for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { - auto& blurJob = static_cast(_task)->_jobs[i+1]; - auto& gaussianBlur = blurJob.edit(); + 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((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f); + gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, sigma); + // Gaussian blur increases at each level to have a slower rolloff on the edge + // of the response + sigma *= 1.5f; } } @@ -176,20 +259,24 @@ void Bloom::configure(const Config& config) { for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { blurName.back() = '0' + i; auto blurConfig = config.getConfig(blurName); - blurConfig->setProperty("filterScale", 3.0f); + blurConfig->setProperty("filterScale", 1.0f); } } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs); - // Level 0 blur + // Multi-scale blur const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer); - const auto blurFB1 = task.addJob("BloomBlur1", blurFB0, true); - const auto blurFB2 = task.addJob("BloomBlur2", blurFB1, true); + const auto halfBlurFB0 = task.addJob("BloomHalfBlur0", blurFB0); + const auto blurFB1 = task.addJob("BloomBlur1", halfBlurFB0, true); + const auto halfBlurFB1 = task.addJob("BloomHalfBlur1", blurFB1); + const auto blurFB2 = task.addJob("BloomBlur2", halfBlurFB1, true); const auto& input = inputs.get(); const auto& frameBuffer = input[1]; - const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying(); - task.addJob("DebugBloom", debugInput); + + const auto applyInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + task.addJob("BloomApply", applyInput); + task.addJob("DebugBloom", applyInput); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index bd74939ea4..7c99555bd3 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -18,17 +18,17 @@ class BloomConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty) + 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(true) {} - float intensity{ 0.2f }; - float size{ 0.4f }; + float size{ 0.45f }; void setIntensity(float value); + float getIntensity() const; void setSize(float value); signals: @@ -41,7 +41,7 @@ class ThresholdConfig : public render::Job::Config { public: - float threshold{ 0.25f }; + float threshold{ 1.25f }; signals: void dirty(); @@ -66,12 +66,42 @@ private: float _threshold; }; + +class BloomApplyConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) + +public: + + float intensity{ 0.5f }; + +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 DebugBloomConfig : public render::Job::Config { Q_OBJECT public: - DebugBloomConfig() : render::Job::Config(true) {} + DebugBloomConfig() : render::Job::Config(false) {} }; diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index dbc16d892d..d14da4889d 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -28,15 +28,14 @@ void main(void) { vec3 texel3 = vec3(reds.w, greens.w, blues.w); vec4 luminances; - vec3 luminanceWeights = vec3(0.3,0.5,0.2); + vec3 luminanceWeights = vec3(0.333,0.333,0.333); luminances.x = dot(texel0, luminanceWeights); luminances.y = dot(texel1, luminanceWeights); luminances.z = dot(texel2, luminanceWeights); luminances.w = dot(texel0, luminanceWeights); - float hardness = 8; - vec4 mask = clamp((luminances-threshold) * hardness, 0, 1); + vec4 mask = clamp(luminances-threshold, 0, 1); vec3 color; color.x = dot(mask, reds); diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index 64ea8f0342..2df89b8808 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -73,9 +73,9 @@ void DeferredFramebuffer::allocate() { _deferredFramebufferDepthColor->setDepthStencilBuffer(_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/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index b42bae3950..a200e0c8ec 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -48,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; } } @@ -274,7 +274,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra auto blurVPipeline = getBlurVPipeline(); auto blurHPipeline = getBlurHPipeline(); - glm::ivec4 viewport = { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() }; + glm::ivec4 viewport { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() }; _parameters->setWidthHeight(viewport.z, viewport.w, args->isStereo()); glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); diff --git a/libraries/render/src/render/BlurTask_shared.slh b/libraries/render/src/render/BlurTask_shared.slh index 84c633d74c..beca32c1be 100644 --- a/libraries/render/src/render/BlurTask_shared.slh +++ b/libraries/render/src/render/BlurTask_shared.slh @@ -7,4 +7,4 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#define BLUR_MAX_NUM_TAPS 25 +#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..5d4b5c18cb --- /dev/null +++ b/libraries/render/src/render/ResampleTask.cpp @@ -0,0 +1,88 @@ +// +// 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; + +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); + + 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); + } + + const auto sourceSize = sourceFramebuffer->getSize(); + 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)); + // Add half a texel of offset to be sure to sample in the middle of 4 neighbouring texture pixels + // to perform box filtering. + batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f / sourceSize.x, 0.5f / sourceSize.y, 1.f, 1.f); + 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..70cb9a54bb --- /dev/null +++ b/libraries/render/src/render/ResampleTask.h @@ -0,0 +1,40 @@ +// +// 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: + + 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 e99b33305c..0f2265b396 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 index 7deeffc218..d2a928861c 100644 --- a/scripts/developer/utilities/render/bloom.qml +++ b/scripts/developer/utilities/render/bloom.qml @@ -40,7 +40,7 @@ Item { integral: false config: root.config property: "intensity" - max: 1.0 + max: 5.0 min: 0.0 width: 280 } @@ -58,7 +58,7 @@ Item { integral: false config: root.configThreshold property: "threshold" - max: 1.0 + max: 2.0 min: 0.0 width: 280 } From 76305c5285f6d33e100460677d9549652e8f9c2f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 10 Oct 2017 14:49:04 +0200 Subject: [PATCH 10/16] Better debugging options for bloom --- libraries/render-utils/src/BloomEffect.cpp | 53 +++++++++++----- libraries/render-utils/src/BloomEffect.h | 18 +++++- scripts/developer/utilities/render/bloom.qml | 63 ++++++++++++++++++-- 3 files changed, 112 insertions(+), 22 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index f289f59faa..c6c30e7ff6 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -157,7 +157,9 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In DebugBloom::DebugBloom() { } -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) { @@ -176,12 +178,15 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In level2FB->getRenderBuffer(0) }; + static auto TEXCOORD_RECT_SLOT = 1; + if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + 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()); @@ -199,21 +204,39 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In batch.resetViewTransform(); batch.setPipeline(_pipeline); - Transform 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); + Transform modelTransform; + if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) { + batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f); - 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 = 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, -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, 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); + } 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); + } }); } diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 7c99555bd3..637e37aa04 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -98,11 +98,25 @@ private: 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 { @@ -112,13 +126,13 @@ public: using JobModel = render::Job::ModelI; DebugBloom(); - ~DebugBloom(); - void configure(const Config& config) {} + void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: gpu::PipelinePointer _pipeline; + DebugBloomConfig::Mode _mode; }; class Bloom { diff --git a/scripts/developer/utilities/render/bloom.qml b/scripts/developer/utilities/render/bloom.qml index d2a928861c..66e92e0eff 100644 --- a/scripts/developer/utilities/render/bloom.qml +++ b/scripts/developer/utilities/render/bloom.qml @@ -28,11 +28,64 @@ Item { root.config["enabled"] = checked; } } - CheckBox { - text: "Debug" - checked: root.configDebug["enabled"] - onCheckedChanged: { - root.configDebug["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 { From f55d44dfc33e0b48431ef41ea84ce5edd6fcba71 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 10 Oct 2017 17:49:35 +0200 Subject: [PATCH 11/16] Threshold now doesn't downsample anymore. Done in separate pass --- libraries/render-utils/src/BloomEffect.cpp | 53 +++++++++++-------- libraries/render-utils/src/BloomEffect.h | 16 +++--- libraries/render-utils/src/BloomThreshold.slf | 28 ++-------- libraries/render/src/render/ResampleTask.cpp | 8 +-- 4 files changed, 44 insertions(+), 61 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index c6c30e7ff6..e236b290c2 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -21,15 +21,15 @@ #define BLOOM_BLUR_LEVEL_COUNT 3 -ThresholdAndDownsampleJob::ThresholdAndDownsampleJob() { +BloomThreshold::BloomThreshold() { } -void ThresholdAndDownsampleJob::configure(const Config& config) { +void BloomThreshold::configure(const Config& config) { _threshold = config.threshold; } -void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { +void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -40,25 +40,23 @@ void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderCo assert(inputFrameBuffer->hasColor()); - auto inputColor = inputFrameBuffer->getRenderBuffer(0); + auto inputBuffer = inputFrameBuffer->getRenderBuffer(0); auto sourceViewport = args->_viewport; - auto fullSize = glm::ivec2(inputColor->getDimensions()); - auto halfSize = fullSize / 2; - auto halfViewport = args->_viewport >> 1; + auto bufferSize = glm::ivec2(inputBuffer->getDimensions()); - 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, + if (!_outputBuffer || _outputBuffer->getSize() != inputFrameBuffer->getSize()) { + 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))); - _downsampledBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold")); - _downsampledBuffer->setRenderBuffer(0, colorTexture); + _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::getDrawViewportQuadTransformTexcoordVS(); + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); @@ -74,23 +72,19 @@ void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderCo gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setViewportTransform(halfViewport); + batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(inputFrameBuffer->getSize(), args->_viewport)); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, args->_viewport)); batch.setPipeline(_pipeline); - batch.setFramebuffer(_downsampledBuffer); - batch.setResourceTexture(COLOR_MAP_SLOT, inputColor); + batch.setFramebuffer(_outputBuffer); + batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer); 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; + outputs = _outputBuffer; } BloomApply::BloomApply() { @@ -287,14 +281,27 @@ void Bloom::configure(const Config& config) { } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs); + const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs); + const auto bloomHalfInputBuffer = task.addJob("BloomHalf", bloomInputBuffer); + const auto bloomQuarterInputBuffer = task.addJob("BloomQuarter", bloomHalfInputBuffer); +#if 1 // Multi-scale blur - const auto blurFB0 = task.addJob("BloomBlur0", bloomInputBuffer); + const auto blurFB0 = task.addJob("BloomBlur0", bloomQuarterInputBuffer); const auto halfBlurFB0 = task.addJob("BloomHalfBlur0", blurFB0); const auto blurFB1 = task.addJob("BloomBlur1", halfBlurFB0, true); const auto halfBlurFB1 = task.addJob("BloomHalfBlur1", blurFB1); const auto blurFB2 = task.addJob("BloomBlur2", halfBlurFB1, true); +#else + // Multi-scale downsampling debug + const auto blurFB0 = bloomQuarterInputBuffer; + const auto blurFB1 = task.addJob("BloomHalfBlur1", blurFB0); + const auto blurFB2 = task.addJob("BloomHalfBlur2", blurFB1); + // This is only needed so as not to crash as we expect to have the three blur jobs + task.addJob("BloomBlur0", bloomHalfInputBuffer, true); + task.addJob("BloomBlur1", blurFB1, true); + task.addJob("BloomBlur2", blurFB2, true); +#endif const auto& input = inputs.get(); const auto& frameBuffer = input[1]; diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 637e37aa04..fdc2d1a0ba 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -25,7 +25,7 @@ public: BloomConfig() : render::Task::Config(true) {} - float size{ 0.45f }; + float size{ 0.8f }; void setIntensity(float value); float getIntensity() const; @@ -35,7 +35,7 @@ signals: void dirty(); }; -class ThresholdConfig : public render::Job::Config { +class BloomThresholdConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) @@ -47,21 +47,21 @@ signals: void dirty(); }; -class ThresholdAndDownsampleJob { +class BloomThreshold { public: using Inputs = render::VaryingSet2; using Outputs = gpu::FramebufferPointer; - using Config = ThresholdConfig; - using JobModel = render::Job::ModelIO; + using Config = BloomThresholdConfig; + using JobModel = render::Job::ModelIO; - ThresholdAndDownsampleJob(); + BloomThreshold(); void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); private: - gpu::FramebufferPointer _downsampledBuffer; + gpu::FramebufferPointer _outputBuffer; gpu::PipelinePointer _pipeline; float _threshold; }; @@ -73,7 +73,7 @@ class BloomApplyConfig : public render::Job::Config { public: - float intensity{ 0.5f }; + float intensity{ 0.8f }; signals: void dirty(); diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index d14da4889d..14d20dd684 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -17,29 +17,11 @@ 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); + vec4 color = texture(colorMap, varTexCoord0); + float luminance = (color.r+color.g+color.b) / 3.0; + float mask = clamp((luminance-threshold)*0.25, 0, 1); - 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); + color *= mask; - vec4 luminances; - vec3 luminanceWeights = vec3(0.333,0.333,0.333); - - luminances.x = dot(texel0, luminanceWeights); - luminances.y = dot(texel1, luminanceWeights); - luminances.z = dot(texel2, luminanceWeights); - luminances.w = dot(texel0, luminanceWeights); - - vec4 mask = clamp(luminances-threshold, 0, 1); - vec3 color; - - color.x = dot(mask, reds); - color.y = dot(mask, greens); - color.z = dot(mask, blues); - outFragColor = vec4(color/4.0, 1.0); + outFragColor = vec4(color.rgb, 1.0); } diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index 5d4b5c18cb..ef75b54094 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -48,15 +48,12 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer); - static auto TEXCOORD_RECT_SLOT = 1; - if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS(); + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); 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()); @@ -80,9 +77,6 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0)); - // Add half a texel of offset to be sure to sample in the middle of 4 neighbouring texture pixels - // to perform box filtering. - batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f / sourceSize.x, 0.5f / sourceSize.y, 1.f, 1.f); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } From d3add89b3c3a8b0cc88f589a55131c269535dccc Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 10 Oct 2017 18:28:42 +0200 Subject: [PATCH 12/16] Adjusted bloom size range --- libraries/render-utils/src/BloomEffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index e236b290c2..239508cd50 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -250,7 +250,7 @@ float BloomConfig::getIntensity() const { void BloomConfig::setSize(float value) { std::string blurName{ "BloomBlurN" }; - auto sigma = value*3.0f; + auto sigma = 0.5f+value*3.5f; for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { blurName.back() = '0' + i; From 055270df9c6bec1d20b97f4f9df0b2bee44d829b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 12 Oct 2017 19:26:05 +0200 Subject: [PATCH 13/16] Removed compilation warnings on Mac and Ubuntu --- libraries/render-utils/src/BloomEffect.cpp | 1 - libraries/render/src/render/BlurTask.cpp | 2 +- libraries/render/src/render/ResampleTask.cpp | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 239508cd50..69bc790c59 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -41,7 +41,6 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons assert(inputFrameBuffer->hasColor()); auto inputBuffer = inputFrameBuffer->getRenderBuffer(0); - auto sourceViewport = args->_viewport; auto bufferSize = glm::ivec2(inputBuffer->getDimensions()); if (!_outputBuffer || _outputBuffer->getSize() != inputFrameBuffer->getSize()) { diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index a200e0c8ec..5b759ca33c 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -82,7 +82,7 @@ void BlurParams::setFilterGaussianTaps(int numHalfTaps, float sigma) { const int numTaps = 2 * numHalfTaps + 1; assert(numTaps <= BLUR_MAX_NUM_TAPS); assert(sigma > 0.0f); - const float inverseTwoSigmaSquared = float(0.5 / (sigma*sigma)); + const float inverseTwoSigmaSquared = float(0.5 / double(sigma*sigma)); float totalWeight = 1.0f; float weight; float offset; diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index ef75b54094..929db50c31 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -61,7 +61,6 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F _pipeline = gpu::Pipeline::create(program, state); } - const auto sourceSize = sourceFramebuffer->getSize(); const auto bufferSize = resampledFrameBuffer->getSize(); glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; From 39604363eaeefb11ceae5c3870b117c0a21bcc5b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 8 Nov 2017 11:38:20 +0100 Subject: [PATCH 14/16] Optimized performance of bloom by reducing blur resolution and merging some downsampling passes with blur passes --- libraries/render-utils/src/BloomApply.slf | 2 +- libraries/render-utils/src/BloomEffect.cpp | 116 +++++++++++++----- libraries/render-utils/src/BloomEffect.h | 19 ++- libraries/render-utils/src/BloomThreshold.slf | 28 ++++- libraries/render/src/render/BlurTask.cpp | 32 +++-- libraries/render/src/render/BlurTask.h | 7 +- libraries/render/src/render/ResampleTask.cpp | 4 +- libraries/render/src/render/ResampleTask.h | 3 +- 8 files changed, 151 insertions(+), 60 deletions(-) diff --git a/libraries/render-utils/src/BloomApply.slf b/libraries/render-utils/src/BloomApply.slf index 17f13b1187..953258e8ab 100644 --- a/libraries/render-utils/src/BloomApply.slf +++ b/libraries/render-utils/src/BloomApply.slf @@ -23,5 +23,5 @@ void main(void) { vec4 blur1 = texture(blurMap1, varTexCoord0); vec4 blur2 = texture(blurMap2, varTexCoord0); - outFragColor = vec4((blur0.rgb+blur1.rgb+blur2.rgb)/3.0, intensity); + 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 index 69bc790c59..9d9367a6d5 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -21,8 +21,9 @@ #define BLOOM_BLUR_LEVEL_COUNT 3 -BloomThreshold::BloomThreshold() { - +BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) : + _downsamplingFactor(downsamplingFactor) { + assert(downsamplingFactor > 0); } void BloomThreshold::configure(const Config& config) { @@ -41,9 +42,13 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons assert(inputFrameBuffer->hasColor()); auto inputBuffer = inputFrameBuffer->getRenderBuffer(0); - auto bufferSize = glm::ivec2(inputBuffer->getDimensions()); + auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions()); - if (!_outputBuffer || _outputBuffer->getSize() != inputFrameBuffer->getSize()) { + // 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))); @@ -68,13 +73,15 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons _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(args->_viewport); + batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, args->_viewport)); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport)); batch.setPipeline(_pipeline); batch.setFramebuffer(_outputBuffer); @@ -117,8 +124,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + state->setDepthTest(gpu::State::DepthTest(false, false)); _pipeline = gpu::Pipeline::create(program, state); } @@ -127,26 +133,70 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In 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(args->_viewport); + batch.setViewportTransform(viewport); batch.setProjectionTransform(glm::mat4()); batch.resetViewTransform(); batch.setPipeline(_pipeline); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); + 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); + 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() { } @@ -161,6 +211,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In 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(); @@ -216,6 +267,11 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In 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; @@ -258,7 +314,7 @@ void BloomConfig::setSize(float value) { assert(blurJobIt != task->_jobs.end()); auto& gaussianBlur = blurJobIt->edit(); auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, sigma); + gaussianBlurParams->setFilterGaussianTaps(5, sigma); // Gaussian blur increases at each level to have a slower rolloff on the edge // of the response sigma *= 1.5f; @@ -280,32 +336,24 @@ void Bloom::configure(const Config& config) { } void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { - const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs); - const auto bloomHalfInputBuffer = task.addJob("BloomHalf", bloomInputBuffer); - const auto bloomQuarterInputBuffer = task.addJob("BloomQuarter", bloomHalfInputBuffer); + // Start by computing threshold of color buffer input at quarter resolution + const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U); -#if 1 - // Multi-scale blur - const auto blurFB0 = task.addJob("BloomBlur0", bloomQuarterInputBuffer); - const auto halfBlurFB0 = task.addJob("BloomHalfBlur0", blurFB0); - const auto blurFB1 = task.addJob("BloomBlur1", halfBlurFB0, true); - const auto halfBlurFB1 = task.addJob("BloomHalfBlur1", blurFB1); - const auto blurFB2 = task.addJob("BloomBlur2", halfBlurFB1, true); -#else - // Multi-scale downsampling debug - const auto blurFB0 = bloomQuarterInputBuffer; - const auto blurFB1 = task.addJob("BloomHalfBlur1", blurFB0); - const auto blurFB2 = task.addJob("BloomHalfBlur2", blurFB1); - // This is only needed so as not to crash as we expect to have the three blur jobs - task.addJob("BloomBlur0", bloomHalfInputBuffer, true); - task.addJob("BloomBlur1", blurFB1, true); - task.addJob("BloomBlur2", blurFB2, true); -#endif + // 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]; - const auto applyInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + // Mix all blur levels at quarter resolution + const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying(); task.addJob("BloomApply", applyInput); - task.addJob("DebugBloom", 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 index fdc2d1a0ba..76ce1b8f52 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -54,7 +54,7 @@ public: using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; - BloomThreshold(); + BloomThreshold(unsigned int downsamplingFactor); void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); @@ -64,6 +64,7 @@ private: gpu::FramebufferPointer _outputBuffer; gpu::PipelinePointer _pipeline; float _threshold; + unsigned int _downsamplingFactor; }; @@ -96,6 +97,20 @@ private: 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) @@ -121,7 +136,7 @@ signals: class DebugBloom { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = DebugBloomConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/BloomThreshold.slf b/libraries/render-utils/src/BloomThreshold.slf index 14d20dd684..e4b96618df 100644 --- a/libraries/render-utils/src/BloomThreshold.slf +++ b/libraries/render-utils/src/BloomThreshold.slf @@ -16,12 +16,30 @@ uniform float threshold; in vec2 varTexCoord0; out vec4 outFragColor; +#define DOWNSAMPLING_FACTOR 4 +#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2) + void main(void) { - vec4 color = texture(colorMap, varTexCoord0); - float luminance = (color.r+color.g+color.b) / 3.0; - float mask = clamp((luminance-threshold)*0.25, 0, 1); + vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT; + vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT; + vec2 startUv = varTexCoord0; + vec4 maskedColor = vec4(0,0,0,0); - color *= mask; + for (int y=0 ; y 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(); } @@ -158,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::createRenderBuffer(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); } @@ -167,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(); } @@ -181,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::createRenderBuffer(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); } @@ -195,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(); } @@ -276,8 +282,8 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra auto blurHPipeline = getBlurHPipeline(); glm::ivec4 viewport { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() }; - _parameters->setWidthHeight(viewport.z, viewport.w, args->isStereo()); - glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); + 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) { @@ -310,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 cbee3e35f1..e8d268dc63 100644 --- a/libraries/render/src/render/BlurTask.h +++ b/libraries/render/src/render/BlurTask.h @@ -72,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; @@ -85,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 }; }; @@ -115,7 +116,7 @@ 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); diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index 929db50c31..65c0ff45b9 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -17,6 +17,8 @@ using namespace render; +gpu::PipelinePointer HalfDownsample::_pipeline; + HalfDownsample::HalfDownsample() { } @@ -57,7 +59,7 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(false)); + state->setDepthTest(gpu::State::DepthTest(false, false)); _pipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/render/src/render/ResampleTask.h b/libraries/render/src/render/ResampleTask.h index 70cb9a54bb..da2b7b3537 100644 --- a/libraries/render/src/render/ResampleTask.h +++ b/libraries/render/src/render/ResampleTask.h @@ -30,7 +30,8 @@ namespace render { protected: - gpu::PipelinePointer _pipeline; + static gpu::PipelinePointer _pipeline; + gpu::FramebufferPointer _destinationFrameBuffer; gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); From bccd6df57c14c6586218597db4fcc9bc61a4676f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 9 Nov 2017 11:19:28 +0100 Subject: [PATCH 15/16] Fixed warning on Mac and Ubuntu --- libraries/render/src/render/BlurTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 54ec531c8b..2be6f8fad2 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -137,8 +137,8 @@ void BlurParams::setLinearDepthPosFar(float farPosDepth) { BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor) : -_generateOutputFramebuffer(generateOutputFramebuffer), -_downsampleFactor(downsampleFactor) { + _downsampleFactor(downsampleFactor), + _generateOutputFramebuffer(generateOutputFramebuffer) { assert(downsampleFactor > 0); } From c5bd86a4bf401f836ef7bbab8227daf3002ce98e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 21:54:40 +0100 Subject: [PATCH 16/16] Disabled bloom by default --- libraries/render-utils/src/BloomEffect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 76ce1b8f52..5352c65e4d 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -23,7 +23,7 @@ class BloomConfig : public render::Task::Config { public: - BloomConfig() : render::Task::Config(true) {} + BloomConfig() : render::Task::Config(false) {} float size{ 0.8f };