From b43514fb8bfd2cc90c617f615adaf88fd7066dc2 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 7 Sep 2017 12:14:32 +0200 Subject: [PATCH] Modified outline shader to do fill branch on CPU. Fixed assymetric outline due to depth sample not offset by a half texel in shader. --- libraries/render-utils/src/Outline.slf | 87 +---------------- libraries/render-utils/src/Outline.slh | 95 +++++++++++++++++++ libraries/render-utils/src/OutlineEffect.cpp | 85 +++++++++-------- libraries/render-utils/src/OutlineEffect.h | 3 +- libraries/render-utils/src/Outline_filled.slf | 13 +++ 5 files changed, 162 insertions(+), 121 deletions(-) create mode 100644 libraries/render-utils/src/Outline.slh create mode 100644 libraries/render-utils/src/Outline_filled.slf diff --git a/libraries/render-utils/src/Outline.slf b/libraries/render-utils/src/Outline.slf index 1d3d21a3c7..68ef870cba 100644 --- a/libraries/render-utils/src/Outline.slf +++ b/libraries/render-utils/src/Outline.slf @@ -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=0.0 && uv.y<=1.0) { - for (x=0 ; 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)$> diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh new file mode 100644 index 0000000000..6e510581b4 --- /dev/null +++ b/libraries/render-utils/src/Outline.slh @@ -0,0 +1,95 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> + +<@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=0.0 && uv.y<=1.0) { + for (x=0 ; 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@> diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index adbfed2775..289405b127 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -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 skinnedShapeKeys{}; + std::vector 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() { diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 4cbfb5851c..7e7cf83d19 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -117,11 +117,12 @@ private: using OutlineConfigurationBuffer = gpu::StructBuffer; - 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; diff --git a/libraries/render-utils/src/Outline_filled.slf b/libraries/render-utils/src/Outline_filled.slf new file mode 100644 index 0000000000..aaa3396bac --- /dev/null +++ b/libraries/render-utils/src/Outline_filled.slf @@ -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)$>