Skip to content

Commit

Permalink
Hydrogent: reworked selection outline with the Jump-Flood algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Nov 6, 2023
1 parent 2246fe7 commit ef8273a
Show file tree
Hide file tree
Showing 20 changed files with 892 additions and 88 deletions.
2 changes: 2 additions & 0 deletions Hydrogent/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ set(SOURCE
src/Tasks/HnRenderEnvMapTask.cpp
src/Tasks/HnPostProcessTask.cpp
src/Tasks/HnSetupSelectionDepthTask.cpp
src/Tasks/HnProcessSelectionTask.cpp
src/Tasks/HnReadRprimIdTask.cpp
src/Tasks/HnTaskManager.cpp
)
Expand Down Expand Up @@ -53,6 +54,7 @@ set(INTERFACE
interface/Tasks/HnRenderEnvMapTask.hpp
interface/Tasks/HnPostProcessTask.hpp
interface/Tasks/HnSetupSelectionDepthTask.hpp
interface/Tasks/HnProcessSelectionTask.hpp
interface/Tasks/HnReadRprimIdTask.hpp
interface/Tasks/HnTaskManager.hpp
)
Expand Down
15 changes: 10 additions & 5 deletions Hydrogent/interface/HnRenderPassState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,19 @@ class HnRenderPassState final : public pxr::HdRenderPassState
ITextureView* SelectionDepthDSV = nullptr;
ITextureView* DepthDSV = nullptr;

ITextureView* ClosestSelectedLocation0RTV = nullptr;
ITextureView* ClosestSelectedLocation1RTV = nullptr;

constexpr explicit operator bool() const
{
// clang-format off
return FinalColorRTV != nullptr &&
OffscreenColorRTV != nullptr &&
MeshIdRTV != nullptr &&
SelectionDepthDSV != nullptr &&
DepthDSV != nullptr;
return FinalColorRTV != nullptr &&
OffscreenColorRTV != nullptr &&
MeshIdRTV != nullptr &&
SelectionDepthDSV != nullptr &&
DepthDSV != nullptr &&
ClosestSelectedLocation0RTV != nullptr &&
ClosestSelectedLocation1RTV != nullptr;
// clang-format on
}
};
Expand Down
17 changes: 10 additions & 7 deletions Hydrogent/interface/HnTokens.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,16 @@ namespace USD
(nearestMipmapLinear) \
(nearestMipmapNearest)

#define HN_RENDER_RESOURCE_TOKENS \
(cameraAttribsBuffer) \
(lightAttribsBuffer) \
(offscreenColorTarget) \
(finalColorTarget) \
(meshIdTarget) \
(selectionDepthBuffer) \
#define HN_RENDER_RESOURCE_TOKENS \
(cameraAttribsBuffer) \
(lightAttribsBuffer) \
(offscreenColorTarget) \
(finalColorTarget) \
(meshIdTarget) \
(selectionDepthBuffer) \
(closestSelectedLocation0Target) \
(closestSelectedLocation1Target) \
(closestSelectedLocationFinalTarget) \
(depthBuffer)

// clang-format on
Expand Down
11 changes: 6 additions & 5 deletions Hydrogent/interface/Tasks/HnPostProcessTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class HnPostProcessTask final : public HnTask

private:
void PreparePSO(TEXTURE_FORMAT RTVFormat);
void PrepareSRB(const HnRenderPassState& RPState);
void PrepareSRB(const HnRenderPassState& RPState, ITextureView* ClosestSelectedLocationSRV);

private:
pxr::HdRenderIndex* m_RenderIndex = nullptr;
Expand All @@ -118,13 +118,14 @@ class HnPostProcessTask final : public HnTask
RefCntAutoPtr<IShaderResourceBinding> m_SRB;
struct ShaderVariables
{
IShaderResourceVariable* Color = nullptr;
IShaderResourceVariable* Depth = nullptr;
IShaderResourceVariable* SelectionDepth = nullptr;
IShaderResourceVariable* Color = nullptr;
IShaderResourceVariable* Depth = nullptr;
IShaderResourceVariable* SelectionDepth = nullptr;
IShaderResourceVariable* ClosestSelectedLocation = nullptr;

constexpr operator bool() const
{
return Color != nullptr && Depth != nullptr && SelectionDepth != nullptr;
return Color != nullptr && Depth != nullptr && SelectionDepth != nullptr && ClosestSelectedLocation;
}
};
ShaderVariables m_ShaderVars;
Expand Down
141 changes: 141 additions & 0 deletions Hydrogent/interface/Tasks/HnProcessSelectionTask.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2023 Diligent Graphics LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/

#pragma once

#include <array>

#include "HnTask.hpp"

#include "../../../../DiligentCore/Graphics/GraphicsEngine/interface/PipelineState.h"
#include "../../../../DiligentCore/Graphics/GraphicsEngine/interface/ShaderResourceBinding.h"
#include "../../../../DiligentCore/Graphics/GraphicsEngine/interface/Buffer.h"
#include "../../../../DiligentCore/Common/interface/RefCntAutoPtr.hpp"
#include "../../../../DiligentCore/Common/interface/BasicMath.hpp"

namespace Diligent
{

namespace USD
{

struct HnProcessSelectionTaskParams
{
float MaximumDistance = 4.0f;

constexpr bool operator==(const HnProcessSelectionTaskParams& rhs) const
{
return MaximumDistance == rhs.MaximumDistance;
}
};

/// Processes selection depth buffer with the jump-flood algorithm.
// https://blog.demofox.org/2016/02/29/fast-voronoi-diagrams-and-distance-dield-textures-on-the-gpu-with-the-jump-flooding-algorithm/
// https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9
class HnProcessSelectionTask final : public HnTask
{
public:
HnProcessSelectionTask(pxr::HdSceneDelegate* ParamsDelegate, const pxr::SdfPath& Id);
~HnProcessSelectionTask();

virtual void Sync(pxr::HdSceneDelegate* Delegate,
pxr::HdTaskContext* TaskCtx,
pxr::HdDirtyBits* DirtyBits) override final;

virtual void Prepare(pxr::HdTaskContext* TaskCtx,
pxr::HdRenderIndex* PostProcessIndex) override final;


virtual void Execute(pxr::HdTaskContext* TaskCtx) override final;

private:
void PrepareTechniques(TEXTURE_FORMAT RTVFormat);
void PrepareSRBs(const HnRenderPassState& RPState);

private:
Uint32 m_NumJFIterations = 3;

pxr::HdRenderIndex* m_RenderIndex = nullptr;

RefCntAutoPtr<IBuffer> m_ConstantsCB;

// Processes selected objects depth buffer and for each valid location
// writes its coordinates into the output buffer.
struct InitClosestLocationTech
{
RefCntAutoPtr<IPipelineState> PSO;
RefCntAutoPtr<IShaderResourceBinding> SRB;
struct ShaderVariables
{
IShaderResourceVariable* SelectionDepth = nullptr;

constexpr explicit operator bool() const
{
return SelectionDepth != nullptr;
}
} Vars;

explicit operator bool() const
{
return PSO && SRB;
}

bool IsDirty = true;
} m_InitTech;

// Jump-flood algorithm iteration: samples the previous closest location
// with the specified offset and writes the updated closest location to the
// output buffer.
struct UpdateClosestLocationTech
{
RefCntAutoPtr<IPipelineState> PSO;
struct ShaderResources
{
RefCntAutoPtr<IShaderResourceBinding> SRB;
struct ShaderVariables
{
IShaderResourceVariable* SrcClosestLocation = nullptr;

constexpr explicit operator bool() const
{
return SrcClosestLocation != nullptr;
}
} Vars;
};
std::array<ShaderResources, 2> Res; // Ping-pong

explicit operator bool() const
{
return PSO && Res[0].SRB && Res[1].SRB;
}

bool IsDirty = true;
} m_UpdateTech;
};

} // namespace USD

} // namespace Diligent
11 changes: 8 additions & 3 deletions Hydrogent/interface/Tasks/HnSetupRenderingTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ struct HnSetupRenderingTaskParams
return !(*this == rhs);
}

TEXTURE_FORMAT ColorFormat = TEX_FORMAT_RGBA16_FLOAT;
TEXTURE_FORMAT MeshIdFormat = TEX_FORMAT_R32_FLOAT;
TEXTURE_FORMAT DepthFormat = TEX_FORMAT_D32_FLOAT;
TEXTURE_FORMAT ColorFormat = TEX_FORMAT_RGBA16_FLOAT;
TEXTURE_FORMAT MeshIdFormat = TEX_FORMAT_R32_FLOAT;
TEXTURE_FORMAT DepthFormat = TEX_FORMAT_D32_FLOAT;
TEXTURE_FORMAT ClosestSelectedLocationFormat = TEX_FORMAT_RG16_UNORM;

bool FrontFaceCCW = false;

Expand Down Expand Up @@ -142,6 +143,10 @@ class HnSetupRenderingTask final : public HnTask
pxr::SdfPath m_MeshIdTargetId;
pxr::SdfPath m_SelectionDepthBufferId;
pxr::SdfPath m_DepthBufferId;
pxr::SdfPath m_ClosestSelLocn0TargetId;
pxr::SdfPath m_ClosestSelLocn1TargetId;

TEXTURE_FORMAT m_ClosestSelectedLocationFormat = TEX_FORMAT_UNKNOWN;

pxr::HdRenderIndex* m_RenderIndex = nullptr;
};
Expand Down
5 changes: 5 additions & 0 deletions Hydrogent/interface/Tasks/HnTaskManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class HnTaskManager
static constexpr TaskUID TaskUID_RenderRprimsTranslucentSelected = 0x50a786394d834b4f;
static constexpr TaskUID TaskUID_RenderEnvMap = 0xf646122e1dc74bab;
static constexpr TaskUID TaskUID_ReadRprimId = 0x199572fe7ff144ef;
static constexpr TaskUID TaskUID_ProcessSelection = 0x87ef181ec6d4cf8;
static constexpr TaskUID TaskUID_PostProcess = 0x1f5367e65d034500;

HnTaskManager(pxr::HdRenderIndex& RenderIndex,
Expand Down Expand Up @@ -101,6 +102,8 @@ class HnTaskManager
/// - RenderRprimsTranslucentSelected
/// * Renders only selected Rprims with the translucent material tag (depth only)
/// - ReadRprimId
/// - ProcessSelection
/// * Generates the closest selected location texture using the Jump-Flood algorithm
/// - PostProcess
///
/// | Task | Selected Rprims | Unselected Rprims | Color | Mesh ID | Selection Detph | Main Depth |
Expand All @@ -118,6 +121,7 @@ class HnTaskManager
/// | RenderRprimsAdditiveSelected | V | | | | V | |
/// | RenderRprimsTranslucentSelected | V | | | | V | |
/// | ReadRprimId | | | | | | |
/// | ProcessSelection | | | | | | |
/// | PostProcess | | | | | | |
///
/// \return The list of tasks that can be passed to pxr::HdEngine::Execute.
Expand Down Expand Up @@ -197,6 +201,7 @@ class HnTaskManager
void CreateReadRprimIdTask();
void CreateCopySelectionDepthTask();
void CreateSetupSelectionDepthTask();
void CreateProcessSelectionTask();
void CreatePostProcessTask();

private:
Expand Down
44 changes: 44 additions & 0 deletions Hydrogent/shaders/HnClosestSelectedLocation.fxh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef _HN_CLOSEST_SELECTED_LOCATION_FXH_
#define _HN_CLOSEST_SELECTED_LOCATION_FXH_

struct ClosestSelectedLocationConstants
{
float ClearDepth;
float SampleRange;
float Padding1;
float Padding2;
};

#ifndef __cplusplus
float2 EncodeClosestSelectedLocation(float2 Location, bool IsValid)
{
if (IsValid)
{
Location.y = Location.y * 0.5 + 0.5;
return Location;
}
else
{
return float2(0.0, 0.0);
}
}

void DecodeClosestSelectedLocation(in float2 EncodedLocation,
out float2 Location,
out bool IsValid)
{
if (EncodedLocation.y <= 0.25)
{
IsValid = false;
Location = float2(0.0, 0.0);
}
else
{
Location = EncodedLocation;
Location.y = Location.y * 2.0 - 1.0;
IsValid = true;
}
}
#endif

#endif // _HN_CLOSEST_SELECTED_LOCATION_FXH_
23 changes: 23 additions & 0 deletions Hydrogent/shaders/HnInitClosestSelectedLocation.psh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "HnClosestSelectedLocation.fxh"

struct PSInput
{
float4 Pos : SV_POSITION;
};

cbuffer cbConstants
{
ClosestSelectedLocationConstants g_Constants;
}

Texture2D g_SelectionDepth;

void main(in PSInput PSIn,
out float2 Location : SV_Target0)
{
bool IsSelected = g_SelectionDepth.Load(int3(PSIn.Pos.xy, 0)).r != g_Constants.ClearDepth;
float Width;
float Height;
g_SelectionDepth.GetDimensions(Width, Height);
Location = EncodeClosestSelectedLocation(PSIn.Pos.xy / float2(Width, Height), IsSelected);
}
Loading

0 comments on commit ef8273a

Please sign in to comment.