Working bloom but still visually unstable with geometry aliasing

This commit is contained in:
Olivier Prat 2017-10-09 19:17:51 +02:00
parent cda48cbf67
commit 0261265330
11 changed files with 320 additions and 29 deletions

View file

@ -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);
}

View file

@ -14,8 +14,10 @@
#include "gpu/StandardShaderLib.h"
#include <render/BlurTask.h>
#include <render/ResampleTask.h>
#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<render::Task::TaskConcept*>(_task);
auto blurJobIt = task->editJob("BloomApply");
assert(blurJobIt != task->_jobs.end());
blurJobIt->getConfiguration()->setProperty("intensity", value);
}
float BloomConfig::getIntensity() const {
auto task = static_cast<render::Task::TaskConcept*>(_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<render::Task::TaskConcept*>(_task)->_jobs[i+1];
auto& gaussianBlur = blurJob.edit<render::BlurGaussian>();
blurName.back() = '0' + i;
auto task = static_cast<render::Task::TaskConcept*>(_task);
auto blurJobIt = task->editJob(blurName);
assert(blurJobIt != task->_jobs.end());
auto& gaussianBlur = blurJobIt->edit<render::BlurGaussian>();
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<render::BlurGaussian>(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<ThresholdAndDownsampleJob>("BloomThreshold", inputs);
// Level 0 blur
// Multi-scale blur
const auto blurFB0 = task.addJob<render::BlurGaussian>("BloomBlur0", bloomInputBuffer);
const auto blurFB1 = task.addJob<render::BlurGaussian>("BloomBlur1", blurFB0, true);
const auto blurFB2 = task.addJob<render::BlurGaussian>("BloomBlur2", blurFB1, true);
const auto halfBlurFB0 = task.addJob<render::HalfDownsample>("BloomHalfBlur0", blurFB0);
const auto blurFB1 = task.addJob<render::BlurGaussian>("BloomBlur1", halfBlurFB0, true);
const auto halfBlurFB1 = task.addJob<render::HalfDownsample>("BloomHalfBlur1", blurFB1);
const auto blurFB2 = task.addJob<render::BlurGaussian>("BloomBlur2", halfBlurFB1, true);
const auto& input = inputs.get<Inputs>();
const auto& frameBuffer = input[1];
const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying();
task.addJob<DebugBloom>("DebugBloom", debugInput);
const auto applyInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2).asVarying();
task.addJob<BloomApply>("BloomApply", applyInput);
task.addJob<DebugBloom>("DebugBloom", applyInput);
}

View file

@ -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<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
using Config = BloomApplyConfig;
using JobModel = render::Job::ModelI<BloomApply, Inputs, Config>;
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) {}
};

View file

@ -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);

View file

@ -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);

View file

@ -48,10 +48,10 @@ void BlurParams::setWidthHeight(int width, int height, bool isStereo) {
}
}
void BlurParams::setTexcoordTransform(const glm::vec4 texcoordTransformViewport) {
auto texcoordTransform = _parametersBuffer.get<Params>().texcoordTransform;
if (texcoordTransformViewport != texcoordTransform) {
_parametersBuffer.edit<Params>().texcoordTransform = texcoordTransform;
void BlurParams::setTexcoordTransform(glm::vec4 texcoordTransformViewport) {
auto& params = _parametersBuffer.get<Params>();
if (texcoordTransformViewport != params.texcoordTransform) {
_parametersBuffer.edit<Params>().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());

View file

@ -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

View file

@ -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);
});
}

View file

@ -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, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
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

View file

@ -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) {}

View file

@ -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
}