From 0dd567b161a79096876825a69dc8524a18beabc9 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 7 Nov 2024 22:33:55 -0800 Subject: [PATCH] PBR Renderer and Hydrogent: added option to pack vertex normals into 32-bit uint --- Hydrogent/include/HnMeshUtils.hpp | 4 +++ Hydrogent/interface/HnMesh.hpp | 5 +-- Hydrogent/interface/HnRenderDelegate.hpp | 3 ++ Hydrogent/src/HnGeometryPool.cpp | 11 +++++- Hydrogent/src/HnMesh.cpp | 44 ++++++++++++++++-------- Hydrogent/src/HnMeshUtils.cpp | 21 +++++++++++ Hydrogent/src/HnRenderDelegate.cpp | 15 ++++++-- PBR/interface/PBR_Renderer.hpp | 8 +++++ PBR/src/PBR_Renderer.cpp | 43 ++++++++++++++++++++--- Shaders/PBR/private/RenderPBR.vsh | 19 +++++++++- 10 files changed, 149 insertions(+), 24 deletions(-) diff --git a/Hydrogent/include/HnMeshUtils.hpp b/Hydrogent/include/HnMeshUtils.hpp index 3a9f425d..5dfb8e8b 100644 --- a/Hydrogent/include/HnMeshUtils.hpp +++ b/Hydrogent/include/HnMeshUtils.hpp @@ -162,6 +162,10 @@ class HnMeshUtils final /// pxr::VtValue ConvertVertexPrimvarToFaceVarying(const pxr::VtValue& VertexData, size_t ValuesPerVertex = 1) const; + + /// Pack vertex normals into 32-bit unsigned integers. + pxr::VtValue PackVertexNormals(const pxr::VtValue& Normals) const; + private: template void ProcessFaces(HandleFaceType&& HandleFace) const; diff --git a/Hydrogent/interface/HnMesh.hpp b/Hydrogent/interface/HnMesh.hpp index f298364d..bed25e5a 100644 --- a/Hydrogent/interface/HnMesh.hpp +++ b/Hydrogent/interface/HnMesh.hpp @@ -197,7 +197,8 @@ class HnMesh final : public pxr::HdMesh pxr::HdDirtyBits& DirtyBits, const pxr::TfToken& ReprToken); - bool AddStagingBufferSourceForPrimvar(StagingVertexData& StagingVerts, + bool AddStagingBufferSourceForPrimvar(HnRenderDelegate* RenderDelegate, + StagingVertexData& StagingVerts, const pxr::TfToken& Name, pxr::VtValue Primvar, pxr::HdInterpolation Interpolation, @@ -214,7 +215,7 @@ class HnMesh final : public pxr::HdMesh const pxr::HdExtComputationPrimvarDescriptor& SkinningCompPrimDesc, StagingVertexData& StagingVerts); - void GenerateSmoothNormals(StagingVertexData& StagingVerts); + void GenerateSmoothNormals(HnRenderDelegate& RenderDelegate, StagingVertexData& StagingVerts); struct GeometrySubsetRange { diff --git a/Hydrogent/interface/HnRenderDelegate.hpp b/Hydrogent/interface/HnRenderDelegate.hpp index e022170b..35f17198 100644 --- a/Hydrogent/interface/HnRenderDelegate.hpp +++ b/Hydrogent/interface/HnRenderDelegate.hpp @@ -181,6 +181,9 @@ class HnRenderDelegate final : public pxr::HdRenderDelegate /// has no effect and texture loading will be synchronous. bool AsyncTextureLoading = false; + /// Whether to pack vertex normals into a 32-bit uint. + bool PackVertexNormals = 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 5f9fc8c6..d334da20 100644 --- a/Hydrogent/src/HnGeometryPool.cpp +++ b/Hydrogent/src/HnGeometryPool.cpp @@ -187,13 +187,22 @@ class HnGeometryPool::VertexData final : public GeometryPoolData const pxr::HdTupleType ElementType = Source->GetTupleType(); 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"); + } else if (SourceName == pxr::HdTokens->normals) - VERIFY(ElementType.type == pxr::HdTypeFloatVec3 && ElementType.count == 1, "Unexpected normal element type"); + { + VERIFY((ElementType.type == pxr::HdTypeFloatVec3 || ElementType.type == pxr::HdTypeInt32) && ElementType.count == 1, + "Unexpected normal element type"); + } else if (SourceName == pxr::HdTokens->displayColor) + { VERIFY(ElementType.type == pxr::HdTypeFloatVec3 && ElementType.count == 1, "Unexpected vertex color element type"); + } else if (SourceName == HnTokens->joints) + { VERIFY(ElementType.type == pxr::HdTypeFloatVec4 && ElementType.count == 2, "Unexpected joints element type"); + } AddStream(SourceName, ElementSize, Source, nullptr); } diff --git a/Hydrogent/src/HnMesh.cpp b/Hydrogent/src/HnMesh.cpp index bb7f2430..2db29c99 100644 --- a/Hydrogent/src/HnMesh.cpp +++ b/Hydrogent/src/HnMesh.cpp @@ -386,7 +386,7 @@ void HnMesh::UpdateRepr(pxr::HdSceneDelegate& SceneDelegate, GetVertexBuffer(pxr::HdTokens->normals) == nullptr && !StagingVerts.Points.IsEmpty()) { - GenerateSmoothNormals(StagingVerts); + GenerateSmoothNormals(*RenderDelegate, StagingVerts); } UpdateConstantPrimvars(SceneDelegate, RenderParam, DirtyBits, ReprToken); @@ -528,7 +528,8 @@ void HnMesh::UpdateTopology(pxr::HdSceneDelegate& SceneDelegate, DirtyBits &= ~pxr::HdChangeTracker::DirtyTopology; } -bool HnMesh::AddStagingBufferSourceForPrimvar(StagingVertexData& StagingVerts, +bool HnMesh::AddStagingBufferSourceForPrimvar(HnRenderDelegate* RenderDelegate, + StagingVertexData& StagingVerts, const pxr::TfToken& Name, pxr::VtValue Primvar, pxr::HdInterpolation Interpolation, @@ -537,6 +538,16 @@ bool HnMesh::AddStagingBufferSourceForPrimvar(StagingVertexData& StagingVerts, 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); + } + } + pxr::VtValue FaceVaryingPrimvar; pxr::VtValue* pSrcPrimvar = &Primvar; if ((Interpolation == pxr::HdInterpolationVertex || Interpolation == pxr::HdInterpolationVarying) && m_HasFaceVaryingPrimvars) @@ -677,7 +688,7 @@ bool HnMesh::AddJointInfluencesStagingBufferSource(const pxr::VtValue& NumInflue } } - return AddStagingBufferSourceForPrimvar(StagingVerts, HnTokens->joints, pxr::VtValue::Take(Joints), pxr::HdInterpolationVertex, 2); + return AddStagingBufferSourceForPrimvar(nullptr, StagingVerts, HnTokens->joints, pxr::VtValue::Take(Joints), pxr::HdInterpolationVertex, 2); } void HnMesh::PrimvarsInfo::AddDirtyPrimvar(pxr::HdDirtyBits& DirtyBits, @@ -781,9 +792,10 @@ void HnMesh::UpdateSkinningPrimvars(pxr::HdSceneDelegate& const pxr::HdExtComputationPrimvarDescriptor& SkinningCompPrimDesc, StagingVertexData& StagingVerts) { - const pxr::HdRenderIndex& RenderIndex = SceneDelegate.GetRenderIndex(); - const pxr::SdfPath& SkinnigCompId = SkinningCompPrimDesc.sourceComputationId; - const HnExtComputation* SkinningComp = static_cast(RenderIndex.GetSprim(pxr::HdPrimTypeTokens->extComputation, SkinnigCompId)); + const pxr::HdRenderIndex& RenderIndex = SceneDelegate.GetRenderIndex(); + HnRenderDelegate* RenderDelegate = static_cast(RenderIndex.GetRenderDelegate()); + const pxr::SdfPath& SkinnigCompId = SkinningCompPrimDesc.sourceComputationId; + const HnExtComputation* SkinningComp = static_cast(RenderIndex.GetSprim(pxr::HdPrimTypeTokens->extComputation, SkinnigCompId)); if (SkinningComp == nullptr) { LOG_ERROR_MESSAGE("Unable to find skinning computation ", SkinnigCompId); @@ -807,7 +819,7 @@ void HnMesh::UpdateSkinningPrimvars(pxr::HdSceneDelegate& SkinningPrimvarsVersion += InputAggregatorComp->GetSceneInputsVersion(); } - entt::registry& Registry = static_cast(SceneDelegate.GetRenderIndex().GetRenderDelegate())->GetEcsRegistry(); + entt::registry& Registry = RenderDelegate->GetEcsRegistry(); Components::Skinning& SkinningData = Registry.get(m_Entity); if (SkinningPrimvarsVersion != m_SkinningPrimvarsVersion) @@ -857,7 +869,7 @@ void HnMesh::UpdateSkinningPrimvars(pxr::HdSceneDelegate& StagingVerts.Points = RestPointsVal; - AddStagingBufferSourceForPrimvar(StagingVerts, SkinningCompPrimDesc.name, std::move(RestPointsVal), SkinningCompPrimDesc.interpolation); + AddStagingBufferSourceForPrimvar(RenderDelegate, StagingVerts, SkinningCompPrimDesc.name, std::move(RestPointsVal), SkinningCompPrimDesc.interpolation); AddJointInfluencesStagingBufferSource(NumInfluencesPerComponentVal, InfluencesVal, StagingVerts); // Skeleton adapter hides normals, so try to get them directly from the scene delegate @@ -867,9 +879,9 @@ void HnMesh::UpdateSkinningPrimvars(pxr::HdSceneDelegate& // There is no way to get the interpolation of the normals primvar from the scene delegate, // so rely on the number of elements. if (NormalsPrimvar.GetArraySize() == m_Topology.GetNumPoints()) - AddStagingBufferSourceForPrimvar(StagingVerts, pxr::HdTokens->normals, std::move(NormalsPrimvar), pxr::HdInterpolationVertex); + AddStagingBufferSourceForPrimvar(RenderDelegate, StagingVerts, pxr::HdTokens->normals, std::move(NormalsPrimvar), pxr::HdInterpolationVertex); else if (NormalsPrimvar.GetArraySize() == m_Topology.GetNumFaceVaryings()) - AddStagingBufferSourceForPrimvar(StagingVerts, pxr::HdTokens->normals, std::move(NormalsPrimvar), pxr::HdInterpolationFaceVarying); + AddStagingBufferSourceForPrimvar(RenderDelegate, StagingVerts, pxr::HdTokens->normals, std::move(NormalsPrimvar), pxr::HdInterpolationFaceVarying); } m_SkinningPrimvarsVersion = SkinningPrimvarsVersion; @@ -900,6 +912,8 @@ void HnMesh::UpdateVertexAndVaryingPrimvars(pxr::HdSceneDelegate& SceneDelegate, const PrimvarsInfo& VertexPrimvarsInfo, StagingVertexData& StagingVerts) { + HnRenderDelegate* RenderDelegate = static_cast(SceneDelegate.GetRenderIndex().GetRenderDelegate()); + for (const auto& it : VertexPrimvarsInfo.Dirty) { const pxr::TfToken& Name = it.first; @@ -912,7 +926,7 @@ void HnMesh::UpdateVertexAndVaryingPrimvars(pxr::HdSceneDelegate& SceneDelegate, if (PrimDesc.name == pxr::HdTokens->points) StagingVerts.Points = PrimValue; - AddStagingBufferSourceForPrimvar(StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); + AddStagingBufferSourceForPrimvar(RenderDelegate, StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); } for (const pxr::HdExtComputationPrimvarDescriptor& ExtCompPrimDesc : VertexPrimvarsInfo.ExtComp) @@ -932,13 +946,15 @@ void HnMesh::UpdateFaceVaryingPrimvars(pxr::HdSceneDelegate& SceneDelegate, const PrimvarsInfo& FacePrimvarsInfo, StagingVertexData& StagingVerts) { + HnRenderDelegate* RenderDelegate = static_cast(SceneDelegate.GetRenderIndex().GetRenderDelegate()); + for (const auto& it : FacePrimvarsInfo.Dirty) { const pxr::TfToken& Name = it.first; const pxr::HdPrimvarDescriptor& PrimDesc = it.second; pxr::VtValue PrimValue = GetPrimvar(&SceneDelegate, PrimDesc.name); - AddStagingBufferSourceForPrimvar(StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); + AddStagingBufferSourceForPrimvar(RenderDelegate, StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); } } @@ -1000,7 +1016,7 @@ void HnMesh::UpdateConstantPrimvars(pxr::HdSceneDelegate& SceneDelegate, } } -void HnMesh::GenerateSmoothNormals(StagingVertexData& StagingVerts) +void HnMesh::GenerateSmoothNormals(HnRenderDelegate& RenderDelegate, StagingVertexData& StagingVerts) { pxr::Hd_VertexAdjacency Adjacency; Adjacency.BuildAdjacencyTable(&m_Topology); @@ -1025,7 +1041,7 @@ void HnMesh::GenerateSmoothNormals(StagingVertexData& StagingVerts) return; } - AddStagingBufferSourceForPrimvar(StagingVerts, pxr::HdTokens->normals, pxr::VtValue::Take(Normals), pxr::HdInterpolationVertex); + AddStagingBufferSourceForPrimvar(&RenderDelegate, StagingVerts, pxr::HdTokens->normals, pxr::VtValue::Take(Normals), pxr::HdInterpolationVertex); } void HnMesh::UpdateIndexData(StagingIndexData& StagingInds, const pxr::VtValue& Points) diff --git a/Hydrogent/src/HnMeshUtils.cpp b/Hydrogent/src/HnMeshUtils.cpp index 224e5ae8..8bda01f8 100644 --- a/Hydrogent/src/HnMeshUtils.cpp +++ b/Hydrogent/src/HnMeshUtils.cpp @@ -28,6 +28,7 @@ #include "DebugUtilities.hpp" #include "AdvancedMath.hpp" #include "GfTypeConversions.hpp" +#include "PBR_Renderer.hpp" #include "pxr/base/gf/vec2i.h" #include "pxr/base/gf/vec3i.h" @@ -375,6 +376,26 @@ pxr::VtValue HnMeshUtils::ConvertVertexPrimvarToFaceVarying(const pxr::VtValue& } } +pxr::VtValue HnMeshUtils::PackVertexNormals(const pxr::VtValue& Normals) const +{ + 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); + } + else + { + LOG_ERROR_MESSAGE("Failed to pack vertex normals for mesh '", m_MeshId.GetString(), "': ", Normals.GetTypeName(), " is not supported"); + return {}; + } +} + } // namespace USD } // namespace Diligent diff --git a/Hydrogent/src/HnRenderDelegate.cpp b/Hydrogent/src/HnRenderDelegate.cpp index 1efbf047..aa29bd2b 100644 --- a/Hydrogent/src/HnRenderDelegate.cpp +++ b/Hydrogent/src/HnRenderDelegate.cpp @@ -174,6 +174,7 @@ static std::shared_ptr CreateUSDRenderer(const HnRenderDelegate::C USDRendererCI.EnableClearCoat = true; USDRendererCI.AllowHotShaderReload = RenderDelegateCI.AllowHotShaderReload; + USDRendererCI.PackVertexNormals = RenderDelegateCI.PackVertexNormals; const RenderDeviceInfo& DeviceInfo = RenderDelegateCI.pDevice->GetDeviceInfo(); // There is a major performance degradation when using row-major matrices @@ -252,11 +253,21 @@ static std::shared_ptr CreateUSDRenderer(const HnRenderDelegate::C USDRendererCI.ShaderTexturesArrayMode = USD_Renderer::SHADER_TEXTURE_ARRAY_MODE_NONE; } - static constexpr LayoutElement Inputs[] = + // 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 + }; + const LayoutElement Inputs[] = { // clang-format off {USD_Renderer::VERTEX_ATTRIB_ID_POSITION, HnRenderPass::VERTEX_BUFFER_SLOT_POSITIONS, 3, VT_FLOAT32}, // float3 Pos : ATTRIB0; - {USD_Renderer::VERTEX_ATTRIB_ID_NORMAL, HnRenderPass::VERTEX_BUFFER_SLOT_NORMALS, 3, VT_FLOAT32}, // float3 Normal : ATTRIB1; + 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/PBR/interface/PBR_Renderer.hpp b/PBR/interface/PBR_Renderer.hpp index 7ebe4ad0..f662d20a 100644 --- a/PBR/interface/PBR_Renderer.hpp +++ b/PBR/interface/PBR_Renderer.hpp @@ -202,6 +202,9 @@ class PBR_Renderer /// Whether to use skin pre-transform before applying joint transformations. bool UseSkinPreTransform = false; + /// Whether vertex normals are packed into a single 32-bit uint, see PackVertexNormal(). + bool PackVertexNormals = false; + /// PCF shadow kernel size. /// Allowed values are 2, 3, 5, 7. Uint32 PCFKernelSize = 3; @@ -721,6 +724,11 @@ class PBR_Renderer Uint32 GetJointsBufferSize() const; const char* GetJointTransformsVarName() const; + /// Packs normal into a single 32-bit uint. + /// + /// \remarks The function assumes that the input vector is normalized. + static Uint32 PackVertexNormal(const float3& Normal); + protected: ShaderMacroHelper DefineMacros(const PSOKey& Key) const; diff --git a/PBR/src/PBR_Renderer.cpp b/PBR/src/PBR_Renderer.cpp index 90419fd7..c38b1e9f 100644 --- a/PBR/src/PBR_Renderer.cpp +++ b/PBR/src/PBR_Renderer.cpp @@ -1175,6 +1175,7 @@ ShaderMacroHelper PBR_Renderer::DefineMacros(const PSOKey& Key) const Macros.Add("JOINTS_BUFFER_MODE", static_cast(m_Settings.JointsBufferMode)); Macros.Add("USE_SKIN_PRE_TRANSFORM", m_Settings.UseSkinPreTransform); + Macros.Add("PACK_VERTEX_NORMALS", m_Settings.PackVertexNormals); Macros.Add("TONE_MAPPING_MODE", "TONE_MAPPING_MODE_UNCHARTED2"); Macros.Add("PRIMITIVE_ARRAY_SIZE", static_cast(m_Settings.PrimitiveArraySize)); @@ -1447,11 +1448,19 @@ 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, + }; + const std::array VSAttribs = // { // clang-format off VSAttribInfo{VERTEX_ATTRIB_ID_POSITION, "Pos", VT_FLOAT32, 3, PSO_FLAG_NONE}, - VSAttribInfo{VERTEX_ATTRIB_ID_NORMAL, "Normal", VT_FLOAT32, 3, PSO_FLAG_USE_VERTEX_NORMALS}, + VSNormalAttrib, 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}, @@ -1486,8 +1495,26 @@ void PBR_Renderer::GetVSInputStructAndLayout(PSO_FLAGS PSOFlags, DEV_CHECK_ERR(AttribFound, "Input layout does not contain attribute '", Attrib.Name, "' (index ", Attrib.Index, ")"); } #endif - VERIFY_EXPR(Attrib.Type == VT_FLOAT32); - ss << " float" << Attrib.NumComponents << std::setw(9) << Attrib.Name << " : ATTRIB" << Attrib.Index << ";" << std::endl; + switch (Attrib.Type) + { + case VT_FLOAT32: + ss << " float"; + break; + case VT_UINT32: + ss << " uint"; + break; + default: + UNEXPECTED("Unexpected attribute type"); + } + if (Attrib.NumComponents > 1) + { + ss << Attrib.NumComponents; + } + else + { + ss << ' '; + } + ss << std::setw(9) << Attrib.Name << " : ATTRIB" << Attrib.Index << ";" << std::endl; } else { @@ -1652,7 +1679,7 @@ void PBR_Renderer::CreatePSO(PsoHashMapType& PsoHashMap, GraphicsDesc.PrimitiveTopology == PRIMITIVE_TOPOLOGY_POINT_LIST && m_Device.GetDeviceInfo().IsVulkanDevice() && m_Settings.PrimitiveArraySize == 0; // When PrimitiveArraySize > 0, we convert HLSL to GLSL - const auto VSOutputStruct = GetVSOutputStruct(PSOFlags, UseVkPointSize, m_Settings.PrimitiveArraySize > 0); + const std::string VSOutputStruct = GetVSOutputStruct(PSOFlags, UseVkPointSize, m_Settings.PrimitiveArraySize > 0); CreateInfo::PSMainSourceInfo PSMainSource; if (m_Settings.GetPSMainSource) @@ -2089,4 +2116,12 @@ 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 1137196a..d6cddcd7 100644 --- a/Shaders/PBR/private/RenderPBR.vsh +++ b/Shaders/PBR/private/RenderPBR.vsh @@ -139,6 +139,23 @@ float4 GetVertexColor(float4 Color) return Color; } +#if PACK_VERTEX_NORMALS +float3 GetNormal(in uint PackedNormal) +{ + float3 Normal; + Normal.x = float( PackedNormal & 0xFFFFu) / 32767.0 - 1.0; + Normal.y = float((PackedNormal >> 16u) & 0x7FFFu) / 16383.0 - 1.0; + Normal.z = sqrt(max(1.0 - dot(Normal.xy, Normal.xy), 0.0)); + Normal.z *= (PackedNormal & 0x80000000u) != 0u ? -1.0 : 1.0; + return Normal; +} +#else +float3 GetNormal(in float3 Normal) +{ + return Normal; +} +#endif + void main(in VSInput VSIn, out VSOutput VSOut) { @@ -188,7 +205,7 @@ void main(in VSInput VSIn, #endif #if USE_VERTEX_NORMALS - float3 Normal = VSIn.Normal; + float3 Normal = GetNormal(VSIn.Normal); #else float3 Normal = float3(0.0, 0.0, 1.0); #endif