From 25b3549e042f9e21438d0236c4610a641c4c5bb3 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 18 Oct 2017 15:00:53 +0200 Subject: [PATCH] Working multiple outlines except debugging scripts which applies config to all outlines and graphics bug when filled --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- .../src/ui/overlays/ContextOverlayInterface.h | 6 +- libraries/render-utils/src/Outline.slh | 81 +--- libraries/render-utils/src/OutlineEffect.cpp | 423 ++++++------------ libraries/render-utils/src/OutlineEffect.h | 178 +++----- libraries/render-utils/src/Outline_shared.slh | 4 - .../render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render-utils/src/model_outline.slf | 21 - .../render-utils/src/model_outline_fade.slf | 31 -- libraries/render/src/render/Scene.h | 5 + .../utilities/render/debugOutline.js | 12 +- .../developer/utilities/render/outline.qml | 12 +- 12 files changed, 251 insertions(+), 526 deletions(-) delete mode 100644 libraries/render-utils/src/model_outline.slf delete mode 100644 libraries/render-utils/src/model_outline_fade.slf diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f677dc6550..3a3026d5ae 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,7 +36,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _selectionToSceneHandlers[0].initialize("contextOverlayHighlightList"); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[0], &SelectionToSceneHandler::selectedItemsListChanged); - for (auto i = 1; i < MAX_HIGHLIGHT_COUNT; i++) { + for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT ; i++) { _selectionToSceneHandlers[i].initialize(QString("contextOverlayHighlightList")+QString::number(i)); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[i], &SelectionToSceneHandler::selectedItemsListChanged); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 28e3707f99..3d33d2a802 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -48,10 +48,6 @@ class ContextOverlayInterface : public QObject, public Dependency { std::shared_ptr _contextOverlay { nullptr }; public: - enum { - MAX_HIGHLIGHT_COUNT = 5 - }; - ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } @@ -91,7 +87,7 @@ private: void deletingEntity(const EntityItemID& entityItemID); - SelectionToSceneHandler _selectionToSceneHandlers[MAX_HIGHLIGHT_COUNT]; + SelectionToSceneHandler _selectionToSceneHandlers[render::Scene::MAX_OUTLINE_COUNT]; }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index bc71cabf1e..fe9594cc12 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -16,13 +16,11 @@ <@include Outline_shared.slh@> uniform outlineParamsBuffer { - OutlineParameters groups[GROUP_COUNT]; + OutlineParameters params; }; uniform sampler2D sceneDepthMap; uniform sampler2D outlinedDepthMap; -uniform sampler2D outlinedIdMap; -uniform int enabledGroupsMask; in vec2 varTexCoord0; out vec4 outFragColor; @@ -33,52 +31,47 @@ const float OPACITY_EPSILON = 5e-3; <@func main(IS_FILLED)@> -int getGroupIndexFromColor(vec4 color) { - ivec4 id = ivec4(color * GROUP_ID_COLOR_COMPONENT_MAX) << ivec4(0, GROUP_ID_COLOR_COMPONENT_BITS, GROUP_ID_COLOR_COMPONENT_BITS*2, GROUP_ID_COLOR_COMPONENT_BITS*3); - return (id.r | id.g | id.b | id.a) - 1; -} - -vec4 computeGroupOutline(int centerGroupId, float centerDepth, int groupId, vec2 texCoord) { +void main(void) { + // 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 halfTexel = getInvWidthHeight() / 2; + vec2 texCoord0 = varTexCoord0+halfTexel; + float outlinedDepth = texture(outlinedDepthMap, texCoord0).x; float intensity = 0.0; - if (centerGroupId==groupId && centerDepth < FAR_Z) { - // We're on the outlined object, thus no outline to do! + 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@> - OutlineParameters groupParams = groups[groupId]; - // But we need to fill the interior - float sceneDepth = texture(sceneDepthMap, texCoord).x; + float sceneDepth = texture(sceneDepthMap, texCoord0).x; // Transform to linear depth for better precision - centerDepth = -evalZeyeFromZdb(centerDepth); + outlinedDepth = -evalZeyeFromZdb(outlinedDepth); sceneDepth = -evalZeyeFromZdb(sceneDepth); // Are we occluded? - intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._occludedFillOpacity : groupParams._unoccludedFillOpacity; - return vec4(groupParams._color.rgb, intensity); + intensity = sceneDepth < (outlinedDepth-LINEAR_DEPTH_BIAS) ? params._occludedFillOpacity : params._unoccludedFillOpacity; <@else@> - return vec4(0,0,0,0); + discard; <@endif@> } else { - OutlineParameters groupParams = groups[groupId]; float weight = 0.0; - vec2 deltaUv = groupParams._size / groupParams._blurKernelSize; - vec2 lineStartUv = texCoord - groupParams._size / 2.0; + vec2 deltaUv = params._size / params._blurKernelSize; + vec2 lineStartUv = texCoord0 - 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) { - vec4 outlinedIdColor = texture(outlinedIdMap, uv); - float outlinedDepth = texture(outlinedDepthMap, uv).x; - int outlinedId = getGroupIndexFromColor(outlinedIdColor); - intensity += (outlinedDepth diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 74a29e8237..d00819d0f2 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -18,6 +18,7 @@ #include "gpu/Context.h" #include "gpu/StandardShaderLib.h" +#include #include "surfaceGeometry_copyDepth_frag.h" #include "debug_deferred_buffer_vert.h" @@ -30,22 +31,29 @@ using namespace render; OutlineRessources::OutlineRessources() { } -void OutlineRessources::update(const gpu::TexturePointer& colorBuffer) { +void OutlineRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { + auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); + // If the depth buffer or size changed, we need to delete our FBOs and recreate them at the // new correct dimensions. - if (_depthTexture) { - auto newFrameSize = glm::ivec2(colorBuffer->getDimensions()); + if (_depthFrameBuffer) { if (_frameSize != newFrameSize) { _frameSize = newFrameSize; clear(); } } + if (!_colorFrameBuffer) { + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac + _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); + _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + } + } } void OutlineRessources::clear() { - _frameBuffer.reset(); - _depthTexture.reset(); - _idTexture.reset(); + _depthFrameBuffer.reset(); } void OutlineRessources::allocate() { @@ -53,177 +61,95 @@ void OutlineRessources::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - - _idTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_2, width, height)); - _depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); + auto depthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height)); - _frameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); - _frameBuffer->setDepthStencilBuffer(_depthTexture, depthFormat); - _frameBuffer->setRenderBuffer(0, _idTexture); + _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("outlineDepth")); + _depthFrameBuffer->setDepthStencilBuffer(depthTexture, depthFormat); } -gpu::FramebufferPointer OutlineRessources::getFramebuffer() { - if (!_frameBuffer) { +gpu::FramebufferPointer OutlineRessources::getDepthFramebuffer() { + if (!_depthFrameBuffer) { allocate(); } - return _frameBuffer; + return _depthFrameBuffer; } gpu::TexturePointer OutlineRessources::getDepthTexture() { - if (!_depthTexture) { - allocate(); - } - return _depthTexture; -} - -gpu::TexturePointer OutlineRessources::getIdTexture() { - if (!_idTexture) { - allocate(); - } - return _idTexture; -} - -glm::vec4 encodeIdToColor(unsigned int id) { - union { - struct { - unsigned int r : 2; - unsigned int g : 2; - unsigned int b : 2; - unsigned int a : 2; - }; - unsigned char id; - } groupId; - - static_assert(GROUP_ID_COLOR_COMPONENT_BITS == 2, "Assuming two bits per component contrary to GLSL shader code. See Outline_shared.slh"); - - assert(id < 254); - groupId.id = id+1; - - glm::vec4 idColor{ groupId.r, groupId.g, groupId.b, groupId.a }; - - // Normalize. Since we put 2 bits into each color component, each component has a maximum - // value of 3. - idColor /= GROUP_ID_COLOR_COMPONENT_MAX; - return idColor; -} - -void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - auto& groups = inputs.get0(); - auto& deferredFrameBuffer = inputs.get1(); - - RenderArgs* args = renderContext->args; - ShapeKey::Builder defaultKeyBuilder; - auto hasOutline = false; - - if (!_outlineRessources) { - _outlineRessources = std::make_shared(); - } - _outlineRessources->update(deferredFrameBuffer->getDeferredColorTexture()); - - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); - auto colorLoc = maskPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - auto skinnedColorLoc = maskSkinnedPipeline.get()->pipeline->getProgram()->getUniforms().findLocation("color"); - unsigned int groupId = 0; - - for (auto& inShapeBounds : groups) { - if (!inShapeBounds.isNull()) { - auto& inShapes = inShapeBounds.get(); - - if (!inShapes.empty()) { - if (!hasOutline) { - batch.setFramebuffer(_outlineRessources->getFramebuffer()); - // Clear it only if it hasn't been done before - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, - vec4(0.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0, false); - - // 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); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - hasOutline = true; - } - - std::vector skinnedShapeKeys{}; - // Encode group id in quantized color - glm::vec4 idColor = encodeIdToColor(groupId); - - // Iterate through all inShapes and render the unskinned - args->_shapePipeline = maskPipeline; - batch.setPipeline(maskPipeline->pipeline); - batch._glUniform4f(colorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (auto items : inShapes) { - if (items.first.isSkinned()) { - skinnedShapeKeys.push_back(items.first); - } else { - renderItems(renderContext, items.second); - } - } - - // Reiterate to render the skinned - args->_shapePipeline = maskSkinnedPipeline; - batch.setPipeline(maskSkinnedPipeline->pipeline); - batch._glUniform4f(skinnedColorLoc, idColor.r, idColor.g, idColor.b, idColor.a); - for (const auto& key : skinnedShapeKeys) { - renderItems(renderContext, inShapes.at(key)); - } - } - } - - ++groupId; - } - - args->_shapePipeline = nullptr; - args->_batch = nullptr; - }); - - if (hasOutline) { - output = _outlineRessources; - } else { - output = nullptr; - } + return getDepthFramebuffer()->getDepthStencilBuffer(); } PrepareDrawOutline::PrepareDrawOutline() { - + _ressources = std::make_shared(); } void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto destinationFrameBuffer = inputs; - auto framebufferSize = destinationFrameBuffer->getSize(); - if (!_primaryWithoutDepthBuffer || framebufferSize != _frameBufferSize) { - // Failing to recreate this frame buffer when the screen has been resized creates a bug on Mac - _primaryWithoutDepthBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithoutDepth")); - _primaryWithoutDepthBuffer->setRenderBuffer(0, destinationFrameBuffer->getRenderBuffer(0)); - _frameBufferSize = framebufferSize; + _ressources->update(destinationFrameBuffer); + outputs = _ressources; +} + +void DrawOutlineMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + auto& inShapes = inputs.get0(); + + if (!inShapes.empty()) { + auto ressources = inputs.get1(); + + RenderArgs* args = renderContext->args; + ShapeKey::Builder defaultKeyBuilder; + + outputs = args->_viewport; + + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); + auto maskSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); + + batch.setFramebuffer(ressources->getDepthFramebuffer()); + batch.clearDepthFramebuffer(1.0f); + + // 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); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + std::vector skinnedShapeKeys{}; + + // Iterate through all inShapes and render the unskinned + args->_shapePipeline = maskPipeline; + batch.setPipeline(maskPipeline->pipeline); + for (auto items : inShapes) { + if (items.first.isSkinned()) { + skinnedShapeKeys.push_back(items.first); + } else { + renderItems(renderContext, items.second); + } + } + + // Reiterate to render the skinned + args->_shapePipeline = maskSkinnedPipeline; + batch.setPipeline(maskSkinnedPipeline->pipeline); + for (const auto& key : skinnedShapeKeys) { + renderItems(renderContext, inShapes.at(key)); + } + + args->_shapePipeline = nullptr; + args->_batch = nullptr; + }); + } else { + // Outline rect should be null as there are no outlined shapes + outputs = glm::ivec4(0, 0, 0, 0); } - - outputs = _primaryWithoutDepthBuffer; -} - -int DrawOutlineConfig::getGroupCount() const { - return Outline::MAX_GROUP_COUNT; -} - -void DrawOutlineConfig::setGroup(int value) { - assert(value >= 0 && value < Outline::MAX_GROUP_COUNT); - group = std::min(value, Outline::MAX_GROUP_COUNT); - group = std::max(group, 0); - emit dirty(); } gpu::PipelinePointer DrawOutline::_pipeline; @@ -236,41 +162,28 @@ void DrawOutline::configure(const Config& config) { auto& configuration = _configuration.edit(); const auto OPACITY_EPSILON = 5e-3f; - bool someFilled = false; - bool isFilled; + configuration._color = config.color; + configuration._intensity = config.intensity * (config.glow ? 2.f : 1.f); + configuration._unoccludedFillOpacity = config.unoccludedFillOpacity; + configuration._occludedFillOpacity = config.occludedFillOpacity; + configuration._threshold = config.glow ? 1.f : 1e-3f; + configuration._blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width * 3 + 0.5f))); + // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. + _size = config.width / 400.0f; + configuration._size.x = (_size * _framebufferSize.y) / _framebufferSize.x; + configuration._size.y = _size; - for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { - auto& dstGroupConfig = configuration._groups[groupId]; - auto& srcGroupConfig = config.groupParameters[groupId]; - - dstGroupConfig._color = srcGroupConfig.color; - dstGroupConfig._intensity = srcGroupConfig.intensity * (srcGroupConfig.glow ? 2.f : 1.f); - dstGroupConfig._unoccludedFillOpacity = srcGroupConfig.unoccludedFillOpacity; - dstGroupConfig._occludedFillOpacity = srcGroupConfig.occludedFillOpacity; - dstGroupConfig._threshold = srcGroupConfig.glow ? 1.f : 1e-3f; - dstGroupConfig._blurKernelSize = std::min(10, std::max(2, (int)floorf(srcGroupConfig.width * 2 + 0.5f))); - // Size is in normalized screen height. We decide that for outline width = 1, this is equal to 1/400. - _sizes[groupId] = srcGroupConfig.width / 400.0f; - - isFilled = (srcGroupConfig.unoccludedFillOpacity > OPACITY_EPSILON || srcGroupConfig.occludedFillOpacity > OPACITY_EPSILON); - someFilled = someFilled || isFilled; - } - - if (someFilled) { - _mode = M_SOME_FILLED; - } else { - _mode = M_ALL_UNFILLED; - } + _isFilled = (config.unoccludedFillOpacity > OPACITY_EPSILON || config.occludedFillOpacity > OPACITY_EPSILON); } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto outlineFrameBuffer = inputs.get1(); + auto outlineRect = inputs.get4(); - if (outlineFrameBuffer) { + if (outlineFrameBuffer && outlineRect.z>0 && outlineRect.w>0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto outlinedDepthTexture = outlineFrameBuffer->getDepthTexture(); - auto outlinedIdTexture = outlineFrameBuffer->getIdTexture(); auto destinationFrameBuffer = inputs.get3(); auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); @@ -281,13 +194,8 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I if (_framebufferSize != framebufferSize) { auto& configuration = _configuration.edit(); - - for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { - auto& groupConfig = configuration._groups[groupId]; - - groupConfig._size.x = (_sizes[groupId] * framebufferSize.y) / framebufferSize.x; - groupConfig._size.y = _sizes[groupId]; - } + configuration._size.x = (_size * framebufferSize.y) / framebufferSize.x; + configuration._size.y = _size; _framebufferSize = framebufferSize; } @@ -301,14 +209,10 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); batch.setPipeline(pipeline); - auto enabledGroupsLoc = pipeline->getProgram()->getUniforms().findLocation("enabledGroupsMask"); - batch.setUniformBuffer(OUTLINE_PARAMS_SLOT, _configuration); batch.setUniformBuffer(FRAME_TRANSFORM_SLOT, frameTransform->getFrameTransformBuffer()); batch.setResourceTexture(SCENE_DEPTH_SLOT, sceneDepthBuffer->getPrimaryDepthTexture()); batch.setResourceTexture(OUTLINED_DEPTH_SLOT, outlinedDepthTexture); - batch.setResourceTexture(OUTLINED_ID_SLOT, outlinedIdTexture); - batch._glUniform1i(enabledGroupsLoc, 1); batch.draw(gpu::TRIANGLE_STRIP, 4); }); } @@ -326,7 +230,6 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", FRAME_TRANSFORM_SLOT)); slotBindings.insert(gpu::Shader::Binding("sceneDepthMap", SCENE_DEPTH_SLOT)); slotBindings.insert(gpu::Shader::Binding("outlinedDepthMap", OUTLINED_DEPTH_SLOT)); - slotBindings.insert(gpu::Shader::Binding("outlinedIdMap", OUTLINED_ID_SLOT)); gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -339,19 +242,17 @@ const gpu::PipelinePointer& DrawOutline::getPipeline() { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return _mode == M_SOME_FILLED ? _pipelineFilled : _pipeline; + return _isFilled ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { _geometryDepthId = DependencyManager::get()->allocateID(); - _geometryColorId = DependencyManager::get()->allocateID(); } DebugOutline::~DebugOutline() { auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_geometryDepthId); - geometryCache->releaseID(_geometryColorId); } } @@ -360,9 +261,9 @@ void DebugOutline::configure(const Config& config) { } void DebugOutline::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto outlineFramebuffer = input; + const auto outlineRessources = input; - if (_isDisplayEnabled && outlineFramebuffer) { + if (_isDisplayEnabled && outlineRessources) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; @@ -384,20 +285,10 @@ void DebugOutline::run(const render::RenderContextPointer& renderContext, const const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); batch.setPipeline(getDepthPipeline()); - batch.setResourceTexture(0, outlineFramebuffer->getDepthTexture()); - { - const glm::vec2 bottomLeft(-1.0f, -1.0f); - const glm::vec2 topRight(0.0f, 1.0f); - geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); - } - - batch.setPipeline(getIdPipeline()); - batch.setResourceTexture(0, outlineFramebuffer->getIdTexture()); - { - const glm::vec2 bottomLeft(0.0f, -1.0f); - const glm::vec2 topRight(1.0f, 1.0f); - geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryColorId); - } + batch.setResourceTexture(0, outlineRessources->getDepthTexture()); + const glm::vec2 bottomLeft(-1.0f, -1.0f); + const glm::vec2 topRight(1.0f, 1.0f); + geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); batch.setResourceTexture(0, nullptr); }); @@ -439,27 +330,6 @@ void DebugOutline::initializePipelines() { _depthPipeline = gpu::Pipeline::create(program, state); } - - // ID shader - { - static const std::string ID_SHADER{ - "vec4 getFragmentColor() {" - " return texelFetch(albedoMap, ivec2(gl_FragCoord.xy), 0); " - "}" - }; - - auto fragmentShader = FRAGMENT_SHADER; - fragmentShader.replace(SOURCE_PLACEHOLDER_INDEX, SOURCE_PLACEHOLDER.size(), ID_SHADER); - - const auto ps = gpu::Shader::createPixel(fragmentShader); - const auto program = gpu::Shader::createProgram(vs, ps); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("albedoMap", 0)); - gpu::Shader::makeProgram(*program, slotBindings); - - _idPipeline = gpu::Pipeline::create(program, state); - } } const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { @@ -470,14 +340,6 @@ const gpu::PipelinePointer& DebugOutline::getDepthPipeline() { return _depthPipeline; } -const gpu::PipelinePointer& DebugOutline::getIdPipeline() { - if (!_idPipeline) { - initializePipelines(); - } - - return _idPipeline; -} - DrawOutlineTask::DrawOutlineTask() { } @@ -496,70 +358,63 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende ShapePlumberPointer shapePlumber = std::make_shared(); { auto state = std::make_shared(); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setColorWriteMask(true, true, true, true); - + state->setDepthTest(true, true, gpu::LESS); + state->setColorWriteMask(false, false, false, false); initMaskPipelines(*shapePlumber, state); } - DrawOutlineMask::Groups sortedBounds; - for (auto i = 0; i < Outline::MAX_GROUP_COUNT; i++) { + // Prepare for outline group rendering. + const auto outlineRessources = task.addJob("PrepareOutline", primaryFramebuffer); + + for (auto i = 0; i < render::Scene::MAX_OUTLINE_COUNT; i++) { const auto groupItems = groups[i]; const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); // Sort const auto sortedPipelines = task.addJob("OutlinePipelineSort", outlinedItems); - sortedBounds[i] = task.addJob("OutlineDepthSort", sortedPipelines); + const auto sortedBounds = task.addJob("OutlineDepthSort", sortedPipelines); + + // Draw depth of outlined objects in separate buffer + std::string name; + { + std::ostringstream stream; + stream << "OutlineMask" << i; + name = stream.str(); + } + const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, outlineRessources).asVarying(); + const auto outlinedRect = task.addJob(name, drawMaskInputs, shapePlumber); + + // Draw outline + { + std::ostringstream stream; + stream << "OutlineEffect" << i; + name = stream.str(); + } + const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlineRessources, sceneFrameBuffer, primaryFramebuffer, outlinedRect).asVarying(); + task.addJob(name, drawOutlineInputs); } - // Draw depth of outlined objects in separate buffer - const auto drawMaskInputs = DrawOutlineMask::Inputs(sortedBounds, sceneFrameBuffer).asVarying(); - const auto outlinedFrameBuffer = task.addJob("OutlineMask", drawMaskInputs, shapePlumber); - - // Prepare for outline group rendering. - const auto destFrameBuffer = task.addJob("PrepareOutline", primaryFramebuffer); - - // Draw outline - const auto drawOutlineInputs = DrawOutline::Inputs(deferredFrameTransform, outlinedFrameBuffer, sceneFrameBuffer, destFrameBuffer).asVarying(); - task.addJob("OutlineEffect", drawOutlineInputs); - // Debug outline - task.addJob("OutlineDebug", outlinedFrameBuffer); + task.addJob("OutlineDebug", outlineRessources); } #include "model_shadow_vert.h" -#include "model_shadow_fade_vert.h" #include "skin_model_shadow_vert.h" -#include "skin_model_shadow_fade_vert.h" -#include "model_outline_frag.h" -#include "model_outline_fade_frag.h" +#include "model_shadow_frag.h" void DrawOutlineTask::initMaskPipelines(render::ShapePlumber& shapePlumber, gpu::StatePointer state) { auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto modelPixel = gpu::Shader::createPixel(std::string(model_outline_frag)); + auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutSkinned().withoutFade(), + ShapeKey::Filter::Builder().withoutSkinned(), modelProgram, state); auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, modelPixel); shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withSkinned().withoutFade(), + ShapeKey::Filter::Builder().withSkinned(), skinProgram, state); - - auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_shadow_fade_vert)); - auto modelFadePixel = gpu::Shader::createPixel(std::string(model_outline_fade_frag)); - gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(modelFadeVertex, modelFadePixel); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withoutSkinned().withFade(), - modelFadeProgram, state); - - auto skinFadeVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_fade_vert)); - gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(skinFadeVertex, modelFadePixel); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withSkinned().withFade(), - skinFadeProgram, state); } diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index bc10fc5e41..b1c2e3c3dd 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -20,12 +20,13 @@ class OutlineRessources { public: OutlineRessources(); - gpu::FramebufferPointer getFramebuffer(); - gpu::TexturePointer getIdTexture(); + gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); + gpu::FramebufferPointer getColorFramebuffer() { return _colorFrameBuffer; } + // Update the source framebuffer size which will drive the allocation of all the other resources. - void update(const gpu::TexturePointer& colorBuffer); + void update(const gpu::FramebufferPointer& primaryFrameBuffer); const glm::ivec2& getSourceFrameSize() const { return _frameSize; } protected: @@ -33,9 +34,8 @@ protected: void clear(); void allocate(); - gpu::FramebufferPointer _frameBuffer; - gpu::TexturePointer _depthTexture; - gpu::TexturePointer _idTexture; + gpu::FramebufferPointer _depthFrameBuffer; + gpu::FramebufferPointer _colorFrameBuffer; glm::ivec2 _frameSize; }; @@ -45,9 +45,8 @@ using OutlineRessourcesPointer = std::shared_ptr; class PrepareDrawOutline { public: using Inputs = gpu::FramebufferPointer; - using Outputs = gpu::FramebufferPointer; - using Config = render::Job::Config; - using JobModel = render::Job::ModelIO; + using Outputs = OutlineRessourcesPointer; + using JobModel = render::Job::ModelIO; PrepareDrawOutline(); @@ -55,84 +54,63 @@ public: private: - gpu::FramebufferPointer _primaryWithoutDepthBuffer; - gpu::Vec2u _frameBufferSize{ 0, 0 }; + OutlineRessourcesPointer _ressources; }; -class Outline { +class DrawOutlineMask { +public: + + using Inputs = render::VaryingSet2; + using Outputs = glm::ivec4; + using JobModel = render::Job::ModelIO; + + DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + protected: -#include "Outline_shared.slh" - -public: - enum { - MAX_GROUP_COUNT = GROUP_COUNT - }; + render::ShapePlumberPointer _shapePlumber; }; class DrawOutlineConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int group MEMBER group WRITE setGroup NOTIFY dirty); - Q_PROPERTY(bool glow READ isGlow WRITE setGlow NOTIFY dirty) - Q_PROPERTY(float width READ getWidth WRITE setWidth NOTIFY dirty) - Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) + Q_PROPERTY(bool glow MEMBER glow NOTIFY dirty) + Q_PROPERTY(float width MEMBER width NOTIFY dirty) + Q_PROPERTY(float intensity MEMBER intensity 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) - Q_PROPERTY(float unoccludedFillOpacity READ getUnoccludedFillOpacity WRITE setUnoccludedFillOpacity NOTIFY dirty) - Q_PROPERTY(float occludedFillOpacity READ getOccludedFillOpacity WRITE setOccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float unoccludedFillOpacity MEMBER unoccludedFillOpacity NOTIFY dirty) + Q_PROPERTY(float occludedFillOpacity MEMBER occludedFillOpacity NOTIFY dirty) public: - struct GroupParameters { - glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; - float unoccludedFillOpacity{ 0.0f }; - float occludedFillOpacity{ 0.0f }; - bool glow{ false }; - }; + void setColorR(float value) { color.r = value; emit dirty(); } + float getColorR() const { return color.r; } - int getGroupCount() const; + void setColorG(float value) { color.g = value; emit dirty(); } + float getColorG() const { return color.g; } - void setColorR(float value) { groupParameters[group].color.r = value; emit dirty(); } - float getColorR() const { return groupParameters[group].color.r; } + void setColorB(float value) { color.b = value; emit dirty(); } + float getColorB() const { return color.b; } - void setColorG(float value) { groupParameters[group].color.g = value; emit dirty(); } - float getColorG() const { return groupParameters[group].color.g; } - - void setColorB(float value) { groupParameters[group].color.b = value; emit dirty(); } - float getColorB() const { return groupParameters[group].color.b; } - - void setGlow(bool value) { groupParameters[group].glow = value; emit dirty(); } - bool isGlow() const { return groupParameters[group].glow; } - - void setWidth(float value) { groupParameters[group].width = value; emit dirty(); } - float getWidth() const { return groupParameters[group].width; } - - void setIntensity(float value) { groupParameters[group].intensity = value; emit dirty(); } - float getIntensity() const { return groupParameters[group].intensity; } - - void setUnoccludedFillOpacity(float value) { groupParameters[group].unoccludedFillOpacity = value; emit dirty(); } - float getUnoccludedFillOpacity() const { return groupParameters[group].unoccludedFillOpacity; } - - void setOccludedFillOpacity(float value) { groupParameters[group].occludedFillOpacity = value; emit dirty(); } - float getOccludedFillOpacity() const { return groupParameters[group].occludedFillOpacity; } - - void setGroup(int value); - - int group{ 0 }; - GroupParameters groupParameters[Outline::MAX_GROUP_COUNT]; + glm::vec3 color{ 1.f, 0.7f, 0.2f }; + float width{ 2.0f }; + float intensity{ 0.9f }; + float unoccludedFillOpacity{ 0.0f }; + float occludedFillOpacity{ 0.0f }; + bool glow{ false }; signals: void dirty(); }; -class DrawOutline : public Outline { +class DrawOutline { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = DrawOutlineConfig; using JobModel = render::Job::ModelI; @@ -143,71 +121,27 @@ public: private: +#include "Outline_shared.slh" + enum { SCENE_DEPTH_SLOT = 0, OUTLINED_DEPTH_SLOT, - OUTLINED_ID_SLOT, OUTLINE_PARAMS_SLOT = 0, FRAME_TRANSFORM_SLOT, }; - struct OutlineConfiguration { - OutlineParameters _groups[MAX_GROUP_COUNT]; - }; - - enum Mode { - M_ALL_UNFILLED, - M_SOME_FILLED, - }; - - using OutlineConfigurationBuffer = gpu::StructBuffer; + using OutlineConfigurationBuffer = gpu::StructBuffer; const gpu::PipelinePointer& getPipeline(); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; + OutlineConfigurationBuffer _configuration; glm::ivec2 _framebufferSize{ 0,0 }; - Mode _mode{ M_ALL_UNFILLED }; - float _sizes[MAX_GROUP_COUNT]; -}; - -class DrawOutlineTask { -public: - - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet4; - using Config = render::Task::Config; - using JobModel = render::Task::ModelI; - - DrawOutlineTask(); - - void configure(const Config& config); - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); - -private: - - static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); -}; - -class DrawOutlineMask { -public: - - using Groups = render::VaryingArray; - using Inputs = render::VaryingSet2; - // Output will contain outlined objects only z-depth texture and the input primary buffer but without the primary depth buffer - using Outputs = OutlineRessourcesPointer; - using JobModel = render::Job::ModelIO; - - DrawOutlineMask(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output); - -protected: - - render::ShapePlumberPointer _shapePlumber; - OutlineRessourcesPointer _outlineRessources; + bool _isFilled{ false }; + float _size; }; class DebugOutlineConfig : public render::Job::Config { @@ -237,16 +171,30 @@ public: private: gpu::PipelinePointer _depthPipeline; - gpu::PipelinePointer _idPipeline; int _geometryDepthId{ 0 }; - int _geometryColorId{ 0 }; bool _isDisplayEnabled{ false }; const gpu::PipelinePointer& getDepthPipeline(); - const gpu::PipelinePointer& getIdPipeline(); void initializePipelines(); }; +class DrawOutlineTask { +public: + + using Groups = render::VaryingArray; + using Inputs = render::VaryingSet4; + using Config = render::Task::Config; + using JobModel = render::Task::ModelI; + + DrawOutlineTask(); + + void configure(const Config& config); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); + +private: + + static void initMaskPipelines(render::ShapePlumber& plumber, gpu::StatePointer state); +}; #endif // hifi_render_utils_OutlineEffect_h diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index bda67c49d6..2ebece8903 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -9,10 +9,6 @@ # define VEC4 vec4 #endif -#define GROUP_COUNT 7 -#define GROUP_ID_COLOR_COMPONENT_BITS 2 -#define GROUP_ID_COLOR_COMPONENT_MAX 3 - struct OutlineParameters { VEC3 _color; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 73d8b93c07..54c2248d8b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -181,7 +181,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto selectedItems = addSelectItemJobs(task, selectionBaseName, metas, opaques, transparents); DrawOutlineTask::Groups outlineGroups; outlineGroups[0] = selectedItems; - for (auto i = 1; i < DrawOutline::MAX_GROUP_COUNT; i++) { + for (auto i = 1; i < render::Scene::MAX_OUTLINE_COUNT; i++) { std::ostringstream selectionName; selectionName << selectionBaseName; selectionName << i; diff --git a/libraries/render-utils/src/model_outline.slf b/libraries/render-utils/src/model_outline.slf deleted file mode 100644 index 8c11b2b295..0000000000 --- a/libraries/render-utils/src/model_outline.slf +++ /dev/null @@ -1,21 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_outline.frag -// fragment shader -// -// Created by Olivier Prat on 10/13/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 -// - -layout(location = 0) out vec4 _fragColor; - -uniform vec4 color; - -void main(void) { - _fragColor = color; -} diff --git a/libraries/render-utils/src/model_outline_fade.slf b/libraries/render-utils/src/model_outline_fade.slf deleted file mode 100644 index 69ff54dea6..0000000000 --- a/libraries/render-utils/src/model_outline_fade.slf +++ /dev/null @@ -1,31 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_outline_fade.frag -// fragment shader -// -// Created by Olivier Prat on 10/13/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 Fade.slh@> -<$declareFadeFragment()$> - -layout(location = 0) out vec4 _fragColor; - -uniform vec4 color; - -in vec4 _worldPosition; - -void main(void) { - FadeObjectParams fadeParams; - - <$fetchFadeObjectParams(fadeParams)$> - applyFadeClip(fadeParams, _worldPosition.xyz); - - _fragColor = color; -} diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index fef2077897..04a285bcd1 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -102,6 +102,11 @@ typedef std::queue TransactionQueue; // Items are notified accordingly on any update message happening class Scene { public: + + enum { + MAX_OUTLINE_COUNT = 16 + }; + Scene(glm::vec3 origin, float size); ~Scene(); diff --git a/scripts/developer/utilities/render/debugOutline.js b/scripts/developer/utilities/render/debugOutline.js index ac75197933..8e8a00f912 100644 --- a/scripts/developer/utilities/render/debugOutline.js +++ b/scripts/developer/utilities/render/debugOutline.js @@ -54,6 +54,15 @@ var end2 = { visible: true } +var outlineGroupIndex = 0 + +function setOutlineGroupIndex(index) { + print("Switching to outline group "+index) + outlineGroupIndex = index +} + +window.fromQml.connect(setOutlineGroupIndex); + var renderStates = [{name: "test", end: end}]; var defaultRenderStates = [{name: "test", distance: 20.0, end: end2}]; @@ -78,7 +87,6 @@ function update() { var result = LaserPointers.getPrevRayPickResult(ray); var selectionName = "contextOverlayHighlightList" - var outlineGroupIndex = Render.getConfig("RenderMainView.OutlineEffect").group if (outlineGroupIndex>0) { selectionName += outlineGroupIndex @@ -100,7 +108,7 @@ function update() { } Selection.addToSelectedItemsList(selectionName, typeName, result.objectID) - //print("type: " + result.type + ", id: " + result.objectID); + print("OUTLINE " + outlineGroupIndex + " picked type: " + result.type + ", id: " + result.objectID); prevID = result.objectID; prevType = typeName; diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index d62e3b8899..1e19b2cac9 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -15,7 +15,8 @@ import "configSlider" Item { id: root property var debugConfig: Render.getConfig("RenderMainView.OutlineDebug") - property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect") + property var drawConfig: Render.getConfig("RenderMainView.OutlineEffect0") + signal sendToScript(var message); Column { spacing: 8 @@ -26,12 +27,15 @@ Item { Timer { id: postpone interval: 100; running: false; repeat: false - onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets } + onTriggered: { + paramWidgetLoader.sourceComponent = paramWidgets; + sendToScript(currentIndex) + } } onCurrentIndexChanged: { - // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component + // This is a hack to be sure the widgets below properly reflect the change of index: delete the Component // by setting the loader source to Null and then recreate it 100ms later - root.drawConfig["group"] = currentIndex + root.drawConfig = Render.getConfig("RenderMainView.OutlineEffect"+currentIndex) paramWidgetLoader.sourceComponent = undefined; postpone.interval = 100 postpone.start()