mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 21:33:48 +02:00
commit
3f5b21ec22
15 changed files with 1067 additions and 89 deletions
27
libraries/render-utils/src/BloomApply.slf
Normal file
27
libraries/render-utils/src/BloomApply.slf
Normal 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)*intensity, 1.0f);
|
||||
}
|
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
359
libraries/render-utils/src/BloomEffect.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
//
|
||||
// BloomEffect.cpp
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "BloomEffect.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
#include <render/BlurTask.h>
|
||||
#include <render/ResampleTask.h>
|
||||
|
||||
#include "BloomThreshold_frag.h"
|
||||
#include "BloomApply_frag.h"
|
||||
|
||||
#define BLOOM_BLUR_LEVEL_COUNT 3
|
||||
|
||||
BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) :
|
||||
_downsamplingFactor(downsamplingFactor) {
|
||||
assert(downsamplingFactor > 0);
|
||||
}
|
||||
|
||||
void BloomThreshold::configure(const Config& config) {
|
||||
_threshold = config.threshold;
|
||||
}
|
||||
|
||||
void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameTransform = inputs.get0();
|
||||
const auto inputFrameBuffer = inputs.get1();
|
||||
|
||||
assert(inputFrameBuffer->hasColor());
|
||||
|
||||
auto inputBuffer = inputFrameBuffer->getRenderBuffer(0);
|
||||
auto bufferSize = gpu::Vec2u(inputBuffer->getDimensions());
|
||||
|
||||
// Downsample resolution
|
||||
bufferSize.x /= _downsamplingFactor;
|
||||
bufferSize.y /= _downsamplingFactor;
|
||||
|
||||
if (!_outputBuffer || _outputBuffer->getSize() != bufferSize) {
|
||||
auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputBuffer->getTexelFormat(), bufferSize.x, bufferSize.y,
|
||||
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
|
||||
_outputBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomThreshold"));
|
||||
_outputBuffer->setRenderBuffer(0, colorTexture);
|
||||
}
|
||||
|
||||
static const int COLOR_MAP_SLOT = 0;
|
||||
static const int THRESHOLD_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setFramebuffer(_outputBuffer);
|
||||
batch.setResourceTexture(COLOR_MAP_SLOT, inputBuffer);
|
||||
batch._glUniform1f(THRESHOLD_SLOT, _threshold);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
|
||||
outputs = _outputBuffer;
|
||||
}
|
||||
|
||||
BloomApply::BloomApply() {
|
||||
|
||||
}
|
||||
|
||||
void BloomApply::configure(const Config& config) {
|
||||
_intensity = config.intensity;
|
||||
}
|
||||
|
||||
void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
static auto BLUR0_SLOT = 0;
|
||||
static auto BLUR1_SLOT = 1;
|
||||
static auto BLUR2_SLOT = 2;
|
||||
static auto INTENSITY_SLOT = 3;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(BloomApply_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap0", BLUR0_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap1", BLUR1_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("blurMap2", BLUR2_SLOT));
|
||||
slotBindings.insert(gpu::Shader::Binding("intensity", INTENSITY_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto blur0FB = inputs.get1();
|
||||
const auto blur1FB = inputs.get2();
|
||||
const auto blur2FB = inputs.get3();
|
||||
const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport));
|
||||
batch.setResourceTexture(BLUR0_SLOT, blur0FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR1_SLOT, blur1FB->getRenderBuffer(0));
|
||||
batch.setResourceTexture(BLUR2_SLOT, blur2FB->getRenderBuffer(0));
|
||||
batch._glUniform1f(INTENSITY_SLOT, _intensity / 3.0f);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
|
||||
void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto bloomFrameBuffer = inputs.get1();
|
||||
|
||||
if (frameBuffer && bloomFrameBuffer) {
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
|
||||
gpu::State::ZERO, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
|
||||
batch.setResourceTexture(0, bloomFrameBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DebugBloom::DebugBloom() {
|
||||
}
|
||||
|
||||
void DebugBloom::configure(const Config& config) {
|
||||
_mode = static_cast<DebugBloomConfig::Mode>(config.mode);
|
||||
assert(_mode < DebugBloomConfig::MODE_COUNT);
|
||||
}
|
||||
|
||||
void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto frameBuffer = inputs.get0();
|
||||
const auto combinedBlurBuffer = inputs.get4();
|
||||
const auto framebufferSize = frameBuffer->getSize();
|
||||
const auto level0FB = inputs.get1();
|
||||
const auto level1FB = inputs.get2();
|
||||
const auto level2FB = inputs.get3();
|
||||
const gpu::TexturePointer levelTextures[BLOOM_BLUR_LEVEL_COUNT] = {
|
||||
level0FB->getRenderBuffer(0),
|
||||
level1FB->getRenderBuffer(0),
|
||||
level2FB->getRenderBuffer(0)
|
||||
};
|
||||
|
||||
static auto TEXCOORD_RECT_SLOT = 1;
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("texcoordRect"), TEXCOORD_RECT_SLOT));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(frameBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
Transform modelTransform;
|
||||
if (_mode == DebugBloomConfig::MODE_ALL_LEVELS) {
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.0f, 0.0f, 1.f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport / 2);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 1.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[0]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[1]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(-2.0f, -2.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[2]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
modelTransform.postTranslate(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, combinedBlurBuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
} else {
|
||||
auto viewport = args->_viewport;
|
||||
auto blurLevel = _mode - DebugBloomConfig::MODE_LEVEL0;
|
||||
|
||||
viewport.z /= 2;
|
||||
|
||||
batch._glUniform4f(TEXCOORD_RECT_SLOT, 0.5f, 0.0f, 0.5f, 1.f);
|
||||
|
||||
modelTransform = gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, viewport);
|
||||
modelTransform.postTranslate(glm::vec3(-1.0f, 0.0f, 0.0f));
|
||||
batch.setModelTransform(modelTransform);
|
||||
batch.setResourceTexture(0, levelTextures[blurLevel]);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void BloomConfig::setIntensity(float value) {
|
||||
auto task = static_cast<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 = 0.5f+value*3.5f;
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
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(5, sigma);
|
||||
// Gaussian blur increases at each level to have a slower rolloff on the edge
|
||||
// of the response
|
||||
sigma *= 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
Bloom::Bloom() {
|
||||
|
||||
}
|
||||
|
||||
void Bloom::configure(const Config& config) {
|
||||
std::string blurName{ "BloomBlurN" };
|
||||
|
||||
for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) {
|
||||
blurName.back() = '0' + i;
|
||||
auto blurConfig = config.getConfig<render::BlurGaussian>(blurName);
|
||||
blurConfig->setProperty("filterScale", 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
|
||||
// Start by computing threshold of color buffer input at quarter resolution
|
||||
const auto bloomInputBuffer = task.addJob<BloomThreshold>("BloomThreshold", inputs, 4U);
|
||||
|
||||
// Multi-scale blur, each new blur is half resolution of the previous pass
|
||||
const auto blurFB0 = task.addJob<render::BlurGaussian>("BloomBlur0", bloomInputBuffer, true);
|
||||
const auto blurFB1 = task.addJob<render::BlurGaussian>("BloomBlur1", blurFB0, true, 2U);
|
||||
const auto blurFB2 = task.addJob<render::BlurGaussian>("BloomBlur2", blurFB1, true, 2U);
|
||||
|
||||
const auto& input = inputs.get<Inputs>();
|
||||
const auto& frameBuffer = input[1];
|
||||
|
||||
// Mix all blur levels at quarter resolution
|
||||
const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying();
|
||||
task.addJob<BloomApply>("BloomApply", applyInput);
|
||||
// And them blend result in additive manner on top of final color buffer
|
||||
const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying();
|
||||
task.addJob<BloomDraw>("BloomDraw", drawInput);
|
||||
|
||||
const auto debugInput = DebugBloom::Inputs(frameBuffer, blurFB0, blurFB1, blurFB2, bloomInputBuffer).asVarying();
|
||||
task.addJob<DebugBloom>("DebugBloom", debugInput);
|
||||
}
|
166
libraries/render-utils/src/BloomEffect.h
Normal file
166
libraries/render-utils/src/BloomEffect.h
Normal file
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// BloomEffect.h
|
||||
// render-utils/src/
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_utils_BloomEffect_h
|
||||
#define hifi_render_utils_BloomEffect_h
|
||||
|
||||
#include <render/Engine.h>
|
||||
|
||||
#include "DeferredFrameTransform.h"
|
||||
|
||||
class BloomConfig : public render::Task::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty)
|
||||
Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
BloomConfig() : render::Task::Config(false) {}
|
||||
|
||||
float size{ 0.8f };
|
||||
|
||||
void setIntensity(float value);
|
||||
float getIntensity() const;
|
||||
void setSize(float value);
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThresholdConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float threshold{ 1.25f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomThreshold {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Outputs = gpu::FramebufferPointer;
|
||||
using Config = BloomThresholdConfig;
|
||||
using JobModel = render::Job::ModelIO<BloomThreshold, Inputs, Outputs, Config>;
|
||||
|
||||
BloomThreshold(unsigned int downsamplingFactor);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::FramebufferPointer _outputBuffer;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
float _threshold;
|
||||
unsigned int _downsamplingFactor;
|
||||
};
|
||||
|
||||
|
||||
class BloomApplyConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
float intensity{ 0.8f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class BloomApply {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<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 BloomDraw {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using JobModel = render::Job::ModelI<BloomDraw, Inputs>;
|
||||
|
||||
BloomDraw() {}
|
||||
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _pipeline;
|
||||
};
|
||||
|
||||
class DebugBloomConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int mode MEMBER mode NOTIFY dirty)
|
||||
|
||||
public:
|
||||
|
||||
enum Mode {
|
||||
MODE_LEVEL0 = 0,
|
||||
MODE_LEVEL1,
|
||||
MODE_LEVEL2,
|
||||
MODE_ALL_LEVELS,
|
||||
|
||||
MODE_COUNT
|
||||
};
|
||||
|
||||
DebugBloomConfig() : render::Job::Config(false) {}
|
||||
|
||||
int mode{ MODE_ALL_LEVELS };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class DebugBloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet5<gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Config = DebugBloomConfig;
|
||||
using JobModel = render::Job::ModelI<DebugBloom, Inputs, Config>;
|
||||
|
||||
DebugBloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
gpu::PipelinePointer _pipeline;
|
||||
DebugBloomConfig::Mode _mode;
|
||||
};
|
||||
|
||||
class Bloom {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
|
||||
using Config = BloomConfig;
|
||||
using JobModel = render::Task::ModelI<Bloom, Inputs, Config>;
|
||||
|
||||
Bloom();
|
||||
|
||||
void configure(const Config& config);
|
||||
void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_render_utils_BloomEffect_h
|
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
45
libraries/render-utils/src/BloomThreshold.slf
Normal file
|
@ -0,0 +1,45 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// BloomThreshold.slf
|
||||
// Perform a soft threshold on an input texture and downsample to half size in one go.
|
||||
//
|
||||
// Created by Olivier Prat on 09/26/2017
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
uniform float threshold;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
out vec4 outFragColor;
|
||||
|
||||
#define DOWNSAMPLING_FACTOR 4
|
||||
#define SAMPLE_COUNT (DOWNSAMPLING_FACTOR/2)
|
||||
|
||||
void main(void) {
|
||||
vec2 deltaX = dFdx(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 deltaY = dFdy(varTexCoord0) / SAMPLE_COUNT;
|
||||
vec2 startUv = varTexCoord0;
|
||||
vec4 maskedColor = vec4(0,0,0,0);
|
||||
|
||||
for (int y=0 ; y<SAMPLE_COUNT ; y++) {
|
||||
vec2 uv = startUv;
|
||||
|
||||
for (int x=0 ; x<SAMPLE_COUNT ; x++) {
|
||||
vec4 color = texture(colorMap, uv);
|
||||
float luminance = (color.r+color.g+color.b) / 3.0;
|
||||
float mask = clamp((luminance-threshold)*0.25, 0, 1);
|
||||
|
||||
color *= mask;
|
||||
maskedColor += color;
|
||||
uv += deltaX;
|
||||
}
|
||||
|
||||
startUv += deltaY;
|
||||
}
|
||||
maskedColor /= SAMPLE_COUNT*SAMPLE_COUNT;
|
||||
outFragColor = vec4(maskedColor.rgb, 1.0);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "ToneMappingEffect.h"
|
||||
#include "SubsurfaceScattering.h"
|
||||
#include "DrawHaze.h"
|
||||
#include "BloomEffect.h"
|
||||
#include "HighlightEffect.h"
|
||||
|
||||
#include <sstream>
|
||||
|
@ -166,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying();
|
||||
task.addJob<DrawDeferred>("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>("DebugLightClusters", debugLightClustersInputs);
|
||||
|
@ -177,6 +178,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
|
||||
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
|
||||
|
||||
// Add bloom
|
||||
const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying();
|
||||
task.addJob<Bloom>("Bloom", bloomInputs);
|
||||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||
|
|
|
@ -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<gpu::Buffer>(sizeof(Params), (const gpu::Byte*) ¶ms));
|
||||
setFilterGaussianTaps(3);
|
||||
}
|
||||
|
||||
void BlurParams::setWidthHeight(int width, int height, bool isStereo) {
|
||||
|
@ -49,10 +48,10 @@ void BlurParams::setWidthHeight(int width, int height, bool isStereo) {
|
|||
}
|
||||
}
|
||||
|
||||
void BlurParams::setTexcoordTransform(const glm::vec4 texcoordTransformViewport) {
|
||||
auto texcoordTransform = _parametersBuffer.get<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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,58 @@ void BlurParams::setFilterRadiusScale(float scale) {
|
|||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (scale != filterInfo.x) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.x = scale;
|
||||
_parametersBuffer.edit<Params>().filterInfo.y = scale / BLUR_NUM_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
void BlurParams::setFilterNumTaps(int count) {
|
||||
assert(count <= BLUR_MAX_NUM_TAPS);
|
||||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (count != (int)filterInfo.y) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.y = count;
|
||||
}
|
||||
}
|
||||
|
||||
void BlurParams::setFilterTap(int index, float offset, float value) {
|
||||
auto filterTaps = _parametersBuffer.edit<Params>().filterTaps;
|
||||
assert(index < BLUR_MAX_NUM_TAPS);
|
||||
filterTaps[index].x = offset;
|
||||
filterTaps[index].y = value;
|
||||
}
|
||||
|
||||
void BlurParams::setFilterGaussianTaps(int numHalfTaps, float sigma) {
|
||||
auto& params = _parametersBuffer.edit<Params>();
|
||||
const int numTaps = 2 * numHalfTaps + 1;
|
||||
assert(numTaps <= BLUR_MAX_NUM_TAPS);
|
||||
assert(sigma > 0.0f);
|
||||
const float inverseTwoSigmaSquared = float(0.5 / double(sigma*sigma));
|
||||
float totalWeight = 1.0f;
|
||||
float weight;
|
||||
float offset;
|
||||
int i;
|
||||
|
||||
params.filterInfo.y = numTaps;
|
||||
params.filterTaps[0].x = 0.0f;
|
||||
params.filterTaps[0].y = 1.0f;
|
||||
|
||||
for (i = 0; i < numHalfTaps; i++) {
|
||||
offset = i + 1;
|
||||
weight = (float)exp(-offset*offset * inverseTwoSigmaSquared);
|
||||
params.filterTaps[i + 1].x = offset;
|
||||
params.filterTaps[i + 1].y = weight;
|
||||
params.filterTaps[i + 1 + numHalfTaps].x = -offset;
|
||||
params.filterTaps[i + 1 + numHalfTaps].y = weight;
|
||||
totalWeight += 2 * weight;
|
||||
}
|
||||
|
||||
// Tap weights will be normalized in shader because side cases on edges of screen
|
||||
// won't have the same number of taps as in the center.
|
||||
}
|
||||
|
||||
void BlurParams::setOutputAlpha(float value) {
|
||||
value = glm::clamp(value, 0.0f, 1.0f);
|
||||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||
if (value != filterInfo.z) {
|
||||
_parametersBuffer.edit<Params>().filterInfo.z = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,17 +136,23 @@ void BlurParams::setLinearDepthPosFar(float farPosDepth) {
|
|||
}
|
||||
|
||||
|
||||
BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer) :
|
||||
_generateOutputFramebuffer(generateOutputFramebuffer)
|
||||
{
|
||||
|
||||
BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||
_downsampleFactor(downsampleFactor),
|
||||
_generateOutputFramebuffer(generateOutputFramebuffer) {
|
||||
assert(downsampleFactor > 0);
|
||||
}
|
||||
|
||||
bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFramebuffer, Resources& blurringResources) {
|
||||
if (!sourceFramebuffer) {
|
||||
return false;
|
||||
}
|
||||
if (_blurredFramebuffer && _blurredFramebuffer->getSize() != sourceFramebuffer->getSize()) {
|
||||
|
||||
auto blurBufferSize = sourceFramebuffer->getSize();
|
||||
|
||||
blurBufferSize.x /= _downsampleFactor;
|
||||
blurBufferSize.y /= _downsampleFactor;
|
||||
|
||||
if (_blurredFramebuffer && _blurredFramebuffer->getSize() != blurBufferSize) {
|
||||
_blurredFramebuffer.reset();
|
||||
}
|
||||
|
||||
|
@ -108,7 +164,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
// _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||
//}
|
||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
_blurredFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||
}
|
||||
|
||||
|
@ -117,7 +173,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0);
|
||||
|
||||
if (_generateOutputFramebuffer) {
|
||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != sourceFramebuffer->getSize()) {
|
||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != blurBufferSize) {
|
||||
_outputFramebuffer.reset();
|
||||
}
|
||||
|
||||
|
@ -131,7 +187,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
_outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||
}*/
|
||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto blurringTarget = gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
auto blurringTarget = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), blurBufferSize.x, blurBufferSize.y, gpu::Texture::SINGLE_MIP, blurringSampler);
|
||||
_outputFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||
}
|
||||
|
||||
|
@ -145,8 +201,8 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
|||
return true;
|
||||
}
|
||||
|
||||
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer) :
|
||||
_inOutResources(generateOutputFramebuffer)
|
||||
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||
_inOutResources(generateOutputFramebuffer, downsampleFactor)
|
||||
{
|
||||
_parameters = std::make_shared<BlurParams>();
|
||||
}
|
||||
|
@ -196,7 +252,16 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() {
|
|||
}
|
||||
|
||||
void BlurGaussian::configure(const Config& config) {
|
||||
auto state = getBlurHPipeline()->getState();
|
||||
|
||||
_parameters->setFilterRadiusScale(config.filterScale);
|
||||
_parameters->setOutputAlpha(config.mix);
|
||||
if (config.mix < 1.0f) {
|
||||
state->setBlendFunction(config.mix < 1.0f, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
} else {
|
||||
state->setBlendFunction(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,7 +271,6 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
|
||||
BlurInOutResource::Resources blurringResources;
|
||||
if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) {
|
||||
// early exit if no valid blurring resources
|
||||
|
@ -216,14 +280,15 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
auto blurVPipeline = getBlurVPipeline();
|
||||
auto blurHPipeline = getBlurHPipeline();
|
||||
glm::ivec4 viewport { 0, 0, blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight() };
|
||||
|
||||
_parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->isStereo());
|
||||
glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions());
|
||||
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport));
|
||||
glm::ivec2 textureSize = blurredFramebuffer->getSize();
|
||||
_parameters->setWidthHeight(blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight(), args->isStereo());
|
||||
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, viewport));
|
||||
|
||||
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setViewportTransform(viewport);
|
||||
|
||||
batch.setUniformBuffer(BlurTask_ParamsSlot, _parameters->_parametersBuffer);
|
||||
|
||||
|
@ -251,7 +316,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
|||
|
||||
|
||||
BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, const BlurParamsPointer& params) :
|
||||
_inOutResources(generateOutputFramebuffer),
|
||||
_inOutResources(generateOutputFramebuffer, 1U),
|
||||
_parameters((params ? params : std::make_shared<BlurParams>()))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "Engine.h"
|
||||
|
||||
#include "BlurTask_shared.slh"
|
||||
|
||||
namespace render {
|
||||
|
||||
|
||||
|
@ -25,6 +27,11 @@ public:
|
|||
void setTexcoordTransform(const glm::vec4 texcoordTransformViewport);
|
||||
|
||||
void setFilterRadiusScale(float scale);
|
||||
void setFilterNumTaps(int count);
|
||||
// Tap 0 is considered the center of the kernel
|
||||
void setFilterTap(int index, float offset, float value);
|
||||
void setFilterGaussianTaps(int numHalfTaps, float sigma = 1.47f);
|
||||
void setOutputAlpha(float value);
|
||||
|
||||
void setDepthPerspective(float oneOverTan2FOV);
|
||||
void setDepthThreshold(float threshold);
|
||||
|
@ -40,7 +47,7 @@ public:
|
|||
// Viewport to Texcoord info, if the region of the blur (viewport) is smaller than the full frame
|
||||
glm::vec4 texcoordTransform{ 0.0f, 0.0f, 1.0f, 1.0f };
|
||||
|
||||
// Filter info (radius scale
|
||||
// Filter info (radius scale, number of taps, output alpha)
|
||||
glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
// Depth info (radius scale
|
||||
|
@ -52,6 +59,9 @@ public:
|
|||
// LinearDepth info is { f }
|
||||
glm::vec4 linearDepthInfo{ 0.0f };
|
||||
|
||||
// Taps (offset, weight)
|
||||
glm::vec2 filterTaps[BLUR_MAX_NUM_TAPS];
|
||||
|
||||
Params() {}
|
||||
};
|
||||
gpu::BufferView _parametersBuffer;
|
||||
|
@ -62,7 +72,7 @@ using BlurParamsPointer = std::shared_ptr<BlurParams>;
|
|||
|
||||
class BlurInOutResource {
|
||||
public:
|
||||
BlurInOutResource(bool generateOutputFramebuffer = false);
|
||||
BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor);
|
||||
|
||||
struct Resources {
|
||||
gpu::TexturePointer sourceTexture;
|
||||
|
@ -75,8 +85,9 @@ public:
|
|||
|
||||
gpu::FramebufferPointer _blurredFramebuffer;
|
||||
|
||||
// the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in th einput buffer
|
||||
// the output framebuffer defined if the job needs to output the result in a new framebuffer and not in place in the input buffer
|
||||
gpu::FramebufferPointer _outputFramebuffer;
|
||||
unsigned int _downsampleFactor{ 1U };
|
||||
bool _generateOutputFramebuffer{ false };
|
||||
};
|
||||
|
||||
|
@ -84,12 +95,15 @@ public:
|
|||
class BlurGaussianConfig : public Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled WRITE setEnabled READ isEnabled NOTIFY dirty) // expose enabled flag
|
||||
Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty) // expose enabled flag
|
||||
Q_PROPERTY(float filterScale MEMBER filterScale NOTIFY dirty)
|
||||
Q_PROPERTY(float mix MEMBER mix NOTIFY dirty)
|
||||
public:
|
||||
|
||||
BlurGaussianConfig() : Job::Config(true) {}
|
||||
|
||||
float filterScale{ 0.2f };
|
||||
float mix{ 1.0f };
|
||||
|
||||
signals :
|
||||
void dirty();
|
||||
|
||||
|
@ -102,11 +116,13 @@ public:
|
|||
using Config = BlurGaussianConfig;
|
||||
using JobModel = Job::ModelIO<BlurGaussian, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
|
||||
|
||||
BlurGaussian(bool generateOutputFramebuffer = false);
|
||||
BlurGaussian(bool generateOutputFramebuffer = false, unsigned int downsampleFactor = 1U);
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer);
|
||||
|
||||
BlurParamsPointer getParameters() const { return _parameters; }
|
||||
|
||||
protected:
|
||||
|
||||
BlurParamsPointer _parameters;
|
||||
|
|
|
@ -9,17 +9,7 @@
|
|||
|
||||
<@func declareBlurUniforms()@>
|
||||
|
||||
#define NUM_TAPS 7
|
||||
#define NUM_TAPS_OFFSET 3.0f
|
||||
|
||||
float uniformFilterWidth = 0.05f;
|
||||
|
||||
const float gaussianDistributionCurve[NUM_TAPS] = float[](
|
||||
0.383f, 0.006f, 0.061f, 0.242f, 0.242f, 0.061f, 0.006f
|
||||
);
|
||||
const float gaussianDistributionOffset[NUM_TAPS] = float[](
|
||||
0.0f, -3.0f, -2.0f, -1.0f, 1.0f, 2.0f, 3.0f
|
||||
);
|
||||
<@include BlurTask_shared.slh@>
|
||||
|
||||
struct BlurParameters {
|
||||
vec4 resolutionInfo;
|
||||
|
@ -28,6 +18,7 @@ struct BlurParameters {
|
|||
vec4 depthInfo;
|
||||
vec4 stereoInfo;
|
||||
vec4 linearDepthInfo;
|
||||
vec2 taps[BLUR_MAX_NUM_TAPS];
|
||||
};
|
||||
|
||||
uniform blurParamsBuffer {
|
||||
|
@ -46,6 +37,25 @@ float getFilterScale() {
|
|||
return parameters.filterInfo.x;
|
||||
}
|
||||
|
||||
int getFilterNumTaps() {
|
||||
return int(parameters.filterInfo.y);
|
||||
}
|
||||
|
||||
float getOutputAlpha() {
|
||||
return parameters.filterInfo.z;
|
||||
}
|
||||
|
||||
vec2 getFilterTap(int index) {
|
||||
return parameters.taps[index];
|
||||
}
|
||||
|
||||
float getFilterTapOffset(vec2 tap) {
|
||||
return tap.x;
|
||||
}
|
||||
|
||||
float getFilterTapWeight(vec2 tap) {
|
||||
return tap.y;
|
||||
}
|
||||
|
||||
float getDepthThreshold() {
|
||||
return parameters.depthInfo.x;
|
||||
|
@ -70,19 +80,29 @@ uniform sampler2D sourceMap;
|
|||
|
||||
vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||
texcoord = evalTexcoordTransformed(texcoord);
|
||||
vec4 sampleCenter = texture(sourceMap, texcoord);
|
||||
|
||||
vec2 finalStep = getFilterScale() * direction * pixelStep;
|
||||
vec4 srcBlurred = vec4(0.0);
|
||||
float totalWeight = 0.f;
|
||||
int numTaps = getFilterNumTaps();
|
||||
|
||||
for(int i = 0; i < NUM_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
||||
for(int i = 0; i < numTaps; i++) {
|
||||
vec2 tapInfo = getFilterTap(i);
|
||||
// Fetch color for current sample.
|
||||
vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep);
|
||||
if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) {
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
float weight = getFilterTapWeight(tapInfo);
|
||||
// Accumulate.
|
||||
srcBlurred += srcSample * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalWeight>0.0) {
|
||||
srcBlurred /= totalWeight;
|
||||
}
|
||||
srcBlurred.a = getOutputAlpha();
|
||||
return srcBlurred;
|
||||
}
|
||||
|
||||
|
@ -95,15 +115,6 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
|||
uniform sampler2D sourceMap;
|
||||
uniform sampler2D depthMap;
|
||||
|
||||
#define NUM_HALF_TAPS 4
|
||||
|
||||
const float gaussianDistributionCurveHalf[NUM_HALF_TAPS] = float[](
|
||||
0.383f, 0.242f, 0.061f, 0.006f
|
||||
);
|
||||
const float gaussianDistributionOffsetHalf[NUM_HALF_TAPS] = float[](
|
||||
0.0f, 1.0f, 2.0f, 3.0f
|
||||
);
|
||||
|
||||
vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||
texcoord = evalTexcoordTransformed(texcoord);
|
||||
float sampleDepth = texture(depthMap, texcoord).x;
|
||||
|
@ -122,45 +133,36 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep
|
|||
float scale = distanceToProjectionWindow / sampleDepth;
|
||||
|
||||
vec2 finalStep = filterScale * scale * direction * pixelStep;
|
||||
int numTaps = getFilterNumTaps();
|
||||
|
||||
// Accumulate the center sample
|
||||
vec4 srcBlurred = gaussianDistributionCurve[0] * sampleCenter;
|
||||
vec2 tapInfo = getFilterTap(0);
|
||||
float totalWeight = getFilterTapWeight(tapInfo);
|
||||
vec4 srcBlurred = sampleCenter * totalWeight;
|
||||
|
||||
for(int i = 1; i < numTaps; i++) {
|
||||
tapInfo = getFilterTap(i);
|
||||
|
||||
for(int i = 1; i < NUM_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
||||
float srcDepth = texture(depthMap, sampleCoord).x;
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
vec2 sampleCoord = texcoord + (getFilterTapOffset(tapInfo) * finalStep);
|
||||
if (all(greaterThanEqual(sampleCoord, vec2(0,0))) && all(lessThanEqual(sampleCoord, vec2(1.0,1.0)))) {
|
||||
float srcDepth = texture(depthMap, sampleCoord).x;
|
||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||
float weight = getFilterTapWeight(tapInfo);
|
||||
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
||||
srcSample = mix(srcSample, sampleCenter, s);
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
||||
srcSample = mix(srcSample, sampleCenter, s);
|
||||
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
||||
// Accumulate.
|
||||
srcBlurred += srcSample * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
for(int i = 1; i < NUM_HALF_TAPS; i++) {
|
||||
// Fetch color and depth for current sample.
|
||||
vec2 texcoordOffset = (gaussianDistributionOffsetHalf[i] * finalStep);
|
||||
|
||||
float srcDepthN = texture(depthMap, texcoord - texcoordOffset).x;
|
||||
float srcDepthP = texture(depthMap, texcoord + texcoordOffset).x;
|
||||
vec4 srcSampleN = texture(sourceMap, texcoord - texcoordOffset);
|
||||
vec4 srcSampleP = texture(sourceMap, texcoord + texcoordOffset);
|
||||
|
||||
// If the difference in depth is huge, we lerp color back.
|
||||
float sN = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthN - sampleDepth), 0.0, 1.0);
|
||||
float sP = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepthP - sampleDepth), 0.0, 1.0);
|
||||
|
||||
srcSampleN = mix(srcSampleN, sampleCenter, sN);
|
||||
srcSampleP = mix(srcSampleP, sampleCenter, sP);
|
||||
|
||||
// Accumulate.
|
||||
srcBlurred += gaussianDistributionCurveHalf[i] * (srcSampleP + srcSampleN);
|
||||
}*/
|
||||
|
||||
if (totalWeight>0.0) {
|
||||
srcBlurred /= totalWeight;
|
||||
}
|
||||
return srcBlurred;
|
||||
}
|
||||
|
||||
|
|
10
libraries/render/src/render/BlurTask_shared.slh
Normal file
10
libraries/render/src/render/BlurTask_shared.slh
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Created by Olivier Prat on 09/25/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#define BLUR_MAX_NUM_TAPS 33
|
83
libraries/render/src/render/ResampleTask.cpp
Normal file
83
libraries/render/src/render/ResampleTask.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ResampleTask.cpp
|
||||
// render/src/render
|
||||
//
|
||||
// Various to upsample or downsample textures into framebuffers.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "ResampleTask.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
#include "gpu/StandardShaderLib.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
gpu::PipelinePointer HalfDownsample::_pipeline;
|
||||
|
||||
HalfDownsample::HalfDownsample() {
|
||||
|
||||
}
|
||||
|
||||
void HalfDownsample::configure(const Config& config) {
|
||||
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer HalfDownsample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) {
|
||||
auto resampledFramebufferSize = sourceFramebuffer->getSize();
|
||||
|
||||
resampledFramebufferSize.x /= 2U;
|
||||
resampledFramebufferSize.y /= 2U;
|
||||
|
||||
if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) {
|
||||
_destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("HalfOutput"));
|
||||
|
||||
auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
||||
auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler);
|
||||
_destinationFrameBuffer->setRenderBuffer(0, target);
|
||||
}
|
||||
return _destinationFrameBuffer;
|
||||
}
|
||||
|
||||
void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
resampledFrameBuffer = getResampledFrameBuffer(sourceFramebuffer);
|
||||
|
||||
if (!_pipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
|
||||
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
state->setDepthTest(gpu::State::DepthTest(false, false));
|
||||
_pipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
const auto bufferSize = resampledFrameBuffer->getSize();
|
||||
glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y };
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setFramebuffer(resampledFrameBuffer);
|
||||
|
||||
batch.setViewportTransform(viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(bufferSize, viewport));
|
||||
batch.setResourceTexture(0, sourceFramebuffer->getRenderBuffer(0));
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
});
|
||||
}
|
41
libraries/render/src/render/ResampleTask.h
Normal file
41
libraries/render/src/render/ResampleTask.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ResampleTask.h
|
||||
// render/src/render
|
||||
//
|
||||
// Various to upsample or downsample textures into framebuffers.
|
||||
//
|
||||
// Created by Olivier Prat on 10/09/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_render_ResampleTask_h
|
||||
#define hifi_render_ResampleTask_h
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
class HalfDownsample {
|
||||
public:
|
||||
using Config = JobConfig;
|
||||
using JobModel = Job::ModelIO<HalfDownsample, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
|
||||
|
||||
HalfDownsample();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& resampledFrameBuffer);
|
||||
|
||||
protected:
|
||||
|
||||
static gpu::PipelinePointer _pipeline;
|
||||
|
||||
gpu::FramebufferPointer _destinationFrameBuffer;
|
||||
|
||||
gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_render_ResampleTask_h
|
|
@ -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) {}
|
||||
|
||||
|
|
119
scripts/developer/utilities/render/bloom.qml
Normal file
119
scripts/developer/utilities/render/bloom.qml
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// bloom.qml
|
||||
// developer/utilities/render
|
||||
//
|
||||
// Olivier Prat, created on 09/25/2017.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import "configSlider"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var config: Render.getConfig("RenderMainView.Bloom")
|
||||
property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold")
|
||||
property var configDebug: Render.getConfig("RenderMainView.DebugBloom")
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
|
||||
CheckBox {
|
||||
text: "Enable"
|
||||
checked: root.config["enabled"]
|
||||
onCheckedChanged: {
|
||||
root.config["enabled"] = checked;
|
||||
}
|
||||
}
|
||||
GroupBox {
|
||||
title: "Debug"
|
||||
Row {
|
||||
ExclusiveGroup { id: debugGroup }
|
||||
RadioButton {
|
||||
text : "Off"
|
||||
checked : !root.configDebug["enabled"]
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = false
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 0"
|
||||
checked :root.configDebug["enabled"] && root.configDebug["mode"]==0
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 0
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 1"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==1
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 1
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "Lvl 2"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==2
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 2
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
RadioButton {
|
||||
text : "All"
|
||||
checked : root.configDebug["enabled"] && root.configDebug["mode"]==3
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
root.configDebug["enabled"] = true
|
||||
root.configDebug["mode"] = 3
|
||||
}
|
||||
}
|
||||
exclusiveGroup : debugGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Intensity"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "intensity"
|
||||
max: 5.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Size"
|
||||
integral: false
|
||||
config: root.config
|
||||
property: "size"
|
||||
max: 1.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
ConfigSlider {
|
||||
label: "Threshold"
|
||||
integral: false
|
||||
config: root.configThreshold
|
||||
property: "threshold"
|
||||
max: 2.0
|
||||
min: 0.0
|
||||
width: 280
|
||||
}
|
||||
}
|
||||
}
|
20
scripts/developer/utilities/render/debugBloom.js
Normal file
20
scripts/developer/utilities/render/debugBloom.js
Normal file
|
@ -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(); });
|
Loading…
Reference in a new issue