From 9ed5185a3ea86e8be67dc3c5d698103694d1808d Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 17 Oct 2017 10:01:06 +0200 Subject: [PATCH] Multiple outline group config working --- libraries/render-utils/src/Outline.slh | 2 +- libraries/render-utils/src/OutlineEffect.cpp | 71 ++++--- libraries/render-utils/src/OutlineEffect.h | 121 ++++++----- libraries/render-utils/src/Outline_shared.slh | 4 +- .../developer/utilities/render/outline.qml | 188 ++++++++++-------- 5 files changed, 234 insertions(+), 152 deletions(-) diff --git a/libraries/render-utils/src/Outline.slh b/libraries/render-utils/src/Outline.slh index c58d1c7689..bc71cabf1e 100644 --- a/libraries/render-utils/src/Outline.slh +++ b/libraries/render-utils/src/Outline.slh @@ -53,7 +53,7 @@ vec4 computeGroupOutline(int centerGroupId, float centerDepth, int groupId, vec2 sceneDepth = -evalZeyeFromZdb(sceneDepth); // Are we occluded? - intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._fillOpacityOccluded : groupParams._fillOpacityUnoccluded; + intensity = (sceneDepth < (centerDepth-LINEAR_DEPTH_BIAS)) ? groupParams._occludedFillOpacity : groupParams._unoccludedFillOpacity; return vec4(groupParams._color.rgb, intensity); <@else@> return vec4(0,0,0,0); diff --git a/libraries/render-utils/src/OutlineEffect.cpp b/libraries/render-utils/src/OutlineEffect.cpp index 2cb19826f4..74a29e8237 100644 --- a/libraries/render-utils/src/OutlineEffect.cpp +++ b/libraries/render-utils/src/OutlineEffect.cpp @@ -215,6 +215,17 @@ void PrepareDrawOutline::run(const render::RenderContextPointer& renderContext, 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; gpu::PipelinePointer DrawOutline::_pipelineFilled; @@ -222,15 +233,34 @@ DrawOutline::DrawOutline() { } void DrawOutline::configure(const Config& config) { - _color = config.color; - _blurKernelSize = std::min(10, std::max(2, (int)floorf(config.width*2 + 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.f; - _fillOpacityUnoccluded = config.fillOpacityUnoccluded; - _fillOpacityOccluded = config.fillOpacityOccluded; - _threshold = config.glow ? 1.f : 1e-3f; - _intensity = config.intensity * (config.glow ? 2.f : 1.f); - _hasConfigurationChanged = true; + auto& configuration = _configuration.edit(); + const auto OPACITY_EPSILON = 5e-3f; + + bool someFilled = false; + bool isFilled; + + 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; + } } void DrawOutline::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { @@ -245,27 +275,20 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I auto framebufferSize = glm::ivec2(outlinedDepthTexture->getDimensions()); if (sceneDepthBuffer) { - const auto OPACITY_EPSILON = 5e-3f; - auto pipeline = getPipeline(_fillOpacityUnoccluded>OPACITY_EPSILON || _fillOpacityOccluded>OPACITY_EPSILON); + auto pipeline = getPipeline(); auto args = renderContext->args; - if (_hasConfigurationChanged) + if (_framebufferSize != framebufferSize) { auto& configuration = _configuration.edit(); for (auto groupId = 0; groupId < MAX_GROUP_COUNT; groupId++) { auto& groupConfig = configuration._groups[groupId]; - groupConfig._color = _color; - groupConfig._intensity = _intensity; - groupConfig._fillOpacityUnoccluded = _fillOpacityUnoccluded; - groupConfig._fillOpacityOccluded = _fillOpacityOccluded; - groupConfig._threshold = _threshold; - groupConfig._blurKernelSize = _blurKernelSize; - groupConfig._size.x = (_size * framebufferSize.y) / framebufferSize.x; - groupConfig._size.y = _size; + groupConfig._size.x = (_sizes[groupId] * framebufferSize.y) / framebufferSize.x; + groupConfig._size.y = _sizes[groupId]; } - _hasConfigurationChanged = false; + _framebufferSize = framebufferSize; } gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { @@ -292,7 +315,7 @@ void DrawOutline::run(const render::RenderContextPointer& renderContext, const I } } -const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { +const gpu::PipelinePointer& DrawOutline::getPipeline() { if (!_pipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); auto ps = gpu::Shader::createPixel(std::string(Outline_frag)); @@ -316,7 +339,7 @@ const gpu::PipelinePointer& DrawOutline::getPipeline(bool isFilled) { gpu::Shader::makeProgram(*program, slotBindings); _pipelineFilled = gpu::Pipeline::create(program, state); } - return isFilled ? _pipelineFilled : _pipeline; + return _mode == M_SOME_FILLED ? _pipelineFilled : _pipeline; } DebugOutline::DebugOutline() { @@ -480,7 +503,7 @@ void DrawOutlineTask::build(JobModel& task, const render::Varying& inputs, rende } DrawOutlineMask::Groups sortedBounds; - for (auto i = 0; i < DrawOutline::MAX_GROUP_COUNT; i++) { + for (auto i = 0; i < Outline::MAX_GROUP_COUNT; i++) { const auto groupItems = groups[i]; const auto outlinedItemIDs = task.addJob("OutlineMetaToSubItemIDs", groupItems); const auto outlinedItems = task.addJob("OutlineMetaToSubItems", outlinedItemIDs, true); diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 6e4c0a18a8..bc10fc5e41 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -60,41 +60,8 @@ private: }; -class DrawOutlineConfig : public render::Job::Config { - Q_OBJECT - 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 fillOpacityUnoccluded MEMBER fillOpacityUnoccluded NOTIFY dirty) - Q_PROPERTY(float fillOpacityOccluded MEMBER fillOpacityOccluded NOTIFY dirty) - -public: - - void setColorR(float value) { color.r = value; emit dirty(); } - float getColorR() const { return color.r; } - - void setColorG(float value) { color.g = value; emit dirty(); } - float getColorG() const { return color.g; } - - void setColorB(float value) { color.b = value; emit dirty(); } - float getColorB() const { return color.b; } - - glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 2.0f }; - float intensity{ 0.9f }; - float fillOpacityUnoccluded{ 0.0f }; - float fillOpacityOccluded{ 0.0f }; - bool glow{ false }; - -signals: - void dirty(); -}; - -class DrawOutline { -private: +class Outline { +protected: #include "Outline_shared.slh" @@ -102,6 +69,68 @@ public: enum { MAX_GROUP_COUNT = GROUP_COUNT }; +}; + +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(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) + +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 }; + }; + + int getGroupCount() const; + + void setColorR(float value) { groupParameters[group].color.r = value; emit dirty(); } + float getColorR() const { return groupParameters[group].color.r; } + + 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]; + +signals: + void dirty(); +}; + +class DrawOutline : public Outline { +public: using Inputs = render::VaryingSet4; using Config = DrawOutlineConfig; @@ -127,27 +156,27 @@ private: OutlineParameters _groups[MAX_GROUP_COUNT]; }; + enum Mode { + M_ALL_UNFILLED, + M_SOME_FILLED, + }; + using OutlineConfigurationBuffer = gpu::StructBuffer; - static const gpu::PipelinePointer& getPipeline(bool isFilled); + const gpu::PipelinePointer& getPipeline(); static gpu::PipelinePointer _pipeline; static gpu::PipelinePointer _pipelineFilled; OutlineConfigurationBuffer _configuration; - glm::vec3 _color; - float _size; - int _blurKernelSize; - float _intensity; - float _fillOpacityUnoccluded; - float _fillOpacityOccluded; - float _threshold; - bool _hasConfigurationChanged{ true }; + glm::ivec2 _framebufferSize{ 0,0 }; + Mode _mode{ M_ALL_UNFILLED }; + float _sizes[MAX_GROUP_COUNT]; }; class DrawOutlineTask { public: - using Groups = render::VaryingArray; + using Groups = render::VaryingArray; using Inputs = render::VaryingSet4; using Config = render::Task::Config; using JobModel = render::Task::ModelI; @@ -165,7 +194,7 @@ private: class DrawOutlineMask { public: - using Groups = render::VaryingArray; + 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; diff --git a/libraries/render-utils/src/Outline_shared.slh b/libraries/render-utils/src/Outline_shared.slh index 98d803c28b..bda67c49d6 100644 --- a/libraries/render-utils/src/Outline_shared.slh +++ b/libraries/render-utils/src/Outline_shared.slh @@ -19,8 +19,8 @@ struct OutlineParameters float _intensity; VEC2 _size; - float _fillOpacityUnoccluded; - float _fillOpacityOccluded; + float _unoccludedFillOpacity; + float _occludedFillOpacity; float _threshold; int _blurKernelSize; diff --git a/scripts/developer/utilities/render/outline.qml b/scripts/developer/utilities/render/outline.qml index 1b5cbf6e1c..1270c696d1 100644 --- a/scripts/developer/utilities/render/outline.qml +++ b/scripts/developer/utilities/render/outline.qml @@ -20,6 +20,24 @@ Item { Column { spacing: 8 + ComboBox { + id: groupBox + model: ["Group 0", "Group 1", "Group 2", "Group 3", "Group 4"] + Timer { + id: postpone + interval: 100; running: false; repeat: false + onTriggered: { paramWidgetLoader.sourceComponent = paramWidgets } + } + onCurrentIndexChanged: { + // This is a hack to be sure the widgets below properly reflect the change of category: delete the Component + // by setting the loader source to Null and then recreate it 100ms later + root.drawConfig["group"] = currentIndex + paramWidgetLoader.sourceComponent = undefined; + postpone.interval = 100 + postpone.start() + } + } + CheckBox { text: "View Mask" checked: root.debugConfig["viewMask"] @@ -27,93 +45,105 @@ Item { root.debugConfig["viewMask"] = checked; } } - CheckBox { - text: "Glow" - checked: root.drawConfig["glow"] - onCheckedChanged: { - root.drawConfig["glow"] = checked; - } - } - ConfigSlider { - label: "Width" - integral: false - config: root.drawConfig - property: "width" - max: 15.0 - min: 0.0 - width: 280 - } - ConfigSlider { - label: "Intensity" - integral: false - config: root.drawConfig - property: "intensity" - max: 1.0 - min: 0.0 - width: 280 - } - GroupBox { - title: "Color" - width: 280 + Component { + id: paramWidgets Column { spacing: 8 + CheckBox { + text: "Glow" + checked: root.drawConfig["glow"] + onCheckedChanged: { + drawConfig["glow"] = checked; + } + } + ConfigSlider { + label: "Width" + integral: false + config: root.drawConfig + property: "width" + max: 15.0 + min: 0.0 + width: 280 + } + ConfigSlider { + label: "Intensity" + integral: false + config: root.drawConfig + property: "intensity" + max: 1.0 + min: 0.0 + width: 280 + } - ConfigSlider { - label: "Red" - integral: false - config: root.drawConfig - property: "colorR" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Green" - integral: false - config: root.drawConfig - property: "colorG" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Blue" - integral: false - config: root.drawConfig - property: "colorB" - max: 1.0 - min: 0.0 - width: 270 + GroupBox { + title: "Color" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Red" + integral: false + config: root.drawConfig + property: "colorR" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Green" + integral: false + config: root.drawConfig + property: "colorG" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Blue" + integral: false + config: root.drawConfig + property: "colorB" + max: 1.0 + min: 0.0 + width: 270 + } + } + } + + GroupBox { + title: "Fill Opacity" + width: 280 + Column { + spacing: 8 + + ConfigSlider { + label: "Unoccluded" + integral: false + config: root.drawConfig + property: "fillOpacityUnoccluded" + max: 1.0 + min: 0.0 + width: 270 + } + ConfigSlider { + label: "Occluded" + integral: false + config: root.drawConfig + property: "fillOpacityOccluded" + max: 1.0 + min: 0.0 + width: 270 + } + } } } } - GroupBox { - title: "Fill Opacity" - width: 280 - Column { - spacing: 8 - - ConfigSlider { - label: "Unoccluded" - integral: false - config: root.drawConfig - property: "fillOpacityUnoccluded" - max: 1.0 - min: 0.0 - width: 270 - } - ConfigSlider { - label: "Occluded" - integral: false - config: root.drawConfig - property: "fillOpacityOccluded" - max: 1.0 - min: 0.0 - width: 270 - } - } + Loader { + id: paramWidgetLoader + sourceComponent: paramWidgets } } }