mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 19:02:55 +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);
|
_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 = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting"));
|
||||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "ToneMappingEffect.h"
|
#include "ToneMappingEffect.h"
|
||||||
#include "SubsurfaceScattering.h"
|
#include "SubsurfaceScattering.h"
|
||||||
#include "DrawHaze.h"
|
#include "DrawHaze.h"
|
||||||
|
#include "BloomEffect.h"
|
||||||
#include "HighlightEffect.h"
|
#include "HighlightEffect.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -166,7 +167,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
||||||
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying();
|
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).asVarying();
|
||||||
task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
|
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();
|
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).asVarying();
|
||||||
task.addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
|
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");
|
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
|
// Lighting Buffer ready for tone mapping
|
||||||
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer).asVarying();
|
||||||
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
|
||||||
|
|
|
@ -29,11 +29,10 @@ enum BlurShaderMapSlots {
|
||||||
BlurTask_DepthSlot,
|
BlurTask_DepthSlot,
|
||||||
};
|
};
|
||||||
|
|
||||||
const float BLUR_NUM_SAMPLES = 7.0f;
|
|
||||||
|
|
||||||
BlurParams::BlurParams() {
|
BlurParams::BlurParams() {
|
||||||
Params params;
|
Params params;
|
||||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Params), (const gpu::Byte*) ¶ms));
|
_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) {
|
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) {
|
void BlurParams::setTexcoordTransform(glm::vec4 texcoordTransformViewport) {
|
||||||
auto texcoordTransform = _parametersBuffer.get<Params>().texcoordTransform;
|
auto& params = _parametersBuffer.get<Params>();
|
||||||
if (texcoordTransformViewport != texcoordTransform) {
|
if (texcoordTransformViewport != params.texcoordTransform) {
|
||||||
_parametersBuffer.edit<Params>().texcoordTransform = texcoordTransform;
|
_parametersBuffer.edit<Params>().texcoordTransform = texcoordTransformViewport;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +59,58 @@ void BlurParams::setFilterRadiusScale(float scale) {
|
||||||
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
auto filterInfo = _parametersBuffer.get<Params>().filterInfo;
|
||||||
if (scale != filterInfo.x) {
|
if (scale != filterInfo.x) {
|
||||||
_parametersBuffer.edit<Params>().filterInfo.x = scale;
|
_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) :
|
BlurInOutResource::BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||||
_generateOutputFramebuffer(generateOutputFramebuffer)
|
_downsampleFactor(downsampleFactor),
|
||||||
{
|
_generateOutputFramebuffer(generateOutputFramebuffer) {
|
||||||
|
assert(downsampleFactor > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFramebuffer, Resources& blurringResources) {
|
bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFramebuffer, Resources& blurringResources) {
|
||||||
if (!sourceFramebuffer) {
|
if (!sourceFramebuffer) {
|
||||||
return false;
|
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();
|
_blurredFramebuffer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +164,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
||||||
// _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
// _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||||
//}
|
//}
|
||||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
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);
|
_blurredFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +173,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
||||||
blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0);
|
blurringResources.blurringTexture = _blurredFramebuffer->getRenderBuffer(0);
|
||||||
|
|
||||||
if (_generateOutputFramebuffer) {
|
if (_generateOutputFramebuffer) {
|
||||||
if (_outputFramebuffer && _outputFramebuffer->getSize() != sourceFramebuffer->getSize()) {
|
if (_outputFramebuffer && _outputFramebuffer->getSize() != blurBufferSize) {
|
||||||
_outputFramebuffer.reset();
|
_outputFramebuffer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +187,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
||||||
_outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
_outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat());
|
||||||
}*/
|
}*/
|
||||||
auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT);
|
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);
|
_outputFramebuffer->setRenderBuffer(0, blurringTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +201,8 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer) :
|
BlurGaussian::BlurGaussian(bool generateOutputFramebuffer, unsigned int downsampleFactor) :
|
||||||
_inOutResources(generateOutputFramebuffer)
|
_inOutResources(generateOutputFramebuffer, downsampleFactor)
|
||||||
{
|
{
|
||||||
_parameters = std::make_shared<BlurParams>();
|
_parameters = std::make_shared<BlurParams>();
|
||||||
}
|
}
|
||||||
|
@ -196,7 +252,16 @@ gpu::PipelinePointer BlurGaussian::getBlurHPipeline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlurGaussian::configure(const Config& config) {
|
void BlurGaussian::configure(const Config& config) {
|
||||||
|
auto state = getBlurHPipeline()->getState();
|
||||||
|
|
||||||
_parameters->setFilterRadiusScale(config.filterScale);
|
_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;
|
RenderArgs* args = renderContext->args;
|
||||||
|
|
||||||
|
|
||||||
BlurInOutResource::Resources blurringResources;
|
BlurInOutResource::Resources blurringResources;
|
||||||
if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) {
|
if (!_inOutResources.updateResources(sourceFramebuffer, blurringResources)) {
|
||||||
// early exit if no valid blurring resources
|
// early exit if no valid blurring resources
|
||||||
|
@ -216,14 +280,15 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra
|
||||||
|
|
||||||
auto blurVPipeline = getBlurVPipeline();
|
auto blurVPipeline = getBlurVPipeline();
|
||||||
auto blurHPipeline = getBlurHPipeline();
|
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 = blurredFramebuffer->getSize();
|
||||||
glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions());
|
_parameters->setWidthHeight(blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight(), args->isStereo());
|
||||||
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport));
|
_parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, viewport));
|
||||||
|
|
||||||
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||||
batch.enableStereo(false);
|
batch.enableStereo(false);
|
||||||
batch.setViewportTransform(args->_viewport);
|
batch.setViewportTransform(viewport);
|
||||||
|
|
||||||
batch.setUniformBuffer(BlurTask_ParamsSlot, _parameters->_parametersBuffer);
|
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) :
|
BlurGaussianDepthAware::BlurGaussianDepthAware(bool generateOutputFramebuffer, const BlurParamsPointer& params) :
|
||||||
_inOutResources(generateOutputFramebuffer),
|
_inOutResources(generateOutputFramebuffer, 1U),
|
||||||
_parameters((params ? params : std::make_shared<BlurParams>()))
|
_parameters((params ? params : std::make_shared<BlurParams>()))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
|
|
||||||
|
#include "BlurTask_shared.slh"
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +27,11 @@ public:
|
||||||
void setTexcoordTransform(const glm::vec4 texcoordTransformViewport);
|
void setTexcoordTransform(const glm::vec4 texcoordTransformViewport);
|
||||||
|
|
||||||
void setFilterRadiusScale(float scale);
|
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 setDepthPerspective(float oneOverTan2FOV);
|
||||||
void setDepthThreshold(float threshold);
|
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
|
// 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 };
|
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 };
|
glm::vec4 filterInfo{ 1.0f, 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
// Depth info (radius scale
|
// Depth info (radius scale
|
||||||
|
@ -52,6 +59,9 @@ public:
|
||||||
// LinearDepth info is { f }
|
// LinearDepth info is { f }
|
||||||
glm::vec4 linearDepthInfo{ 0.0f };
|
glm::vec4 linearDepthInfo{ 0.0f };
|
||||||
|
|
||||||
|
// Taps (offset, weight)
|
||||||
|
glm::vec2 filterTaps[BLUR_MAX_NUM_TAPS];
|
||||||
|
|
||||||
Params() {}
|
Params() {}
|
||||||
};
|
};
|
||||||
gpu::BufferView _parametersBuffer;
|
gpu::BufferView _parametersBuffer;
|
||||||
|
@ -62,7 +72,7 @@ using BlurParamsPointer = std::shared_ptr<BlurParams>;
|
||||||
|
|
||||||
class BlurInOutResource {
|
class BlurInOutResource {
|
||||||
public:
|
public:
|
||||||
BlurInOutResource(bool generateOutputFramebuffer = false);
|
BlurInOutResource(bool generateOutputFramebuffer, unsigned int downsampleFactor);
|
||||||
|
|
||||||
struct Resources {
|
struct Resources {
|
||||||
gpu::TexturePointer sourceTexture;
|
gpu::TexturePointer sourceTexture;
|
||||||
|
@ -75,8 +85,9 @@ public:
|
||||||
|
|
||||||
gpu::FramebufferPointer _blurredFramebuffer;
|
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;
|
gpu::FramebufferPointer _outputFramebuffer;
|
||||||
|
unsigned int _downsampleFactor{ 1U };
|
||||||
bool _generateOutputFramebuffer{ false };
|
bool _generateOutputFramebuffer{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -84,12 +95,15 @@ public:
|
||||||
class BlurGaussianConfig : public Job::Config {
|
class BlurGaussianConfig : public Job::Config {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool enabled WRITE setEnabled READ isEnabled NOTIFY dirty) // expose enabled flag
|
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:
|
public:
|
||||||
|
|
||||||
BlurGaussianConfig() : Job::Config(true) {}
|
BlurGaussianConfig() : Job::Config(true) {}
|
||||||
|
|
||||||
float filterScale{ 0.2f };
|
float filterScale{ 0.2f };
|
||||||
|
float mix{ 1.0f };
|
||||||
|
|
||||||
signals :
|
signals :
|
||||||
void dirty();
|
void dirty();
|
||||||
|
|
||||||
|
@ -102,11 +116,13 @@ public:
|
||||||
using Config = BlurGaussianConfig;
|
using Config = BlurGaussianConfig;
|
||||||
using JobModel = Job::ModelIO<BlurGaussian, gpu::FramebufferPointer, gpu::FramebufferPointer, Config>;
|
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 configure(const Config& config);
|
||||||
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer);
|
void run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceFramebuffer, gpu::FramebufferPointer& blurredFramebuffer);
|
||||||
|
|
||||||
|
BlurParamsPointer getParameters() const { return _parameters; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
BlurParamsPointer _parameters;
|
BlurParamsPointer _parameters;
|
||||||
|
|
|
@ -9,17 +9,7 @@
|
||||||
|
|
||||||
<@func declareBlurUniforms()@>
|
<@func declareBlurUniforms()@>
|
||||||
|
|
||||||
#define NUM_TAPS 7
|
<@include BlurTask_shared.slh@>
|
||||||
#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
|
|
||||||
);
|
|
||||||
|
|
||||||
struct BlurParameters {
|
struct BlurParameters {
|
||||||
vec4 resolutionInfo;
|
vec4 resolutionInfo;
|
||||||
|
@ -28,6 +18,7 @@ struct BlurParameters {
|
||||||
vec4 depthInfo;
|
vec4 depthInfo;
|
||||||
vec4 stereoInfo;
|
vec4 stereoInfo;
|
||||||
vec4 linearDepthInfo;
|
vec4 linearDepthInfo;
|
||||||
|
vec2 taps[BLUR_MAX_NUM_TAPS];
|
||||||
};
|
};
|
||||||
|
|
||||||
uniform blurParamsBuffer {
|
uniform blurParamsBuffer {
|
||||||
|
@ -46,6 +37,25 @@ float getFilterScale() {
|
||||||
return parameters.filterInfo.x;
|
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() {
|
float getDepthThreshold() {
|
||||||
return parameters.depthInfo.x;
|
return parameters.depthInfo.x;
|
||||||
|
@ -70,19 +80,29 @@ uniform sampler2D sourceMap;
|
||||||
|
|
||||||
vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||||
texcoord = evalTexcoordTransformed(texcoord);
|
texcoord = evalTexcoordTransformed(texcoord);
|
||||||
vec4 sampleCenter = texture(sourceMap, texcoord);
|
|
||||||
|
|
||||||
vec2 finalStep = getFilterScale() * direction * pixelStep;
|
vec2 finalStep = getFilterScale() * direction * pixelStep;
|
||||||
vec4 srcBlurred = vec4(0.0);
|
vec4 srcBlurred = vec4(0.0);
|
||||||
|
float totalWeight = 0.f;
|
||||||
|
int numTaps = getFilterNumTaps();
|
||||||
|
|
||||||
for(int i = 0; i < NUM_TAPS; i++) {
|
for(int i = 0; i < numTaps; i++) {
|
||||||
// Fetch color and depth for current sample.
|
vec2 tapInfo = getFilterTap(i);
|
||||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
// 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);
|
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||||
|
float weight = getFilterTapWeight(tapInfo);
|
||||||
// Accumulate.
|
// Accumulate.
|
||||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
srcBlurred += srcSample * weight;
|
||||||
|
totalWeight += weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (totalWeight>0.0) {
|
||||||
|
srcBlurred /= totalWeight;
|
||||||
|
}
|
||||||
|
srcBlurred.a = getOutputAlpha();
|
||||||
return srcBlurred;
|
return srcBlurred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,15 +115,6 @@ vec4 pixelShaderGaussian(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||||
uniform sampler2D sourceMap;
|
uniform sampler2D sourceMap;
|
||||||
uniform sampler2D depthMap;
|
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) {
|
vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep) {
|
||||||
texcoord = evalTexcoordTransformed(texcoord);
|
texcoord = evalTexcoordTransformed(texcoord);
|
||||||
float sampleDepth = texture(depthMap, texcoord).x;
|
float sampleDepth = texture(depthMap, texcoord).x;
|
||||||
|
@ -122,45 +133,36 @@ vec4 pixelShaderGaussianDepthAware(vec2 texcoord, vec2 direction, vec2 pixelStep
|
||||||
float scale = distanceToProjectionWindow / sampleDepth;
|
float scale = distanceToProjectionWindow / sampleDepth;
|
||||||
|
|
||||||
vec2 finalStep = filterScale * scale * direction * pixelStep;
|
vec2 finalStep = filterScale * scale * direction * pixelStep;
|
||||||
|
int numTaps = getFilterNumTaps();
|
||||||
|
|
||||||
// Accumulate the center sample
|
// 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.
|
// Fetch color and depth for current sample.
|
||||||
vec2 sampleCoord = texcoord + (gaussianDistributionOffset[i] * finalStep);
|
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;
|
float srcDepth = texture(depthMap, sampleCoord).x;
|
||||||
vec4 srcSample = texture(sourceMap, sampleCoord);
|
vec4 srcSample = texture(sourceMap, sampleCoord);
|
||||||
|
float weight = getFilterTapWeight(tapInfo);
|
||||||
|
|
||||||
// If the difference in depth is huge, we lerp color back.
|
// If the difference in depth is huge, we lerp color back.
|
||||||
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
float s = clamp(depthThreshold * distanceToProjectionWindow * filterScale * abs(srcDepth - sampleDepth), 0.0, 1.0);
|
||||||
srcSample = mix(srcSample, sampleCenter, s);
|
srcSample = mix(srcSample, sampleCenter, s);
|
||||||
|
|
||||||
// Accumulate.
|
// Accumulate.
|
||||||
srcBlurred += gaussianDistributionCurve[i] * srcSample;
|
srcBlurred += srcSample * weight;
|
||||||
|
totalWeight += weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (totalWeight>0.0) {
|
||||||
for(int i = 1; i < NUM_HALF_TAPS; i++) {
|
srcBlurred /= totalWeight;
|
||||||
// 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;
|
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);
|
_concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& getName() const { return _name; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConceptPointer _concept;
|
ConceptPointer _concept;
|
||||||
std::string _name = "";
|
std::string _name = "";
|
||||||
|
@ -206,6 +208,24 @@ public:
|
||||||
|
|
||||||
const Varying getInput() const override { return _input; }
|
const Varying getInput() const override { return _input; }
|
||||||
const Varying getOutput() const override { return _output; }
|
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) {}
|
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