diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp new file mode 100644 index 0000000000..45bf9c7d81 --- /dev/null +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -0,0 +1,207 @@ +// +// OutlineEffect.cpp +// render-utils/src/ +// +// Created by Olivier Prat on 08/08/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 "OutlineEffect.h" + +#include "GeometryCache.h" + +#include "gpu/Context.h" +#include "gpu/StandardShaderLib.h" + +#include "surfaceGeometry_copyDepth_frag.h" +#include "debug_deferred_buffer_vert.h" +#include "debug_deferred_buffer_frag.h" + +OutlineFramebuffer::OutlineFramebuffer() { +} + +void OutlineFramebuffer::update(const gpu::TexturePointer& linearDepthBuffer) { + //If the depth buffer or size changed, we need to delete our FBOs + bool reset = false; + + if (_depthTexture) { + auto newFrameSize = glm::ivec2(linearDepthBuffer->getDimensions()); + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + reset = true; + } + } + + if (reset) { + clear(); + } +} + +void OutlineFramebuffer::clear() { + _depthFramebuffer.reset(); + _depthTexture.reset(); +} + +void OutlineFramebuffer::allocate() { + + auto width = _frameSize.x; + auto height = _frameSize.y; + auto colorFormat = gpu::Element::VEC4F_COLOR_RGBA; // TODO : find a more compact format + + _depthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth", colorFormat, width, height)); + _depthTexture = _depthFramebuffer->getRenderBuffer(0); +} + +gpu::FramebufferPointer OutlineFramebuffer::getDepthFramebuffer() { + if (!_depthFramebuffer) { + allocate(); + } + return _depthFramebuffer; +} + +gpu::TexturePointer OutlineFramebuffer::getDepthTexture() { + if (!_depthTexture) { + allocate(); + } + return _depthTexture; +} + +void PrepareOutline::run(const render::RenderContextPointer& renderContext, const PrepareOutline::Input& input, PrepareOutline::Output& output) { + auto outlinedItems = input.get1(); + + if (!outlinedItems.empty()) { + glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); + auto args = renderContext->args; + auto deferredFrameBuffer = input.get0(); + + if (!_outlineFramebuffer) { + _outlineFramebuffer = std::make_shared(); + } + _outlineFramebuffer->update(deferredFrameBuffer->getPrimaryDepthTexture()); + + if (!_copyDepthPipeline) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_copyDepth_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setColorWriteMask(true, false, false, false); + + // Good to go add the brand new pipeline + _copyDepthPipeline = gpu::Pipeline::create(program, state); + } + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + auto depthBuffer = deferredFrameBuffer->getPrimaryDepthTexture(); + + // Copy depth to texture as we will overwrite + batch.enableStereo(false); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(frameSize, args->_viewport)); + + batch.setFramebuffer(_outlineFramebuffer->getDepthFramebuffer()); + batch.setPipeline(_copyDepthPipeline); + batch.setResourceTexture(0, depthBuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + // Restore previous frame buffer + batch.setFramebuffer(deferredFrameBuffer->getDeferredFramebuffer()); + }); + + output = _outlineFramebuffer; + } else { + output = nullptr; + } +} + +DebugOutline::DebugOutline() { + _geometryId = DependencyManager::get()->allocateID(); +} + +DebugOutline::~DebugOutline() { + auto geometryCache = DependencyManager::get(); + if (geometryCache) { + geometryCache->releaseID(_geometryId); + } +} + +void DebugOutline::configure(const Config& config) { + _isDisplayDepthEnabled = config.viewOutlinedDepth; +} + +void DebugOutline::run(const render::RenderContextPointer& renderContext, const Input& input) { + if (_isDisplayDepthEnabled && input) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); + + const auto geometryBuffer = DependencyManager::get(); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, true); + batch.setModelTransform(Transform()); + + batch.setPipeline(getDebugPipeline()); + batch.setResourceTexture(0, input->getDepthTexture()); + const glm::vec4 color(1.0f, 0.5f, 0.2f, 1.0f); + const glm::vec2 bottomLeft(-1.0f, -1.0f); + const glm::vec2 topRight(1.0f, 1.0f); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId); + + batch.setResourceTexture(0, nullptr); + }); + } +} + +const gpu::PipelinePointer& DebugOutline::getDebugPipeline() { + if (!_debugPipeline) { + static const std::string VERTEX_SHADER{ debug_deferred_buffer_vert }; + static const std::string FRAGMENT_SHADER{ debug_deferred_buffer_frag }; + static const std::string SOURCE_PLACEHOLDER{ "//SOURCE_PLACEHOLDER" }; + static const auto SOURCE_PLACEHOLDER_INDEX = FRAGMENT_SHADER.find(SOURCE_PLACEHOLDER); + Q_ASSERT_X(SOURCE_PLACEHOLDER_INDEX != std::string::npos, Q_FUNC_INFO, + "Could not find source placeholder"); + static const std::string DEFAULT_DEPTH_SHADER{ + "vec4 getFragmentColor() {" + " float depth = texture(depthMap, uv).x;" + " return vec4(vec3(depth), 1.0);" + " }" + }; + + auto bakedFragmentShader = FRAGMENT_SHADER; + bakedFragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), DEFAULT_DEPTH_SHADER); + + static const auto vs = gpu::Shader::createVertex(VERTEX_SHADER); + const auto ps = gpu::Shader::createPixel(bakedFragmentShader); + const auto program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("depthMap", 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + auto state = std::make_shared(); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setColorWriteMask(true, false, false, false); + _debugPipeline = gpu::Pipeline::create(program, state); + } + + return _debugPipeline; +} diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h new file mode 100644 index 0000000000..bb4343b06a --- /dev/null +++ b/libraries/render-utils/src/OutlineEffect.h @@ -0,0 +1,112 @@ +// +// OutlineEffect.h +// render-utils/src/ +// +// Created by Olivier Prat on 08/08/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_OutlineEffect_h +#define hifi_render_utils_OutlineEffect_h + +#include +#include "DeferredFramebuffer.h" + +/* +class PickItemsConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool isEnabled MEMBER isEnabled NOTIFY dirty) + +public: + + bool isEnabled{ false }; + +signals: + + void dirty(); +}; +*/ + +class OutlineFramebuffer { +public: + OutlineFramebuffer(); + + gpu::FramebufferPointer getDepthFramebuffer(); + gpu::TexturePointer getDepthTexture(); + + // Update the source framebuffer size which will drive the allocation of all the other resources. + void update(const gpu::TexturePointer& linearDepthBuffer); + const glm::ivec2& getSourceFrameSize() const { return _frameSize; } + +protected: + + void clear(); + void allocate(); + + gpu::FramebufferPointer _depthFramebuffer; + gpu::TexturePointer _depthTexture; + + glm::ivec2 _frameSize; +}; + +using OutlineFramebufferPointer = std::shared_ptr; + +class PrepareOutline { + +public: + + using Input = render::VaryingSet2; + // Output will contain outlined objects only z-depth texture + using Output = OutlineFramebufferPointer; + using JobModel = render::Job::ModelIO; + + PrepareOutline() {} + + void run(const render::RenderContextPointer& renderContext, const PrepareOutline::Input& input, PrepareOutline::Output& output); + +private: + + OutlineFramebufferPointer _outlineFramebuffer; + gpu::PipelinePointer _copyDepthPipeline; +}; + +class DebugOutlineConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(bool viewOutlinedDepth MEMBER viewOutlinedDepth NOTIFY dirty) + +public: + + bool viewOutlinedDepth{ false }; + +signals: + void dirty(); +}; + + +class DebugOutline { +public: + using Input = OutlineFramebufferPointer; + using Config = DebugOutlineConfig; + using JobModel = render::Job::ModelI; + + DebugOutline(); + ~DebugOutline(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Input& inputs); + +private: + + const gpu::PipelinePointer& getDebugPipeline(); + + gpu::PipelinePointer _debugPipeline; + int _geometryId{ 0 }; + bool _isDisplayDepthEnabled{ false }; +}; + +#endif // hifi_render_utils_OutlineEffect_h + + diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 01c111151f..8740b767f6 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -40,7 +40,7 @@ #include "ToneMappingEffect.h" #include "SubsurfaceScattering.h" #include "PickItemsJob.h" -#include "RenderOutline.h" +#include "OutlineEffect.h" #include @@ -95,7 +95,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawOpaqueOutlined", opaqueOutlineInputs, shapePlumber); // Retrieve z value of the outlined objects - const auto outlinedZBuffer = task.addJob("PrepareOutline", deferredFramebuffer); + const auto outlinePrepareInputs = PrepareOutline::Input(deferredFramebuffer, outlinedOpaques).hasVarying(); + const auto outlinedFrameBuffer = task.addJob("PrepareOutline", outlinePrepareInputs); // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying(); @@ -199,6 +200,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); task.addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); + // Debug outline + task.addJob("DebugOutline", outlinedFrameBuffer); + // Scene Octree Debugging job { task.addJob("DrawSceneOctree", spatialSelection); diff --git a/libraries/render-utils/src/RenderOutline.cpp b/libraries/render-utils/src/RenderOutline.cpp deleted file mode 100644 index 6ecf86143f..0000000000 --- a/libraries/render-utils/src/RenderOutline.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// -// RenderOutline.cpp -// render-utils/src/ -// -// Created by Olivier Prat on 08/08/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 "RenderOutline.h" - -#include "gpu/Context.h" -#include "gpu/StandardShaderLib.h" - -#include "surfaceGeometry_copyDepth_frag.h" - -void PrepareOutline::run(const render::RenderContextPointer& renderContext, const PrepareOutline::Input& deferredFramebuffer, PrepareOutline::Output& output) { - glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); - auto args = renderContext->args; - - // Resizing framebuffers instead of re-building them seems to cause issues with threaded - // rendering - if (_outlineFramebuffer && _outlineFramebuffer->getSize() != frameSize) { - _outlineFramebuffer.reset(); - } - - if (!_outlineFramebuffer) { - _outlineFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("deferredOutline")); - auto colorFormat = gpu::Element::COLOR_RED_HALF; - - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); - - _outlineFramebuffer->setRenderBuffer(0, primaryColorTexture); - } - - if (!_copyDepthPipeline) { - auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(surfaceGeometry_copyDepth_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 0)); - gpu::Shader::makeProgram(*program, slotBindings); - - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - - state->setColorWriteMask(true, false, false, false); - - // Good to go add the brand new pipeline - _copyDepthPipeline = gpu::Pipeline::create(program, state); - } - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - auto depthBuffer = deferredFramebuffer->getPrimaryDepthTexture(); - - // Copy depth to texture as we will overwrite - batch.enableStereo(false); - - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(frameSize, args->_viewport)); - - batch.setFramebuffer(_outlineFramebuffer); - batch.setPipeline(_copyDepthPipeline); - batch.setResourceTexture(0, depthBuffer); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - // Restore previous frame buffer - batch.setFramebuffer(deferredFramebuffer->getDeferredFramebuffer()); - }); - - output = _outlineFramebuffer->getRenderBuffer(0); -} diff --git a/libraries/render-utils/src/RenderOutline.h b/libraries/render-utils/src/RenderOutline.h deleted file mode 100644 index a97f2033ef..0000000000 --- a/libraries/render-utils/src/RenderOutline.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// RenderOutline.h -// render-utils/src/ -// -// Created by Olivier Prat on 08/08/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_RenderOutline_h -#define hifi_render_utils_RenderOutline_h - -#include -#include "DeferredFramebuffer.h" - -/* -class PickItemsConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(bool isEnabled MEMBER isEnabled NOTIFY dirty) - -public: - - bool isEnabled{ false }; - -signals: - - void dirty(); -}; -*/ -class PrepareOutline { - -public: - - using Input = DeferredFramebufferPointer; - // Output will contain outlined objects only z-depth texture - using Output = gpu::TexturePointer; - using JobModel = render::Job::ModelIO; - - PrepareOutline() {} - - void run(const render::RenderContextPointer& renderContext, const PrepareOutline::Input& input, PrepareOutline::Output& output); - -private: - - gpu::FramebufferPointer _outlineFramebuffer; - gpu::PipelinePointer _copyDepthPipeline; -}; - -#endif // hifi_render_utils_RenderOutline_h - - diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js new file mode 100644 index 0000000000..6a456054e1 --- /dev/null +++ b/scripts/developer/utilities/render/debugOutline.js @@ -0,0 +1,20 @@ +// +// debugOutline.js +// developer/utilities/render +// +// Olivier Prat, created on 08/08/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('outline.qml'); +var window = new OverlayWindow({ + title: 'Outline', + source: qml, + width: 250, + height: 80, +}); +window.closed.connect(function() { Script.stop(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml new file mode 100644 index 0000000000..11a9ecf432 --- /dev/null +++ b/scripts/developer/utilities/render/outline.qml @@ -0,0 +1,37 @@ +// +// outline.qml +// developer/utilities/render +// +// Olivier Prat, created on 08/08/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 + +Item { + id: root + property var pickConfig: Render.getConfig("RenderMainView.PickOutlined") + property var debugConfig: Render.getConfig("RenderMainView.DebugOutline") + + Column { + spacing: 8 + + CheckBox { + text: "Edit Outline" + checked: root.pickConfig["enabled"] + onCheckedChanged: { + root.pickConfig["enabled"] = checked; + } + } + CheckBox { + text: "View Outlined Depth" + checked: root.debugConfig["viewOutlinedDepth"] + onCheckedChanged: { + root.debugConfig["viewOutlinedDepth"] = checked; + } + } + } +}