From c490de022cad0e9cdb8c6c56ba454b6edcf5755b Mon Sep 17 00:00:00 2001 From: assiduous Date: Fri, 8 Nov 2024 18:27:57 -0800 Subject: [PATCH] PBR Renderer and Hydrogent: added option to pack vertex positions into two 32-bit uints --- Hydrogent/include/HnMeshUtils.hpp | 7 +++ Hydrogent/interface/HnMesh.hpp | 6 ++- Hydrogent/interface/HnRenderDelegate.hpp | 3 ++ Hydrogent/src/HnGeometryPool.cpp | 3 +- Hydrogent/src/HnMesh.cpp | 40 ++++++++++---- Hydrogent/src/HnMeshUtils.cpp | 66 ++++++++++++++++++++---- Hydrogent/src/HnRenderDelegate.cpp | 24 ++++----- Hydrogent/src/HnRenderPass.cpp | 14 ++--- PBR/interface/GLTF_PBR_Renderer.hpp | 2 + PBR/interface/PBR_Renderer.hpp | 45 +++++++++++++++- PBR/src/GLTF_PBR_Renderer.cpp | 10 ++++ PBR/src/PBR_Renderer.cpp | 29 +++++------ Shaders/PBR/private/RenderPBR.vsh | 47 ++++++++++++++--- Shaders/PBR/public/PBR_Structures.fxh | 9 +++- 14 files changed, 238 insertions(+), 67 deletions(-) diff --git a/Hydrogent/include/HnMeshUtils.hpp b/Hydrogent/include/HnMeshUtils.hpp index 5dfb8e8b..fc5224c2 100644 --- a/Hydrogent/include/HnMeshUtils.hpp +++ b/Hydrogent/include/HnMeshUtils.hpp @@ -166,6 +166,13 @@ class HnMeshUtils final /// Pack vertex normals into 32-bit unsigned integers. pxr::VtValue PackVertexNormals(const pxr::VtValue& Normals) const; + + /// Pack positions into two 32-bit unsigned integers. + /// The positions are scaled and biased to fit into the range [0, 1]. + /// The original positions can be recovered using the following formula: + /// Position = (PackedPosition * Scale) + Bias + pxr::VtValue PackVertexPositions(const pxr::VtValue& Points, pxr::GfVec3f& Scale, pxr::GfVec3f& Bias) const; + private: template void ProcessFaces(HandleFaceType&& HandleFace) const; diff --git a/Hydrogent/interface/HnMesh.hpp b/Hydrogent/interface/HnMesh.hpp index bed25e5a..c6477a1d 100644 --- a/Hydrogent/interface/HnMesh.hpp +++ b/Hydrogent/interface/HnMesh.hpp @@ -95,7 +95,9 @@ class HnMesh final : public pxr::HdMesh { struct Transform { - float4x4 Val = float4x4::Identity(); + float4x4 Matrix = float4x4::Identity(); + float3 PosScale = {1, 1, 1}; + float3 PosBias = {0, 0, 0}; }; struct DisplayColor @@ -204,6 +206,8 @@ class HnMesh final : public pxr::HdMesh pxr::HdInterpolation Interpolation, int ValuesPerElement = 1); + void PreprocessPrimvar(HnRenderDelegate* RenderDelegate, const pxr::TfToken& Name, pxr::VtValue& Primvar); + bool AddJointInfluencesStagingBufferSource(const pxr::VtValue& NumInfluencesPerComponentVal, const pxr::VtValue& InfluencesVal, StagingVertexData& StagingVerts); diff --git a/Hydrogent/interface/HnRenderDelegate.hpp b/Hydrogent/interface/HnRenderDelegate.hpp index 35f17198..c6bd5275 100644 --- a/Hydrogent/interface/HnRenderDelegate.hpp +++ b/Hydrogent/interface/HnRenderDelegate.hpp @@ -184,6 +184,9 @@ class HnRenderDelegate final : public pxr::HdRenderDelegate /// Whether to pack vertex normals into a 32-bit uint. bool PackVertexNormals = false; + /// Whether to pack vertex positions into two 32-bit uints. + bool PackVertexPositions = false; + /// When shadows are enabled, the size of the PCF kernel. /// Allowed values are 2, 3, 5, 7. Uint32 PCFKernelSize = 3; diff --git a/Hydrogent/src/HnGeometryPool.cpp b/Hydrogent/src/HnGeometryPool.cpp index d334da20..8866789c 100644 --- a/Hydrogent/src/HnGeometryPool.cpp +++ b/Hydrogent/src/HnGeometryPool.cpp @@ -188,7 +188,8 @@ class HnGeometryPool::VertexData final : public GeometryPoolData const size_t ElementSize = HdDataSizeOfType(ElementType.type) * ElementType.count; if (SourceName == pxr::HdTokens->points) { - VERIFY(ElementType.type == pxr::HdTypeFloatVec3 && ElementType.count == 1, "Unexpected vertex element type"); + VERIFY((ElementType.type == pxr::HdTypeFloatVec3 || ElementType.type == pxr::HdTypeInt32Vec2) && ElementType.count == 1, + "Unexpected vertex element type"); } else if (SourceName == pxr::HdTokens->normals) { diff --git a/Hydrogent/src/HnMesh.cpp b/Hydrogent/src/HnMesh.cpp index 2db29c99..e18be0f3 100644 --- a/Hydrogent/src/HnMesh.cpp +++ b/Hydrogent/src/HnMesh.cpp @@ -450,7 +450,7 @@ void HnMesh::UpdateRepr(pxr::HdSceneDelegate& SceneDelegate, if (pxr::HdChangeTracker::IsTransformDirty(DirtyBits, Id)) { entt::registry& Registry = RenderDelegate->GetEcsRegistry(); - float4x4& Transform = Registry.get(m_Entity).Val; + float4x4& Transform = Registry.get(m_Entity).Matrix; float4x4 NewTransform = m_SkelLocalToPrimLocal * ToFloat4x4(SceneDelegate.GetTransform(Id)); if (Transform != NewTransform) @@ -528,6 +528,34 @@ void HnMesh::UpdateTopology(pxr::HdSceneDelegate& SceneDelegate, DirtyBits &= ~pxr::HdChangeTracker::DirtyTopology; } +void HnMesh::PreprocessPrimvar(HnRenderDelegate* RenderDelegate, const pxr::TfToken& Name, pxr::VtValue& Primvar) +{ + if (Name == pxr::HdTokens->points) + { + VERIFY_EXPR(RenderDelegate != nullptr); + if (RenderDelegate != nullptr && RenderDelegate->GetUSDRenderer()->GetSettings().VertexPosPackMode == PBR_Renderer::VERTEX_POS_PACK_MODE_64_BIT) + { + HnMeshUtils MeshUtils{m_Topology, GetId()}; + pxr::GfVec3f UnpackScale, UnpackBias; + Primvar = MeshUtils.PackVertexPositions(Primvar, UnpackScale, UnpackBias); + + entt::registry& Registry = RenderDelegate->GetEcsRegistry(); + Components::Transform& Transform = Registry.get(m_Entity); + Transform.PosScale = ToFloat3(UnpackScale); + Transform.PosBias = ToFloat3(UnpackBias); + } + } + else if (Name == pxr::HdTokens->normals) + { + VERIFY_EXPR(RenderDelegate != nullptr); + if (RenderDelegate != nullptr && RenderDelegate->GetUSDRenderer()->GetSettings().PackVertexNormals) + { + HnMeshUtils MeshUtils{m_Topology, GetId()}; + Primvar = MeshUtils.PackVertexNormals(Primvar); + } + } +} + bool HnMesh::AddStagingBufferSourceForPrimvar(HnRenderDelegate* RenderDelegate, StagingVertexData& StagingVerts, const pxr::TfToken& Name, @@ -538,15 +566,7 @@ bool HnMesh::AddStagingBufferSourceForPrimvar(HnRenderDelegate* RenderDelegat if (Primvar.IsEmpty()) return false; - if (Name == pxr::HdTokens->normals) - { - VERIFY_EXPR(RenderDelegate != nullptr); - if (RenderDelegate != nullptr && RenderDelegate->GetUSDRenderer()->GetSettings().PackVertexNormals) - { - HnMeshUtils MeshUtils{m_Topology, GetId()}; - Primvar = MeshUtils.PackVertexNormals(Primvar); - } - } + PreprocessPrimvar(RenderDelegate, Name, Primvar); pxr::VtValue FaceVaryingPrimvar; pxr::VtValue* pSrcPrimvar = &Primvar; diff --git a/Hydrogent/src/HnMeshUtils.cpp b/Hydrogent/src/HnMeshUtils.cpp index 8bda01f8..1541d3b4 100644 --- a/Hydrogent/src/HnMeshUtils.cpp +++ b/Hydrogent/src/HnMeshUtils.cpp @@ -378,22 +378,66 @@ pxr::VtValue HnMeshUtils::ConvertVertexPrimvarToFaceVarying(const pxr::VtValue& pxr::VtValue HnMeshUtils::PackVertexNormals(const pxr::VtValue& Normals) const { - if (Normals.IsHolding()) + if (!Normals.IsHolding()) { - const pxr::VtVec3fArray& NormalsArray = Normals.UncheckedGet(); - pxr::VtIntArray PackedNormals(NormalsArray.size()); - Uint32* pPackedNormals = reinterpret_cast(PackedNormals.data()); - for (size_t i = 0; i < NormalsArray.size(); ++i) - { - pPackedNormals[i] = PBR_Renderer::PackVertexNormal(ToFloat3(NormalsArray[i])); - } - return pxr::VtValue::Take(PackedNormals); + LOG_ERROR_MESSAGE("Failed to pack vertex normals for mesh '", m_MeshId.GetString(), "': ", Normals.GetTypeName(), " is not supported"); + return {}; } - else + + const pxr::VtVec3fArray& NormalsArray = Normals.UncheckedGet(); + pxr::VtIntArray PackedNormals(NormalsArray.size()); + Uint32* pPackedNormals = reinterpret_cast(PackedNormals.data()); + for (size_t i = 0; i < NormalsArray.size(); ++i) { - LOG_ERROR_MESSAGE("Failed to pack vertex normals for mesh '", m_MeshId.GetString(), "': ", Normals.GetTypeName(), " is not supported"); + pPackedNormals[i] = PBR_Renderer::PackVertexNormal(ToFloat3(NormalsArray[i])); + } + return pxr::VtValue::Take(PackedNormals); +} + +pxr::VtValue HnMeshUtils::PackVertexPositions(const pxr::VtValue& Points, pxr::GfVec3f& Scale, pxr::GfVec3f& Bias) const +{ + if (!Points.IsHolding()) + { + LOG_ERROR_MESSAGE("Failed to pack vertex positions for mesh '", m_MeshId.GetString(), "': ", Points.GetTypeName(), " is not supported"); return {}; } + + const pxr::VtVec3fArray& PointsArray = Points.UncheckedGet(); + + pxr::GfVec3f MinPos{FLT_MAX}; + pxr::GfVec3f MaxPos{-FLT_MAX}; + for (const pxr::GfVec3f& Pos : PointsArray) + { + MinPos[0] = std::min(MinPos[0], Pos[0]); + MinPos[1] = std::min(MinPos[1], Pos[1]); + MinPos[2] = std::min(MinPos[2], Pos[2]); + MaxPos[0] = std::max(MaxPos[0], Pos[0]); + MaxPos[1] = std::max(MaxPos[1], Pos[1]); + MaxPos[2] = std::max(MaxPos[2], Pos[2]); + } + Bias = MinPos; + Scale = MaxPos - MinPos; + + const float3 PackScale{ + Scale[0] != 0.f ? 1.f / Scale[0] : 1.f, + Scale[1] != 0.f ? 1.f / Scale[1] : 1.f, + Scale[2] != 0.f ? 1.f / Scale[2] : 1.f, + }; + const float3 PackBias{ + -MinPos[0], + -MinPos[1], + -MinPos[2], + }; + + pxr::VtVec2iArray PackedPositions(PointsArray.size()); + uint2* pPackedPositions = reinterpret_cast(PackedPositions.data()); + const float3* pPoints = reinterpret_cast(PointsArray.data()); + for (size_t i = 0; i < PointsArray.size(); ++i) + { + PBR_Renderer::PackVertexPos64(pPoints[i], PackBias, PackScale, pPackedPositions[i].x, pPackedPositions[i].y); + } + + return pxr::VtValue::Take(PackedPositions); } } // namespace USD diff --git a/Hydrogent/src/HnRenderDelegate.cpp b/Hydrogent/src/HnRenderDelegate.cpp index aa29bd2b..37cbf93c 100644 --- a/Hydrogent/src/HnRenderDelegate.cpp +++ b/Hydrogent/src/HnRenderDelegate.cpp @@ -175,6 +175,9 @@ static std::shared_ptr CreateUSDRenderer(const HnRenderDelegate::C USDRendererCI.AllowHotShaderReload = RenderDelegateCI.AllowHotShaderReload; USDRendererCI.PackVertexNormals = RenderDelegateCI.PackVertexNormals; + USDRendererCI.VertexPosPackMode = RenderDelegateCI.PackVertexPositions ? + USD_Renderer::VERTEX_POS_PACK_MODE_64_BIT : + USD_Renderer::VERTEX_POS_PACK_MODE_NONE; const RenderDeviceInfo& DeviceInfo = RenderDelegateCI.pDevice->GetDeviceInfo(); // There is a major performance degradation when using row-major matrices @@ -253,21 +256,18 @@ static std::shared_ptr CreateUSDRenderer(const HnRenderDelegate::C USDRendererCI.ShaderTexturesArrayMode = USD_Renderer::SHADER_TEXTURE_ARRAY_MODE_NONE; } - // float3 Normal : ATTRIB1; - // or - // uint Normal : ATTRIB1; - const LayoutElement NormalInput{ - USD_Renderer::VERTEX_ATTRIB_ID_NORMAL, - HnRenderPass::VERTEX_BUFFER_SLOT_NORMALS, - USDRendererCI.PackVertexNormals ? 1u : 3u, - USDRendererCI.PackVertexNormals ? VT_UINT32 : VT_FLOAT32, - false, // IsNormalized - }; + // clang-format off + constexpr LayoutElement NormalInput {USD_Renderer::VERTEX_ATTRIB_ID_NORMAL, HnRenderPass::VERTEX_BUFFER_SLOT_NORMALS, 3, VT_FLOAT32}; // float3 Normal : ATTRIB1; + constexpr LayoutElement NormalInputPacked{USD_Renderer::VERTEX_ATTRIB_ID_NORMAL, HnRenderPass::VERTEX_BUFFER_SLOT_NORMALS, 1, VT_UINT32, false}; // uint Normal : ATTRIB1; + constexpr LayoutElement PosInput {USD_Renderer::VERTEX_ATTRIB_ID_POSITION, HnRenderPass::VERTEX_BUFFER_SLOT_POSITIONS, 3, VT_FLOAT32}; // float3 Pos : ATTRIB0; + constexpr LayoutElement PosInputPacked64 {USD_Renderer::VERTEX_ATTRIB_ID_POSITION, HnRenderPass::VERTEX_BUFFER_SLOT_POSITIONS, 2, VT_UINT32, false}; // uint2 Pos : ATTRIB0; + // clang-format om + const LayoutElement Inputs[] = { // clang-format off - {USD_Renderer::VERTEX_ATTRIB_ID_POSITION, HnRenderPass::VERTEX_BUFFER_SLOT_POSITIONS, 3, VT_FLOAT32}, // float3 Pos : ATTRIB0; - NormalInput, + USDRendererCI.VertexPosPackMode == USD_Renderer::VERTEX_POS_PACK_MODE_64_BIT ? PosInputPacked64 : PosInput, + USDRendererCI.PackVertexNormals ? NormalInputPacked : NormalInput, {USD_Renderer::VERTEX_ATTRIB_ID_TEXCOORD0, HnRenderPass::VERTEX_BUFFER_SLOT_TEX_COORDS0, 2, VT_FLOAT32}, // float2 UV0 : ATTRIB2; {USD_Renderer::VERTEX_ATTRIB_ID_TEXCOORD1, HnRenderPass::VERTEX_BUFFER_SLOT_TEX_COORDS1, 2, VT_FLOAT32}, // float2 UV1 : ATTRIB3; {USD_Renderer::VERTEX_ATTRIB_ID_COLOR, HnRenderPass::VERTEX_BUFFER_SLOT_VERTEX_COLORS, 3, VT_FLOAT32}, // float3 Color : ATTRIB6; diff --git a/Hydrogent/src/HnRenderPass.cpp b/Hydrogent/src/HnRenderPass.cpp index 3f4dfe10..2d654e5f 100644 --- a/Hydrogent/src/HnRenderPass.cpp +++ b/Hydrogent/src/HnRenderPass.cpp @@ -91,7 +91,7 @@ HnRenderPass::DrawListItem::DrawListItem(HnRenderDelegate& RenderDelegate, NumVertexBuffers{0} { entt::registry& Registry = RenderDelegate.GetEcsRegistry(); - PrevTransform = Registry.get(MeshEntity).Val; + PrevTransform = Registry.get(MeshEntity).Matrix; } HnRenderPass::HnRenderPass(pxr::HdRenderIndex* pIndex, @@ -504,9 +504,9 @@ HnRenderPass::EXECUTE_RESULT HnRenderPass::Execute(HnRenderPassState& RPState, c const HnMesh::Components::DisplayColor, const HnMesh::Components::Visibility>(ListItem.MeshEntity); - const float4x4& Transform = std::get<0>(MeshAttribs).Val; - const float4& DisplayColor = std::get<1>(MeshAttribs).Val; - const bool MeshVisibile = std::get<2>(MeshAttribs).Val; + const HnMesh::Components::Transform& Transform = std::get<0>(MeshAttribs); + const float4& DisplayColor = std::get<1>(MeshAttribs).Val; + const bool MeshVisibile = std::get<2>(MeshAttribs).Val; const HnMesh::Components::Skinning* pSkinningData = ((ListItem.PSOFlags & PBR_Renderer::PSO_FLAG_USE_JOINTS) && pJointsCB != nullptr) ? &MeshAttribsView.get(ListItem.MeshEntity) : @@ -656,10 +656,12 @@ HnRenderPass::EXECUTE_RESULT HnRenderPass::Execute(HnRenderPassState& RPState, c GLTF_PBR_Renderer::PBRPrimitiveShaderAttribsData AttribsData{ ListItem.PSOFlags, - &Transform, + &Transform.Matrix, &ListItem.PrevTransform, JointCount, FirstJoint, + &Transform.PosScale, + &Transform.PosBias, nullptr, // CustomData 0, // CustomDataSize &pDstMaterialBasicAttribs, @@ -676,7 +678,7 @@ HnRenderPass::EXECUTE_RESULT HnRenderPass::Execute(HnRenderPassState& RPState, c // Using PBRPrimitiveShaderAttribs's CustomData will not work as fallback PSO uses different flags. pDstMaterialBasicAttribs->CustomData.x = ListItem.MeshUID; - ListItem.PrevTransform = Transform; + ListItem.PrevTransform = Transform.Matrix; m_PendingDrawItems.push_back(PendingDrawItem{ ListItem, diff --git a/PBR/interface/GLTF_PBR_Renderer.hpp b/PBR/interface/GLTF_PBR_Renderer.hpp index 4e557602..3a911d31 100644 --- a/PBR/interface/GLTF_PBR_Renderer.hpp +++ b/PBR/interface/GLTF_PBR_Renderer.hpp @@ -255,6 +255,8 @@ class GLTF_PBR_Renderer : public PBR_Renderer const float4x4* PrevNodeMatrix = nullptr; const Uint32 JointCount = 0; const Uint32 FirstJoint = 0; + const float3* PosScale = nullptr; + const float3* PosBias = nullptr; const void* CustomData = nullptr; size_t CustomDataSize = 0; diff --git a/PBR/interface/PBR_Renderer.hpp b/PBR/interface/PBR_Renderer.hpp index f662d20a..eb8ab7d4 100644 --- a/PBR/interface/PBR_Renderer.hpp +++ b/PBR/interface/PBR_Renderer.hpp @@ -128,6 +128,17 @@ class PBR_Renderer JOINTS_BUFFER_MODE_STRUCTURED, }; + /// Vertex position packing mode. + enum VERTEX_POS_PACK_MODE : Uint8 + { + /// Vertex positions are not packed and are stored as float3. + VERTEX_POS_PACK_MODE_NONE = 0, + + /// Vertex positions are packed into two 32-bit uints using + /// 21 bits for normalized x, y, z coordinates, see PackVertxPos64(). + VERTEX_POS_PACK_MODE_64_BIT, + }; + /// Renderer create info struct CreateInfo { @@ -205,6 +216,9 @@ class PBR_Renderer /// Whether vertex normals are packed into a single 32-bit uint, see PackVertexNormal(). bool PackVertexNormals = false; + /// Vertex position packing mode, see VERTEX_POS_PACK_MODE. + VERTEX_POS_PACK_MODE VertexPosPackMode = VERTEX_POS_PACK_MODE_NONE; + /// PCF shadow kernel size. /// Allowed values are 2, 3, 5, 7. Uint32 PCFKernelSize = 3; @@ -727,7 +741,15 @@ class PBR_Renderer /// Packs normal into a single 32-bit uint. /// /// \remarks The function assumes that the input vector is normalized. - static Uint32 PackVertexNormal(const float3& Normal); + static inline Uint32 PackVertexNormal(const float3& Normal); + + /// Packs vertex position into two 32-bit uints. + /// + /// \remarks Bias and Scale are used to map the vertex position to the [0, 1] range as follows: + /// NormPos = (Pos + Bias) * Scale + /// Typically, Bias is set to the negated minimum vertex position and Scale is set to + /// one over the maximum vertex position minus the minimum vertex position. + static inline void PackVertexPos64(const float3& Pos, const float3& Bias, const float3& Scale, Uint32& U0, Uint32& U1); protected: ShaderMacroHelper DefineMacros(const PSOKey& Key) const; @@ -906,4 +928,25 @@ inline void PBR_Renderer::ProcessTexturAttribs(PBR_Renderer::PSO_FLAGS PSOFlags, } } +inline Uint32 PBR_Renderer::PackVertexNormal(const float3& Normal) +{ + Uint32 x = static_cast(clamp((Normal.x + 1.f) * 32767.f, 0.f, 65535.f)); + Uint32 y = static_cast(clamp((Normal.y + 1.f) * 16383.f, 0.f, 32767.f)); + Uint32 z = Normal.z >= 0 ? 0 : 1; + return x | (y << 16) | (z << 31); +} + +inline void PBR_Renderer::PackVertexPos64(const float3& Pos, const float3& Bias, const float3& Scale, Uint32& U0, Uint32& U1) +{ + // X Y Y Z + // | 0 ... 20 | 21 ... 31| | 0 ... 9 | 10 ... 30 | + // 21 11 10 21 + constexpr float3 U21Scale{static_cast((1 << 21) - 1)}; + const float3 NormPos = (Pos + Bias) * Scale; + const uint3 U21Pos = clamp(NormPos * U21Scale, float3{0}, U21Scale).Recast(); + + U0 = U21Pos.x | (U21Pos.y << 21u); + U1 = (U21Pos.y >> 11u) | (U21Pos.z << 10u); +} + } // namespace Diligent diff --git a/PBR/src/GLTF_PBR_Renderer.cpp b/PBR/src/GLTF_PBR_Renderer.cpp index 069af9ab..1fb803b9 100644 --- a/PBR/src/GLTF_PBR_Renderer.cpp +++ b/PBR/src/GLTF_PBR_Renderer.cpp @@ -750,6 +750,16 @@ void* GLTF_PBR_Renderer::WritePBRPrimitiveShaderAttribs(void* pDstTransforms->JointCount = static_cast(AttribsData.JointCount); pDstTransforms->FirstJoint = static_cast(AttribsData.FirstJoint); + const float3& PosScale = AttribsData.PosScale != nullptr ? *AttribsData.PosScale : float3{1, 1, 1}; + pDstTransforms->PosScaleX = PosScale.x; + pDstTransforms->PosScaleY = PosScale.y; + pDstTransforms->PosScaleZ = PosScale.z; + + const float3& PosBias = AttribsData.PosBias != nullptr ? *AttribsData.PosBias : float3{0, 0, 0}; + pDstTransforms->PosBiasX = PosBias.x; + pDstTransforms->PosBiasY = PosBias.y; + pDstTransforms->PosBiasZ = PosBias.z; + static_assert(sizeof(HLSL::GLTFNodeShaderTransforms) % 16 == 0, "Size of HLSL::GLTFNodeShaderTransforms must be a multiple of 16"); pDstPtr += sizeof(HLSL::GLTFNodeShaderTransforms); } diff --git a/PBR/src/PBR_Renderer.cpp b/PBR/src/PBR_Renderer.cpp index c38b1e9f..1d58418b 100644 --- a/PBR/src/PBR_Renderer.cpp +++ b/PBR/src/PBR_Renderer.cpp @@ -1178,6 +1178,10 @@ ShaderMacroHelper PBR_Renderer::DefineMacros(const PSOKey& Key) const Macros.Add("PACK_VERTEX_NORMALS", m_Settings.PackVertexNormals); Macros.Add("TONE_MAPPING_MODE", "TONE_MAPPING_MODE_UNCHARTED2"); + Macros.Add("VERTEX_POS_PACK_MODE", static_cast(m_Settings.VertexPosPackMode)); + Macros.Add("VERTEX_POS_PACK_MODE_NONE", static_cast(VERTEX_POS_PACK_MODE_NONE)); + Macros.Add("VERTEX_POS_PACK_MODE_64_BIT", static_cast(VERTEX_POS_PACK_MODE_64_BIT)); + Macros.Add("PRIMITIVE_ARRAY_SIZE", static_cast(m_Settings.PrimitiveArraySize)); if (m_Settings.PrimitiveArraySize > 0) { @@ -1448,19 +1452,18 @@ void PBR_Renderer::GetVSInputStructAndLayout(PSO_FLAGS PSOFlags, } } - const VSAttribInfo VSNormalAttrib{ - VERTEX_ATTRIB_ID_NORMAL, - "Normal", - m_Settings.PackVertexNormals ? VT_UINT32 : VT_FLOAT32, - m_Settings.PackVertexNormals ? 1u : 3u, - PSO_FLAG_USE_VERTEX_NORMALS, - }; + // clang-format off + constexpr VSAttribInfo VSPosAttrib {VERTEX_ATTRIB_ID_POSITION, "Pos", VT_FLOAT32, 3, PSO_FLAG_NONE}; + constexpr VSAttribInfo VSPosPack64Attrib{VERTEX_ATTRIB_ID_POSITION, "Pos", VT_UINT32, 2, PSO_FLAG_NONE}; + constexpr VSAttribInfo VSNormAttrib {VERTEX_ATTRIB_ID_NORMAL, "Normal", VT_FLOAT32, 3, PSO_FLAG_NONE}; + constexpr VSAttribInfo VSNormPackAttrib {VERTEX_ATTRIB_ID_NORMAL, "Normal", VT_UINT32, 1, PSO_FLAG_NONE}; + // clang-format on const std::array VSAttribs = // { // clang-format off - VSAttribInfo{VERTEX_ATTRIB_ID_POSITION, "Pos", VT_FLOAT32, 3, PSO_FLAG_NONE}, - VSNormalAttrib, + m_Settings.VertexPosPackMode == VERTEX_POS_PACK_MODE_64_BIT ? VSPosPack64Attrib : VSPosAttrib, + m_Settings.PackVertexNormals ? VSNormPackAttrib : VSNormAttrib, VSAttribInfo{VERTEX_ATTRIB_ID_TEXCOORD0, "UV0", VT_FLOAT32, 2, PSO_FLAG_USE_TEXCOORD0}, VSAttribInfo{VERTEX_ATTRIB_ID_TEXCOORD1, "UV1", VT_FLOAT32, 2, PSO_FLAG_USE_TEXCOORD1}, VSAttribInfo{VERTEX_ATTRIB_ID_JOINTS, "Joint0", VT_FLOAT32, 4, PSO_FLAG_USE_JOINTS}, @@ -2116,12 +2119,4 @@ void* PBR_Renderer::WriteSkinningData(void* pDst, const WriteSkinningDataAttribs return WriteSkinningData(pDst, Attribs, PackMatrixRowMajor, m_Settings.MaxJointCount, m_Settings.UseSkinPreTransform); } -Uint32 PBR_Renderer::PackVertexNormal(const float3& Normal) -{ - Uint32 x = static_cast(clamp((Normal.x + 1.f) * 32767.f, 0.f, 65535.f)); - Uint32 y = static_cast(clamp((Normal.y + 1.f) * 16383.f, 0.f, 32767.f)); - Uint32 z = Normal.z >= 0 ? 0 : 1; - return x | (y << 16) | (z << 31); -} - } // namespace Diligent diff --git a/Shaders/PBR/private/RenderPBR.vsh b/Shaders/PBR/private/RenderPBR.vsh index d6cddcd7..bb7b39dd 100644 --- a/Shaders/PBR/private/RenderPBR.vsh +++ b/Shaders/PBR/private/RenderPBR.vsh @@ -140,6 +140,7 @@ float4 GetVertexColor(float4 Color) } #if PACK_VERTEX_NORMALS +// Reverse of PBR_Renderer::PackVertexNormal() float3 GetNormal(in uint PackedNormal) { float3 Normal; @@ -156,21 +157,53 @@ float3 GetNormal(in float3 Normal) } #endif +#if VERTEX_POS_PACK_MODE == VERTEX_POS_PACK_MODE_64_BIT +// Reverse of PBR_Renderer::PackVertexPos64() +float3 GetPosition(VSInput VSIn) +{ + uint2 PackedPos = VSIn.Pos; + + // PackedPos.x PackedPos.y + // X Y Y Z + // | 0 ... 20 | 21 ... 31| | 0 ... 9 | 10 ... 30 | | + // 21 11 10 21 31 + float U21Scale = 1.0 / 2097151.0; // 2^21 - 1 + uint U21Mask = 2097151u; + + float3 Pos; + Pos.x = float(PackedPos.x & U21Mask) * U21Scale; + Pos.y = float((PackedPos.x >> 21u) | ((PackedPos.y & 1023u) << 11u)) * U21Scale; + Pos.z = float(PackedPos.y >> 10u) * U21Scale; + + GLTFNodeShaderTransforms PrimTransforms = PRIMITIVE.Transforms; + float3 PosScale = float3(PrimTransforms.PosScaleX, PrimTransforms.PosScaleY, PrimTransforms.PosScaleZ); + float3 PosBias = float3(PrimTransforms.PosBiasX, PrimTransforms.PosBiasY, PrimTransforms.PosBiasZ); + return Pos * PosScale + PosBias; +} +#else +float3 GetPosition(VSInput VSIn) +{ + return VSIn.Pos; +} +#endif + void main(in VSInput VSIn, out VSOutput VSOut) { + PBRPrimitiveAttribs Primitive = PRIMITIVE; + // Warning: moving this block into GLTF_TransformVertex() function causes huge // performance degradation on Vulkan because glslang/SPIRV-Tools are apparently not able // to eliminate the copy of g_Transforms structure. - float4x4 Transform = PRIMITIVE.Transforms.NodeMatrix; + float4x4 Transform = Primitive.Transforms.NodeMatrix; #if COMPUTE_MOTION_VECTORS - float4x4 PrevTransform = PRIMITIVE.PrevNodeMatrix; + float4x4 PrevTransform = Primitive.PrevNodeMatrix; #endif #if MAX_JOINT_COUNT > 0 && USE_JOINTS - int JointCount = PRIMITIVE.Transforms.JointCount; - int FirstJoint = PRIMITIVE.Transforms.FirstJoint; + int JointCount = Primitive.Transforms.JointCount; + int FirstJoint = Primitive.Transforms.FirstJoint; if (JointCount > 0) { // Mesh is skinned @@ -210,11 +243,13 @@ void main(in VSInput VSIn, float3 Normal = float3(0.0, 0.0, 1.0); #endif - GLTF_TransformedVertex TransformedVert = GLTF_TransformVertex(VSIn.Pos, Normal, Transform); + float3 Pos = GetPosition(VSIn); + + GLTF_TransformedVertex TransformedVert = GLTF_TransformVertex(Pos, Normal, Transform); VSOut.ClipPos = mul(float4(TransformedVert.WorldPos, 1.0), g_Frame.Camera.mViewProj); #if COMPUTE_MOTION_VECTORS - GLTF_TransformedVertex PrevTransformedVert = GLTF_TransformVertex(VSIn.Pos, Normal, PrevTransform); + GLTF_TransformedVertex PrevTransformedVert = GLTF_TransformVertex(Pos, Normal, PrevTransform); VSOut.PrevClipPos = mul(float4(PrevTransformedVert.WorldPos, 1.0), g_Frame.PrevCamera.mViewProj); #endif diff --git a/Shaders/PBR/public/PBR_Structures.fxh b/Shaders/PBR/public/PBR_Structures.fxh index b24b7107..0abccb4d 100644 --- a/Shaders/PBR/public/PBR_Structures.fxh +++ b/Shaders/PBR/public/PBR_Structures.fxh @@ -74,8 +74,13 @@ struct GLTFNodeShaderTransforms int JointCount; int FirstJoint; // Index of the first joint in the joints buffer to start from - float Dummy1; - float Dummy2; + float PosBiasX; // Bias to apply to the position + float PosBiasY; + + float PosBiasZ; // Scale and bias are used to unpack + float PosScaleX; // position (Pos = Pos * PosScale + PosBias) + float PosScaleY; // and are applied before the NodeMatrix. + float PosScaleZ; }; #ifdef CHECK_STRUCT_ALIGNMENT CHECK_STRUCT_ALIGNMENT(GLTFNodeShaderTransforms);