Modified outline shader to do fill branch on CPU. Fixed assymetric outline

due to depth sample not offset by a half texel in shader.
This commit is contained in:
Olivier Prat 2017-09-07 12:14:32 +02:00
parent b9eb317d9b
commit b43514fb8b
5 changed files with 162 additions and 121 deletions

View file

@ -1,8 +1,7 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
//
// Outline.slf
// 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
// with the z of only the objects to be outlined.
// This is the version without the fill effect inside the silhouette.
//
// Created by Olivier Prat on 08/09/2017
// Copyright 2017 High Fidelity, Inc.
@ -10,81 +9,5 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredTransform.slh@>
<$declareDeferredFrameTransform()$>
<@include Outline_shared.slh@>
uniform outlineParamsBuffer {
OutlineParameters params;
};
uniform sampler2D sceneDepthMap;
uniform sampler2D outlinedDepthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
const float FAR_Z = 1.0;
const float LINEAR_DEPTH_BIAS = 5e-3;
const float OPACITY_EPSILON = 5e-3;
void main(void) {
float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x;
float intensity = 0.0;
if (outlinedDepth < FAR_Z) {
// We're not on the far plane so we are on the outlined object, thus no outline to do!
// But maybe we need to fill the interior
// TODO: this should be done as another shader with switch done on CPU
if (params._fillOpacityUnoccluded>OPACITY_EPSILON && params._fillOpacityUnoccluded>OPACITY_EPSILON) {
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
// Transform to linear depth for better precision
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
sceneDepth = -evalZeyeFromZdb(sceneDepth);
// Are we occluded?
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
intensity = params._fillOpacityOccluded;
} else {
intensity = params._fillOpacityUnoccluded;
}
} else {
discard;
}
} else {
float weight = 0.0;
vec2 deltaUv = params._size / params._blurKernelSize;
vec2 lineStartUv = varTexCoord0 - params._size / 2.0;
vec2 uv;
int x;
int y;
for (y=0 ; y<params._blurKernelSize ; y++) {
uv = lineStartUv;
lineStartUv.y += deltaUv.y;
if (uv.y>=0.0 && uv.y<=1.0) {
for (x=0 ; x<params._blurKernelSize ; x++) {
if (uv.x>=0.0 && uv.x<=1.0)
{
outlinedDepth = texture(outlinedDepthMap, uv).x;
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
weight += 1.f;
}
uv.x += deltaUv.x;
}
}
}
intensity /= weight;
if (intensity < OPACITY_EPSILON) {
discard;
}
intensity = min(1.0, intensity / params._threshold) * params._intensity;
}
outFragColor = vec4(params._color.rgb, intensity);
}
<@include Outline.slh@>
<$main(0)$>

View file

@ -0,0 +1,95 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
<!
// Outline.slh
// fragment shader
//
// Created by Olivier Prat on 9/7/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 DeferredTransform.slh@>
<$declareDeferredFrameTransform()$>
<@include Outline_shared.slh@>
uniform outlineParamsBuffer {
OutlineParameters params;
};
uniform sampler2D sceneDepthMap;
uniform sampler2D outlinedDepthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
const float FAR_Z = 1.0;
const float LINEAR_DEPTH_BIAS = 5e-3;
const float OPACITY_EPSILON = 5e-3;
<@func main(IS_FILLED)@>
void main(void) {
float outlinedDepth = texture(outlinedDepthMap, varTexCoord0).x;
float intensity = 0.0;
if (outlinedDepth < FAR_Z) {
// We're not on the far plane so we are on the outlined object, thus no outline to do!
<@if IS_FILLED@>
// But we need to fill the interior
float sceneDepth = texture(sceneDepthMap, varTexCoord0).x;
// Transform to linear depth for better precision
outlinedDepth = -evalZeyeFromZdb(outlinedDepth);
sceneDepth = -evalZeyeFromZdb(sceneDepth);
// Are we occluded?
if (sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS)) {
intensity = params._fillOpacityOccluded;
} else {
intensity = params._fillOpacityUnoccluded;
}
<@else@>
discard;
<@endif@>
} else {
float weight = 0.0;
vec2 deltaUv = params._size / params._blurKernelSize;
// We offset by half a texel to be centered on the depth sample. If we don't do this
// the blur will have a different width between the left / right sides and top / bottom
// sides of the silhouette
vec2 lineStartUv = varTexCoord0 + (getInvWidthHeight() - params._size) / 2.0;
vec2 uv;
int x;
int y;
for (y=0 ; y<params._blurKernelSize ; y++) {
uv = lineStartUv;
lineStartUv.y += deltaUv.y;
if (uv.y>=0.0 && uv.y<=1.0) {
for (x=0 ; x<params._blurKernelSize ; x++) {
if (uv.x>=0.0 && uv.x<=1.0)
{
outlinedDepth = texture(outlinedDepthMap, uv).x;
intensity += (outlinedDepth < FAR_Z) ? 1.0 : 0.0;
weight += 1.f;
}
uv.x += deltaUv.x;
}
}
}
intensity /= weight;
if (intensity < OPACITY_EPSILON) {
discard;
}
intensity = min(1.0, intensity / params._threshold) * params._intensity;
}
outFragColor = vec4(params._color.rgb, intensity);
}
<@endfunc@>

View file

@ -22,6 +22,7 @@
#include "debug_deferred_buffer_vert.h"
#include "debug_deferred_buffer_frag.h"
#include "Outline_frag.h"
#include "Outline_filled_frag.h"
using namespace render;
@ -153,7 +154,6 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
const auto frameTransform = inputs.get0();
auto outlinedDepthBuffer = mainFrameBuffer->getPrimaryDepthTexture();
auto destinationFrameBuffer = inputs.get3();
auto pipeline = getPipeline();
auto framebufferSize = glm::ivec2(outlinedDepthBuffer->getDimensions());
if (!_primaryWithoutDepthBuffer || framebufferSize!=_frameBufferSize) {
@ -164,6 +164,8 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
}
if (sceneDepthBuffer) {
const auto OPACITY_EPSILON = 5e-3f;
auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON);
auto args = renderContext->args;
{
auto& configuration = _configuration.edit();
@ -200,7 +202,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I
}
}
const gpu::PipelinePointer& DrawOutline::getPipeline() {
const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) {
if (!_pipeline) {
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(Outline_frag));
@ -217,8 +219,13 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() {
state->setDepthTest(gpu::State::DepthTest(false, false));
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_pipeline = gpu::Pipeline::create(program, state);
ps = gpu::Shader::createPixel(std::string(Outline_filled_frag));
program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program, slotBindings);
_pipelineFilled = gpu::Pipeline::create(program, state);
}
return _pipeline;
return isFilled ? _pipelineFilled : _pipeline;
}
DebugOutline::DebugOutline() {
@ -310,51 +317,53 @@ void DrawOutlineDepth::run(const render::RenderContextPointer& renderContext, co
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
RenderArgs* args = renderContext->args;
ShapeKey::Builder defaultKeyBuilder;
if (!inShapes.empty()) {
RenderArgs* args = renderContext->args;
ShapeKey::Builder defaultKeyBuilder;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
// Setup camera, projection and viewport for all items
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
// Setup camera, projection and viewport for all items
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder);
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned());
std::vector<ShapeKey> skinnedShapeKeys{};
std::vector<ShapeKey> skinnedShapeKeys{};
// Iterate through all inShapes and render the unskinned
args->_shapePipeline = shadowPipeline;
batch.setPipeline(shadowPipeline->pipeline);
for (auto items : inShapes) {
if (items.first.isSkinned()) {
skinnedShapeKeys.push_back(items.first);
// Iterate through all inShapes and render the unskinned
args->_shapePipeline = shadowPipeline;
batch.setPipeline(shadowPipeline->pipeline);
for (auto items : inShapes) {
if (items.first.isSkinned()) {
skinnedShapeKeys.push_back(items.first);
}
else {
renderItems(renderContext, items.second);
}
}
else {
renderItems(renderContext, items.second);
// Reiterate to render the skinned
args->_shapePipeline = shadowSkinnedPipeline;
batch.setPipeline(shadowSkinnedPipeline->pipeline);
for (const auto& key : skinnedShapeKeys) {
renderItems(renderContext, inShapes.at(key));
}
}
// Reiterate to render the skinned
args->_shapePipeline = shadowSkinnedPipeline;
batch.setPipeline(shadowSkinnedPipeline->pipeline);
for (const auto& key : skinnedShapeKeys) {
renderItems(renderContext, inShapes.at(key));
}
args->_shapePipeline = nullptr;
args->_batch = nullptr;
});
args->_shapePipeline = nullptr;
args->_batch = nullptr;
});
}
}
DrawOutlineTask::DrawOutlineTask() {

View file

@ -117,11 +117,12 @@ private:
using OutlineConfigurationBuffer = gpu::StructBuffer<OutlineParameters>;
const gpu::PipelinePointer& getPipeline();
const gpu::PipelinePointer& getPipeline(bool isFilled);
gpu::FramebufferPointer _primaryWithoutDepthBuffer;
glm::ivec2 _frameBufferSize {0, 0};
gpu::PipelinePointer _pipeline;
gpu::PipelinePointer _pipelineFilled;
OutlineConfigurationBuffer _configuration;
glm::vec3 _color;
float _size;

View file

@ -0,0 +1,13 @@
// Outline_filled.slf
// 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.
// This is the version with the fill effect inside the silhouette.
//
// Created by Olivier Prat on 09/07/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.slh@>
<$main(1)$>