Skip to content

Commit 5e5897f

Browse files
Add bounded morph target blending to PBR renderer
- select the strongest active morph targets - deform positions, normals, and tangents from a structured buffer - preserve previous-frame weights for motion vectors - add configurable target limits and cross-backend tests
1 parent e188ecd commit 5e5897f

9 files changed

Lines changed: 519 additions & 44 deletions

File tree

‎PBR/interface/GLTF_PBR_Renderer.hpp‎

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -221,25 +221,31 @@ class GLTF_PBR_Renderer : public PBR_Renderer
221221

222222
struct PBRPrimitiveShaderAttribsData
223223
{
224-
PSO_FLAGS PSOFlags = PSO_FLAG_NONE;
225-
const float4x4* NodeMatrix = nullptr;
226-
const float4x4* PrevNodeMatrix = nullptr; // #if COMPUTE_MOTION_VECTORS
227-
Uint32 JointCount = 0;
228-
Uint32 FirstJoint = 0; // #if USE_JOINTS
229-
Uint32 PrevFirstJoint = 0; // #if USE_JOINTS && COMPUTE_MOTION_VECTORS
230-
const float3* PosScale = nullptr;
231-
const float3* PosBias = nullptr;
232-
const float4x4* SkinPreTransform = nullptr; // #if USE_JOINTS && USE_SKIN_PRE_TRANSFORM
233-
const float4x4* PrevSkinPreTransform = nullptr; // #if USE_JOINTS && USE_SKIN_PRE_TRANSFORM && COMPUTE_MOTION_VECTORS
234-
const float4* FallbackColor = nullptr;
235-
const void* CustomData = nullptr;
236-
size_t CustomDataSize = 0;
224+
PSO_FLAGS PSOFlags = PSO_FLAG_NONE;
225+
const float4x4* NodeMatrix = nullptr;
226+
const float4x4* PrevNodeMatrix = nullptr; // #if COMPUTE_MOTION_VECTORS
227+
Uint32 JointCount = 0;
228+
Uint32 FirstJoint = 0; // #if USE_JOINTS
229+
Uint32 PrevFirstJoint = 0; // #if USE_JOINTS && COMPUTE_MOTION_VECTORS
230+
const float3* PosScale = nullptr;
231+
const float3* PosBias = nullptr;
232+
const float4x4* SkinPreTransform = nullptr; // #if USE_JOINTS && USE_SKIN_PRE_TRANSFORM
233+
const float4x4* PrevSkinPreTransform = nullptr; // #if USE_JOINTS && USE_SKIN_PRE_TRANSFORM && COMPUTE_MOTION_VECTORS
234+
Uint32 MorphTargetVertexOffset = 0; // #if USE_MORPH_TARGETS
235+
const MorphTargetShaderAttribs* pActiveMorphTargets = nullptr; // #if USE_MORPH_TARGETS
236+
Uint32 ActiveMorphTargetCount = 0;
237+
const MorphTargetShaderAttribs* pPrevActiveMorphTargets = nullptr; // #if USE_MORPH_TARGETS && COMPUTE_MOTION_VECTORS
238+
Uint32 PrevActiveMorphTargetCount = 0;
239+
const float4* FallbackColor = nullptr;
240+
const void* CustomData = nullptr;
241+
size_t CustomDataSize = 0;
237242
};
238243
static void* WritePBRPrimitiveShaderAttribs(void* pDstShaderAttribs,
239244
const PBRPrimitiveShaderAttribsData& AttribsData,
240245
bool TransposeMatrices,
241246
bool UseSkinPreTransform,
242-
VERTEX_POS_PACK_MODE VertexPosPackMode);
247+
VERTEX_POS_PACK_MODE VertexPosPackMode,
248+
Uint32 MaxActiveMorphTargetCount);
243249

244250
struct PBRMaterialShaderAttribsData
245251
{

‎PBR/interface/PBR_Renderer.hpp‎

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,38 @@ class PBR_Renderer
141141
VERTEX_POS_PACK_MODE_64_BIT,
142142
};
143143

144+
static constexpr Uint32 InvalidMorphTargetDataOffset = ~Uint32{0};
145+
146+
/// One morph target selected for the active target palette.
147+
struct ActiveMorphTarget
148+
{
149+
Uint32 TargetIndex = 0;
150+
Float32 Weight = 0;
151+
};
152+
153+
/// Shader-readable data for one active morph target.
154+
///
155+
/// Each referenced stream contains one tightly packed Float32 triplet per
156+
/// vertex. Delta offsets are measured in Float32 elements in the structured
157+
/// morph target buffer. InvalidMorphTargetDataOffset indicates that the
158+
/// target does not provide the corresponding attribute.
159+
struct MorphTargetShaderAttribs
160+
{
161+
Float32 Weight = 0;
162+
Uint32 PositionDeltaOffset = InvalidMorphTargetDataOffset;
163+
Uint32 NormalDeltaOffset = InvalidMorphTargetDataOffset;
164+
Uint32 TangentDeltaOffset = InvalidMorphTargetDataOffset;
165+
};
166+
167+
/// Selects up to MaxActiveTargetCount nonzero weights with the greatest
168+
/// absolute magnitude. The result is sorted by descending magnitude, with
169+
/// lower target indices ordered first when magnitudes are equal. Signed
170+
/// weights are preserved.
171+
static Uint32 SelectActiveMorphTargets(const Float32* pWeights,
172+
Uint32 WeightCount,
173+
ActiveMorphTarget* pActiveTargets,
174+
Uint32 MaxActiveTargetCount) noexcept;
175+
144176
/// Renderer create info
145177
struct CreateInfo
146178
{
@@ -201,6 +233,11 @@ class PBR_Renderer
201233
/// Disable this when joint data is supplied through renderer-specific SRBs.
202234
bool CreateDefaultJointsBuffer = true;
203235

236+
/// Whether to create a default morph target buffer when
237+
/// pMorphTargetBuffer is null. Disable this when morph target data is
238+
/// supplied through renderer-specific SRBs.
239+
bool CreateDefaultMorphTargetBuffer = true;
240+
204241
/// Whether to enable shadows.
205242
/// A pipeline state can use shadows only if this flag is set to true.
206243
bool EnableShadows = false;
@@ -304,6 +341,12 @@ class PBR_Renderer
304341
/// If set to 0, the animation will be disabled.
305342
Uint32 MaxJointCount = 64;
306343

344+
/// Maximum number of morph targets blended by a draw.
345+
///
346+
/// The value determines the size of the per-primitive morph palette
347+
/// and bounds vertex shader work. Set to zero to disable morph targets.
348+
Uint32 MaxActiveMorphTargetCount = 4;
349+
307350
/// Joints buffer mode.
308351
JOINTS_BUFFER_MODE JointsBufferMode = JOINTS_BUFFER_MODE_UNIFORM;
309352

@@ -324,6 +367,7 @@ class PBR_Renderer
324367
/// float4 Weight0 : ATTRIB5; // If PSO_FLAG_USE_JOINTS is set
325368
/// float4 Color : ATTRIB6; // If PSO_FLAG_USE_VERTEX_COLORS is set
326369
/// float3 Tangent : ATTRIB7; // If PSO_FLAG_USE_VERTEX_TANGENTS is set
370+
/// uint VertexID : SV_VertexID; // If PSO_FLAG_USE_MORPH_TARGETS is set
327371
/// };
328372
InputLayoutDesc InputLayout;
329373

@@ -372,6 +416,11 @@ class PBR_Renderer
372416
/// allocate the buffer.
373417
IBuffer* pJointsBuffer = nullptr;
374418

419+
/// A pointer to the user-provided morph target buffer.
420+
/// If null and CreateDefaultMorphTargetBuffer is true, the renderer
421+
/// will allocate a minimal fallback buffer.
422+
IBuffer* pMorphTargetBuffer = nullptr;
423+
375424
/// Texture attribute index info
376425
std::array<int, TEXTURE_ATTRIB_ID_COUNT> TextureAttribIndices{};
377426

@@ -482,6 +531,7 @@ class PBR_Renderer
482531
IBuffer* GetPBRPrimitiveAttribsCB() const { return m_PBRPrimitiveAttribsCB; }
483532
IBuffer* GetPBRMaterialAttribsCB() const { return m_PBRMaterialAttribsCB; }
484533
IBuffer* GetJointsBuffer() const { return m_JointsBuffer; }
534+
IBuffer* GetMorphTargetBuffer() const { return m_MorphTargetBuffer; }
485535
// clang-format on
486536

487537
static constexpr TEXTURE_FORMAT PrefilteredEnvMapFmt = TEX_FORMAT_RGBA16_FLOAT;
@@ -655,8 +705,9 @@ class PBR_Renderer
655705
PSO_FLAG_UNSHADED = PSO_FLAG_BIT(39),
656706
PSO_FLAG_COMPUTE_MOTION_VECTORS = PSO_FLAG_BIT(40),
657707
PSO_FLAG_ENABLE_SHADOWS = PSO_FLAG_BIT(41),
708+
PSO_FLAG_USE_MORPH_TARGETS = PSO_FLAG_BIT(42),
658709

659-
PSO_FLAG_LAST = PSO_FLAG_ENABLE_SHADOWS,
710+
PSO_FLAG_LAST = PSO_FLAG_USE_MORPH_TARGETS,
660711

661712
PSO_FLAG_FIRST_USER_DEFINED = PSO_FLAG_LAST << 1ull,
662713

@@ -878,6 +929,12 @@ class PBR_Renderer
878929
/// Optional joints buffer. When null, the renderer-owned buffer is used.
879930
IBuffer* pJointsBuffer = nullptr;
880931

932+
/// Optional structured Float32 buffer containing morph target deltas.
933+
/// When null, the renderer-owned buffer is used. A buffer containing
934+
/// the referenced data must be provided when PSO_FLAG_USE_MORPH_TARGETS
935+
/// is enabled.
936+
IBuffer* pMorphTargetDeltas = nullptr;
937+
881938
/// Optional shadow map view.
882939
ITextureView* pShadowMap = nullptr;
883940

@@ -952,10 +1009,11 @@ class PBR_Renderer
9521009
static void* WriteSkinningData(void* pDst, const WriteSkinningDataAttribs& Attribs, bool PackMatrixRowMajor, Uint32 MaxJointCount);
9531010
void* WriteSkinningData(void* pDst, const WriteSkinningDataAttribs& Attribs);
9541011

955-
static Uint32 GetJointsDataSize(Uint32 MaxJointCount, bool UsePrevFrameTransforms);
956-
Uint32 GetJointsDataSize(Uint32 JointCount, PSO_FLAGS PSOFlags) const;
957-
Uint32 GetJointsBufferSize() const;
958-
const char* GetJointTransformsVarName() const;
1012+
static Uint32 GetJointsDataSize(Uint32 MaxJointCount, bool UsePrevFrameTransforms);
1013+
Uint32 GetJointsDataSize(Uint32 JointCount, PSO_FLAGS PSOFlags) const;
1014+
Uint32 GetJointsBufferSize() const;
1015+
const char* GetJointTransformsVarName() const;
1016+
static const char* GetMorphTargetDeltasVarName();
9591017

9601018
/// Packs normal into a single 32-bit uint.
9611019
///
@@ -1113,6 +1171,7 @@ class PBR_Renderer
11131171
RefCntAutoPtr<IBuffer> m_PBRPrimitiveAttribsCB;
11141172
RefCntAutoPtr<IBuffer> m_PBRMaterialAttribsCB;
11151173
RefCntAutoPtr<IBuffer> m_JointsBuffer;
1174+
RefCntAutoPtr<IBuffer> m_MorphTargetBuffer;
11161175

11171176
std::unordered_set<std::string> m_GeneratedIncludes;
11181177

‎PBR/src/GLTF_PBR_Renderer.cpp‎

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,8 @@ void GLTF_PBR_Renderer::Render(IDeviceContext* pCtx,
709709
AttribsData,
710710
!m_Settings.PackMatrixRowMajor,
711711
m_Settings.UseSkinPreTransform,
712-
m_Settings.VertexPosPackMode);
712+
m_Settings.VertexPosPackMode,
713+
m_Settings.MaxActiveMorphTargetCount);
713714

714715
VERIFY(reinterpret_cast<uint8_t*>(pEndPtr) <= static_cast<uint8_t*>(pAttribsData) + m_PBRPrimitiveAttribsCB->GetDesc().Size,
715716
"Not enough space in the buffer to store primitive attributes");
@@ -791,7 +792,8 @@ void* GLTF_PBR_Renderer::WritePBRPrimitiveShaderAttribs(void*
791792
const PBRPrimitiveShaderAttribsData& AttribsData,
792793
bool TransposeMatrices,
793794
bool UseSkinPreTransform,
794-
VERTEX_POS_PACK_MODE VertexPosPackMode)
795+
VERTEX_POS_PACK_MODE VertexPosPackMode,
796+
Uint32 MaxActiveMorphTargetCount)
795797
{
796798
// When adding new members, don't forget to update PBR_Renderer::GetPBRPrimitiveAttribsSize!
797799

@@ -814,6 +816,17 @@ void* GLTF_PBR_Renderer::WritePBRPrimitiveShaderAttribs(void*
814816
// } Skinning;
815817
// } Transforms;
816818
//
819+
// struct PBRMorphTargetsShaderAttribs // #if USE_MORPH_TARGETS
820+
// {
821+
// uint VertexOffset;
822+
// uint Padding0;
823+
// uint Padding1;
824+
// uint Padding2;
825+
//
826+
// PBRMorphTargetShaderAttribs Targets[MAX_ACTIVE_MORPH_TARGET_COUNT];
827+
// PBRMorphTargetShaderAttribs PrevTargets[MAX_ACTIVE_MORPH_TARGET_COUNT]; // #if COMPUTE_MOTION_VECTORS
828+
// } MorphTargets;
829+
//
817830
// struct PBRVertexPositionUnpackShaderAttribs // #if VERTEX_POS_PACK_MODE != VERTEX_POS_PACK_MODE_NONE
818831
// {
819832
// float4 Bias;
@@ -890,6 +903,44 @@ void* GLTF_PBR_Renderer::WritePBRPrimitiveShaderAttribs(void*
890903
}
891904
}
892905

906+
if ((AttribsData.PSOFlags & PSO_FLAG_USE_MORPH_TARGETS) != 0 && MaxActiveMorphTargetCount != 0)
907+
{
908+
WriteValue(AttribsData.MorphTargetVertexOffset);
909+
WriteValue(Uint32{0});
910+
WriteValue(Uint32{0});
911+
WriteValue(Uint32{0});
912+
913+
static_assert(sizeof(MorphTargetShaderAttribs) == sizeof(float4), "Unexpected morph target shader attributes size");
914+
const auto WriteMorphTargetPalette = [&](const MorphTargetShaderAttribs* pTargets, Uint32 TargetCount) {
915+
if (TargetCount > MaxActiveMorphTargetCount)
916+
{
917+
UNEXPECTED("Active morph target count (", TargetCount, ") exceeds the renderer limit (", MaxActiveMorphTargetCount, ')');
918+
TargetCount = MaxActiveMorphTargetCount;
919+
}
920+
if (TargetCount != 0 && pTargets == nullptr)
921+
{
922+
UNEXPECTED("Active morph target palette must not be null");
923+
TargetCount = 0;
924+
}
925+
926+
const MorphTargetShaderAttribs EmptyTarget{};
927+
for (Uint32 TargetIndex = 0; TargetIndex < MaxActiveMorphTargetCount; ++TargetIndex)
928+
{
929+
const MorphTargetShaderAttribs& Target = TargetIndex < TargetCount ?
930+
pTargets[TargetIndex] :
931+
EmptyTarget;
932+
std::memcpy(pDstPtr, &Target, sizeof(Target));
933+
pDstPtr += sizeof(Target);
934+
}
935+
};
936+
937+
WriteMorphTargetPalette(AttribsData.pActiveMorphTargets, AttribsData.ActiveMorphTargetCount);
938+
if ((AttribsData.PSOFlags & PSO_FLAG_COMPUTE_MOTION_VECTORS) != 0)
939+
{
940+
WriteMorphTargetPalette(AttribsData.pPrevActiveMorphTargets, AttribsData.PrevActiveMorphTargetCount);
941+
}
942+
}
943+
893944
if (VertexPosPackMode != VERTEX_POS_PACK_MODE_NONE)
894945
{
895946
const float3& PosBias = AttribsData.PosBias != nullptr ? *AttribsData.PosBias : float3{0, 0, 0};

0 commit comments

Comments
 (0)