Multiple gaussians as Unreal. First draft

This commit is contained in:
Olivier Prat 2017-10-09 09:35:38 +02:00
parent 27b9f3516d
commit 27a0fb62d3
6 changed files with 332 additions and 17 deletions

View file

@ -10,18 +10,145 @@
//
#include "BloomEffect.h"
#include "gpu/Context.h"
#include "gpu/StandardShaderLib.h"
#include <render/BlurTask.h>
#include "BloomThreshold_frag.h"
ThresholdAndDownsampleJob::ThresholdAndDownsampleJob() {
}
void ThresholdAndDownsampleJob::configure(const Config& config) {
_threshold = config.threshold;
}
void ThresholdAndDownsampleJob::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
const auto frameTransform = inputs.get0();
const auto inputFrameBuffer = inputs.get1();
assert(inputFrameBuffer->hasColor());
auto inputColor = inputFrameBuffer->getRenderBuffer(0);
auto sourceViewport = args->_viewport;
auto fullSize = glm::ivec2(inputColor->getDimensions());
auto halfSize = fullSize / 2;
auto halfViewport = args->_viewport >> 1;
if (!_downsampledBuffer || _downsampledBuffer->getSize().x != halfSize.x || _downsampledBuffer->getSize().y != halfSize.y) {
auto colorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(inputColor->getTexelFormat(), halfSize.x, halfSize.y,
gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR)));
_downsampledBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("BloomBlur0"));
_downsampledBuffer->setRenderBuffer(0, colorTexture);
}
static const int COLOR_MAP_SLOT = 0;
static const int THRESHOLD_SLOT = 1;
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(BloomThreshold_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding("colorMap", COLOR_MAP_SLOT));
slotBindings.insert(gpu::Shader::Binding("threshold", THRESHOLD_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
_pipeline = gpu::Pipeline::create(program, state);
}
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
/* batch.setViewportTransform(halfViewport);
batch.setProjectionTransform(glm::mat4());
batch.resetViewTransform();
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(inputFrameBuffer->getSize(), args->_viewport));
batch.setPipeline(_pipeline);*/
batch.setFramebuffer(_downsampledBuffer);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, gpu::Vec4(1.0f, 0.2f, 0.9f, 1.f));
/*batch.setResourceTexture(COLOR_MAP_SLOT, inputColor);
batch._glUniform1f(THRESHOLD_SLOT, _threshold);
batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.setViewportTransform(args->_viewport);
batch.setResourceTexture(COLOR_MAP_SLOT, nullptr);
batch.setFramebuffer(nullptr);*/
});
outputs = _downsampledBuffer;
}
DebugBloom::DebugBloom() {
}
DebugBloom::~DebugBloom() {
}
void DebugBloom::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
const auto frameBuffer = inputs.get0();
const auto level0FB = inputs.get1();
const gpu::TexturePointer levelTextures[1] = {
level0FB->getRenderBuffer(0)
};
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::StandardShaderLib::getDrawTextureOpaquePS();
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(gpu::State::DepthTest(false));
_pipeline = gpu::Pipeline::create(program, state);
}
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
//batch.setFramebuffer(frameBuffer);
batch.setViewportTransform(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat, false);
batch.setPipeline(_pipeline);
batch.setResourceTexture(0, levelTextures[0]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
batch.setResourceTexture(0, nullptr);
});
}
void BloomConfig::setIntensity(float value) {
auto blurConfig = getConfig<render::BlurGaussian>();
blurConfig->setProperty("mix", value*0.5f);
/* auto blurConfig = getConfig<render::BlurGaussian>();
blurConfig->setProperty("mix", value*0.5f);*/
}
void BloomConfig::setSize(float value) {
auto& blurJob = static_cast<render::Task::TaskConcept*>(_task)->_jobs.front();
/* auto& blurJob = static_cast<render::Task::TaskConcept*>(_task)->_jobs.front();
auto& gaussianBlur = blurJob.edit<render::BlurGaussian>();
auto gaussianBlurParams = gaussianBlur.getParameters();
gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f);
gaussianBlurParams->setFilterGaussianTaps((BLUR_MAX_NUM_TAPS - 1) / 2, value*7.0f);*/
}
Bloom::Bloom() {
@ -29,11 +156,13 @@ Bloom::Bloom() {
}
void Bloom::configure(const Config& config) {
auto blurConfig = config.getConfig<render::BlurGaussian>();
blurConfig->setProperty("filterScale", 3.0f);
/* auto blurConfig = config.getConfig<render::BlurGaussian>();
blurConfig->setProperty("filterScale", 3.0f);*/
}
void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
const auto& blurInput = inputs;
task.addJob<render::BlurGaussian>("Blur", blurInput);
const auto halfSizeBuffer = task.addJob<ThresholdAndDownsampleJob>("BloomThreshold", inputs);
const auto debugInput = DebugBloom::Inputs(inputs.get<Bloom::Inputs>().get1(), halfSizeBuffer).asVarying();
task.addJob<DebugBloom>("DebugBloom", debugInput);
}

View file

@ -14,27 +14,87 @@
#include <render/Engine.h>
#include "DeferredFrameTransform.h"
class BloomConfig : public render::Task::Config {
Q_OBJECT
Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty)
Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
Q_OBJECT
Q_PROPERTY(float intensity MEMBER intensity WRITE setIntensity NOTIFY dirty)
Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty)
public:
float intensity{ 0.2f };
float size{ 0.4f };
BloomConfig() : render::Task::Config(true) {}
float intensity{ 0.2f };
float size{ 0.4f };
void setIntensity(float value);
void setSize(float value);
signals:
void dirty();
void dirty();
};
class ThresholdConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty)
public:
float threshold{ 0.25f };
signals:
void dirty();
};
class ThresholdAndDownsampleJob {
public:
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
using Outputs = gpu::FramebufferPointer;
using Config = ThresholdConfig;
using JobModel = render::Job::ModelIO<ThresholdAndDownsampleJob, Inputs, Outputs, Config>;
ThresholdAndDownsampleJob();
void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
private:
gpu::FramebufferPointer _downsampledBuffer;
gpu::PipelinePointer _pipeline;
float _threshold;
};
class DebugBloomConfig : public render::Job::Config {
Q_OBJECT
public:
DebugBloomConfig() : render::Job::Config(true) {}
};
class DebugBloom {
public:
using Inputs = render::VaryingSet2<gpu::FramebufferPointer, gpu::FramebufferPointer>;
using Config = DebugBloomConfig;
using JobModel = render::Job::ModelI<DebugBloom, Inputs, Config>;
DebugBloom();
~DebugBloom();
void configure(const Config& config) {}
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
private:
gpu::PipelinePointer _pipeline;
};
class Bloom {
public:
using Inputs = gpu::FramebufferPointer;
using Config = BloomConfig;
using Inputs = render::VaryingSet2<DeferredFrameTransformPointer, gpu::FramebufferPointer>;
using Config = BloomConfig;
using JobModel = render::Task::ModelI<Bloom, Inputs, Config>;
Bloom();

View file

@ -0,0 +1,40 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// BloomThreshold.slf
// Perform a soft threshold on an input texture and downsample to half size in one go.
//
// Created by Olivier Prat on 09/26/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
uniform sampler2D colorMap;
uniform float threshold;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
// Gather 2 by 2 quads from texture, threshold and downsample
vec4 reds = textureGather(colorMap, varTexCoord0, 0);
vec4 greens = textureGather(colorMap, varTexCoord0, 1);
vec4 blues = textureGather(colorMap, varTexCoord0, 2);
float hardness = 8;
vec4 rMask = clamp((reds-threshold) * hardness, 0, 1);
vec4 gMask = clamp((greens-threshold) * hardness, 0, 1);
vec4 bMask = clamp((blues-threshold) * hardness, 0, 1);
reds = smoothstep(vec4(0,0,0,0), reds, rMask);
greens = smoothstep(vec4(0,0,0,0), greens, gMask);
blues = smoothstep(vec4(0,0,0,0), blues, bMask);
vec3 texel0 = vec3(reds.x, greens.x, blues.x);
vec3 texel1 = vec3(reds.y, greens.y, blues.y);
vec3 texel2 = vec3(reds.z, greens.z, blues.z);
vec3 texel3 = vec3(reds.w, greens.w, blues.w);
outFragColor = vec4((texel0+texel1+texel2+texel3)/4.0, 1.0);
}

View file

@ -170,7 +170,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto toneAndPostRangeTimer = task.addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
// Add bloom
const auto bloomInputs = lightingFramebuffer;
const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying();
task.addJob<Bloom>("Bloom", bloomInputs);
// Lighting Buffer ready for tone mapping

View file

@ -0,0 +1,66 @@
//
// bloom.qml
// developer/utilities/render
//
// Olivier Prat, created on 09/25/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "configSlider"
Item {
id: root
property var config: Render.getConfig("RenderMainView.Bloom")
property var configThreshold: Render.getConfig("RenderMainView.BloomThreshold")
property var configDebug: Render.getConfig("RenderMainView.DebugBloom")
Column {
spacing: 8
CheckBox {
text: "Enable"
checked: root.config["enabled"]
onCheckedChanged: {
root.config["enabled"] = checked;
}
}
CheckBox {
text: "Debug"
checked: root.configDebug["enabled"]
onCheckedChanged: {
root.configDebug["enabled"] = checked;
}
}
ConfigSlider {
label: "Intensity"
integral: false
config: root.config
property: "intensity"
max: 1.0
min: 0.0
width: 280
}
ConfigSlider {
label: "Size"
integral: false
config: root.config
property: "size"
max: 1.0
min: 0.0
width: 280
}
ConfigSlider {
label: "Threshold"
integral: false
config: root.configThreshold
property: "threshold"
max: 1.0
min: 0.0
width: 280
}
}
}

View 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(); });