First alpha working version of outline

This commit is contained in:
Olivier Prat 2017-08-09 18:03:52 +02:00
parent f36500a74c
commit 01a028cbb6
9 changed files with 260 additions and 11 deletions

View file

@ -0,0 +1,63 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
//
// Add outline effect based on two zbuffers : one containing the total scene z and another
// with the z of only the objects to be outlined
//
// Created by Olivier Prat on 08/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
//
<@include Outline_shared.slh@>
uniform outlineParamsBuffer {
OutlineParameters params;
};
uniform sampler2D sceneDepthMap;
uniform sampler2D outlinedDepthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
const int BLUR_KERNEL_SIZE = 9;
const float FAR_DISTANCE = 1.0;
void main(void) {
float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x;
if (outlinedDepth < FAR_DISTANCE) {
// We're not on the far plane so we are on the outlined object, thus no outline to do!
discard;
}
//float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
float outlineIntensity = 0.0;
{
const float deltaUv = params._size / BLUR_KERNEL_SIZE;
vec2 uv;
vec2 startUv = varTexCoord0 - vec2(params._size, params._size) / 2.0;
int x;
int y;
for (y=0 ; y<BLUR_KERNEL_SIZE ; y++) {
uv = startUv;
startUv.y += deltaUv;
for (x=0 ; x<BLUR_KERNEL_SIZE ; x++) {
outlinedDepth = texture(outlinedDepthMap, uv).x;
outlineIntensity += (outlinedDepth < FAR_DISTANCE) ? 1.0 : 0.0;
uv.x += deltaUv;
}
}
outlineIntensity /= BLUR_KERNEL_SIZE*BLUR_KERNEL_SIZE;
}
if (outlineIntensity < 1e-3) {
discard;
}
outFragColor = vec4(params._color.rgb, outlineIntensity);
}

View file

@ -18,6 +18,7 @@
#include "surfaceGeometry_copyDepth_frag.h"
#include "debug_deferred_buffer_vert.h"
#include "debug_deferred_buffer_frag.h"
#include "Outline_frag.h"
OutlineFramebuffer::OutlineFramebuffer() {
}
@ -99,6 +100,8 @@ void PrepareOutline::run(const render::RenderContextPointer& renderContext, cons
_copyDepthPipeline = gpu::Pipeline::create(program, state);
}
// TODO : Instead of copying entire buffer, we should only copy the sub rect containing the outlined object
// grown to take into account blur width
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
@ -122,6 +125,69 @@ void PrepareOutline::run(const render::RenderContextPointer& renderContext, cons
}
}
DrawOutline::DrawOutline() {
}
void DrawOutline::configure(const Config& config) {
_color = config.color;
_size = config.width;
}
void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
auto mainFrameBuffer = inputs.get0();
if (mainFrameBuffer) {
auto sceneDepthBuffer = mainFrameBuffer->getPrimaryDepthTexture();
auto outlinedDepthBuffer = inputs.get1();
auto pipeline = getPipeline();
if (outlinedDepthBuffer) {
auto framebufferSize = glm::ivec2(sceneDepthBuffer->getDimensions());
auto args = renderContext->args;
{
auto& configuration = _configuration.edit();
configuration._color = _color;
configuration._size = _size / 1024.f;
}
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setViewportTransform(args->_viewport);
batch.setProjectionTransform(glm::mat4());
batch.resetViewTransform();
batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport));
batch.setPipeline(pipeline);
batch.setUniformBuffer(0, _configuration);
batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer);
batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthBuffer->getDepthTexture());
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
}
}
const gpu::PipelinePointer& DrawOutline::getPipeline() {
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding("outlineParamsBuffer", 0));
slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT));
slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_pipeline = gpu::Pipeline::create(program, state);
}
return _pipeline;
}
DebugOutline::DebugOutline() {
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
}

View file

@ -73,6 +73,60 @@ private:
gpu::PipelinePointer _copyDepthPipeline;
};
class DrawOutlineConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float width MEMBER width NOTIFY dirty)
Q_PROPERTY(float colorR READ getColorR WRITE setColorR NOTIFY dirty)
Q_PROPERTY(float colorG READ getColorG WRITE setColorG NOTIFY dirty)
Q_PROPERTY(float colorB READ getColorB WRITE setColorB NOTIFY dirty)
public:
void setColorR(float value) { color.r = value; }
float getColorR() const { return color.r; }
void setColorG(float value) { color.g = value; }
float getColorG() const { return color.g; }
void setColorB(float value) { color.b = value; }
float getColorB() const { return color.b; }
float width{ 5.f };
glm::vec3 color{ 1.f, 0.7f, 0.2f };
signals:
void dirty();
};
class DrawOutline {
public:
using Inputs = render::VaryingSet2<DeferredFramebufferPointer, OutlineFramebufferPointer>;
using Config = DrawOutlineConfig;
using JobModel = render::Job::ModelI<DrawOutline, Inputs, Config>;
DrawOutline();
void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
private:
enum {
SCENE_DEPTH_SLOT = 0,
OUTLINED_DEPTH_SLOT
};
#include "Outline_shared.slh"
using OutlineConfigurationBuffer = gpu::StructBuffer<OutlineParameters>;
const gpu::PipelinePointer& getPipeline();
gpu::PipelinePointer _pipeline;
OutlineConfigurationBuffer _configuration;
glm::vec3 _color;
float _size;
};
class DebugOutlineConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty)
@ -85,7 +139,6 @@ signals:
void dirty();
};
class DebugOutline {
public:
using Inputs = OutlineFramebufferPointer;

View file

@ -0,0 +1,17 @@
// glsl / C++ compatible source as interface for Outline
#ifdef __cplusplus
# define VEC3 glm::vec3
#else
# define VEC3 vec3
#endif
struct OutlineParameters
{
VEC3 _color;
float _size;
};
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !>
//

View file

@ -14,17 +14,19 @@ PickItemsJob::PickItemsJob(render::ItemKey::Flags validKeys, render::ItemKey::Fl
}
void PickItemsJob::configure(const Config& config) {
_isEnabled = config.pick;
}
void PickItemsJob::run(const render::RenderContextPointer& renderContext, const PickItemsJob::Input& input, PickItemsJob::Output& output) {
output.clear();
if (_isEnabled) {
float minIsectDistance = std::numeric_limits<float>::max();
auto& itemBounds = input;
auto item = findNearestItem(renderContext, itemBounds, minIsectDistance);
float minIsectDistance = std::numeric_limits<float>::max();
auto& itemBounds = input;
auto item = findNearestItem(renderContext, itemBounds, minIsectDistance);
if (render::Item::isValidID(item.id)) {
output.push_back(item);
if (render::Item::isValidID(item.id)) {
output.push_back(item);
}
}
}

View file

@ -16,10 +16,15 @@
#include <render/Item.h>
class PickItemsConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(bool pick MEMBER pick NOTIFY dirty)
public:
PickItemsConfig() : render::Job::Config(false) {}
bool pick{ false };
signals:
void dirty();
};
class PickItemsJob {
@ -40,6 +45,7 @@ private:
render::ItemKey::Flags _validKeys;
render::ItemKey::Flags _excludeKeys;
bool _isEnabled{ false };
render::ItemBound findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const;
};

View file

@ -169,6 +169,10 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
task.addJob<ToneMappingDeferred>("ToneMapping", toneMappingInputs);
// Draw outline
const auto outlineInputs = DrawOutline::Inputs(deferredFramebuffer, outlinedFrameBuffer).hasVarying();
task.addJob<DrawOutline>("DrawOutline", outlineInputs);
{ // DEbug the bounds of the rendered items, still look at the zbuffer
task.addJob<DrawBounds>("DrawMetaBounds", metas);
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);

View file

@ -15,6 +15,6 @@ var window = new OverlayWindow({
title: 'Outline',
source: qml,
width: 250,
height: 80,
height: 250,
});
window.closed.connect(function() { Script.stop(); });

View file

@ -10,20 +10,22 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "configSlider"
Item {
id: root
property var pickConfig: Render.getConfig("RenderMainView.PickOutlined")
property var debugConfig: Render.getConfig("RenderMainView.DebugOutline")
property var drawConfig: Render.getConfig("RenderMainView.DrawOutline")
Column {
spacing: 8
CheckBox {
text: "Edit Outline"
checked: root.pickConfig["enabled"]
checked: root.pickConfig["pick"]
onCheckedChanged: {
root.pickConfig["enabled"] = checked;
root.pickConfig["pick"] = checked;
}
}
CheckBox {
@ -33,5 +35,41 @@ Item {
root.debugConfig["viewOutlinedDepth"] = checked;
}
}
ConfigSlider {
label: "Width"
integral: false
config: root.drawConfig
property: "width"
max: 15.0
min: 0.0
width: 230
}
ConfigSlider {
label: "Color R"
integral: false
config: root.drawConfig
property: "colorR"
max: 1.0
min: 0.0
width: 230
}
ConfigSlider {
label: "Color G"
integral: false
config: root.drawConfig
property: "colorG"
max: 1.0
min: 0.0
width: 230
}
ConfigSlider {
label: "Color B"
integral: false
config: root.drawConfig
property: "colorB"
max: 1.0
min: 0.0
width: 230
}
}
}