Files
UnrealEngine/Engine/Shaders/Private/Nanite/NaniteVertexDeformation.ush
2025-05-18 13:04:45 +08:00

536 lines
21 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "../ViewData.ush"
#include "../SplineMeshCommon.ush"
#include "NaniteSceneCommon.ush"
#include "NaniteAttributeDecode.ush"
#include "NaniteSceneCommon.ush"
#include "NaniteVertexFetch.ush"
#include "../Matrices.ush"
// Represents vertex data for a Nanite mesh in local space, post-deformation (when applicable)
struct FNanitePostDeformVertex
{
// Index of the vertex in the cluster
uint VertIndex;
// Post-deformed position of the vertex
float3 Position;
// Decoded vertex position (BEFORE deformation)
float3 PointLocal;
// Vertex normal (BEFORE deformation)
float3 PreSkinnedNormal;
// Post-deformed tangent basis of the vertex
FNaniteTangentBasis TangentBasis;
// Normalized distance along the spline (spline meshes only)
half SplineDist;
// Decoded vertex attribute data
FNaniteRawAttributeData RawAttributeData;
};
FNanitePostDeformVertex DeformLocalNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FCluster Cluster, FNaniteLocalVertex Input)
{
FNanitePostDeformVertex Output;
Output.VertIndex = Input.VertIndex;
Output.TangentBasis = MakeTangentBasis(Input.RawAttributeData);
Output.SplineDist = 0.0f;
Output.RawAttributeData = Input.RawAttributeData;
Output.PointLocal = Input.Position;
Output.Position = Input.Position;
Output.PreSkinnedNormal = Output.TangentBasis.TangentZ;
#if USE_SKINNING
bool bActiveSkinning = Cluster.bSkinning && InstanceViewData.bIsDeforming;
BRANCH
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning)
{
FNaniteSkinningHeader SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
float3 SkinnedPosition = float3(0.0f, 0.0f, 0.0f);
float3 SkinnedNormal = float3(0.0f, 0.0f, 0.0f);
float3 SkinnedTangent = float3(0.0f, 0.0f, 0.0f);
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
{
uint BoneIndex = 0;
float BoneWeight = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, Input.VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
SkinnedPosition += mul(float4(Input.Position, 1.0f), BoneTransform) * BoneWeight;
SkinnedNormal += mul(Output.TangentBasis.TangentZ, (float3x3)BoneTransform) * BoneWeight;
SkinnedTangent += mul(Output.TangentBasis.TangentXAndSign.xyz, (float3x3)BoneTransform) * BoneWeight;
}
Output.TangentBasis.TangentZ = SkinnedNormal;
Output.TangentBasis.TangentXAndSign.xyz = SkinnedTangent;
#if 0
Output.TangentBasis.RecalculateTangentX();
#endif
Output.Position = SkinnedPosition;
}
#endif
#if USE_SPLINEDEFORM
BRANCH
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SPLINE_MESH) != 0 &&
(InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0)
{
// Deform the local position and tangent basis along the spline
// NOTE: Storing off the spline distance for use later when calculating tangent frame.
FSplineMeshShaderParams SplineMeshParams = SplineMeshLoadParamsFromInstancePayload(InstanceData);
Output.SplineDist = SplineMeshDeformLocalPosNormalTangent(
SplineMeshParams,
Output.Position,
Output.TangentBasis.TangentZ,
Output.TangentBasis.TangentXAndSign.xyz
);
}
#endif
return Output;
}
FNanitePostDeformVertex FetchAndDeformLocalNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FCluster Cluster, FVisibleCluster VisibleCluster, uint VertIndex, uint CompileTimeMaxTexCoords)
{
return DeformLocalNaniteVertex(
PrimitiveData,
InstanceData,
InstanceViewData,
Cluster,
FetchLocalNaniteVertex(InstanceData, Cluster, VisibleCluster, VertIndex, CompileTimeMaxTexCoords));
}
void FetchAndDeformLocalNaniteTriangle(
FPrimitiveSceneData PrimitiveData,
FInstanceSceneData InstanceData,
FInstanceViewData InstanceViewData,
FCluster Cluster,
FVisibleCluster VisibleCluster,
uint3 VertIndexes,
uint CompileTimeMaxTexCoords,
inout FNanitePostDeformVertex OutVerts[3])
{
FNaniteLocalVertex InVerts[3];
FetchLocalNaniteTriangle(InstanceData, Cluster, VisibleCluster, VertIndexes, CompileTimeMaxTexCoords, InVerts);
UNROLL_N(3)
for(uint i = 0; i < 3; ++i)
{
OutVerts[i] = DeformLocalNaniteVertex(PrimitiveData, InstanceData, InstanceViewData, Cluster, InVerts[i]);
}
}
float4x3 SampleSkinningTransform(FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, FBoneInfluenceHeader BoneInfluenceHeader, uint VertIndex)
{
float4x3 SkinningTransform4x3 = 0.0f;
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
{
uint BoneIndex = 0;
float BoneWeight = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
SkinningTransform4x3 += BoneTransform * BoneWeight;
}
return SkinningTransform4x3;
}
float4x3 SampleVoxelSkinningTransform(FInstanceSceneData InstanceData, FCluster Cluster, FNaniteSkinningHeader SkinningHeader)
{
float4x3 SkinningTransform4x3 = 0.0f;
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumClusterBoneInfluences; ++InfluenceIndex)
{
const uint PackedBoneInfluence = ClusterPageData.Load(Cluster.ClusterBoneInfluenceAddress + InfluenceIndex * Cluster.ClusterBoneInfluenceStride);
FVoxelBoneInfluence Influence;
Influence.BoneIndex = PackedBoneInfluence >> 8;
Influence.Weight = (PackedBoneInfluence & 0xFFu) * (1.0f / 255.0f);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + Influence.BoneIndex);
SkinningTransform4x3 += BoneTransform * Influence.Weight;
}
return SkinningTransform4x3;
}
#if NANITE_USE_PRECISE_SKINNING_BOUNDS
// TODO: Get rid of these silly helpers
// Axis 0...5 == -X, +X, -Y, +Y, -Z, +Z
void SetMinMaxValueByAxis(float Value, uint Axis, inout float3 MinPoint, inout float3 MaxPoint)
{
switch (Axis)
{
case 0: MinPoint.x = Value; break;
case 1: MaxPoint.x = Value; break;
case 2: MinPoint.y = Value; break;
case 3: MaxPoint.y = Value; break;
case 4: MinPoint.z = Value; break;
case 5: MaxPoint.z = Value; break;
default: break;
}
}
float GetMinMaxValueByAxis(uint Axis, float3 MinPoint, float3 MaxPoint)
{
switch (Axis)
{
case 0: return MinPoint.x;
case 1: return MaxPoint.x;
case 2: return MinPoint.y;
case 3: return MaxPoint.y;
case 4: return MinPoint.z;
case 5: return MaxPoint.z;
default: return 0.f;
}
}
void SkinClusterBounds(FCluster Cluster, FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, inout float3 OutBoxCenter, inout float3 OutBoxExtent)
{
float3 MinBounds = float(999999999.0f).xxx;
float3 MaxBounds = float(-999999999.0f).xxx;
const uint IBI = 0xFFFFFFFFu;
uint ClusterBoneIndexes[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
float ClusterBoneWeightMin[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
float ClusterBoneWeightMax[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
float3 ClusterBoneBoundsMin[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
float3 ClusterBoneBoundsMax[NANITE_MAX_CLUSTER_BONE_INFLUENCES];
for (uint i = 0; i < NANITE_MAX_CLUSTER_BONE_INFLUENCES; i++)
{
ClusterBoneIndexes[i] = IBI;
ClusterBoneWeightMin[i] = 0.0f;
ClusterBoneWeightMax[i] = 0.0f;
ClusterBoneBoundsMin[i] = 0.0f;
ClusterBoneBoundsMax[i] = 0.0f;
}
// Load cooked data
for (uint i = 0; i < Cluster.NumClusterBoneInfluences; ++i)
{
FClusterBoneInfluence BoneInfluence = DecodeClusterBoneInfluence(Cluster, i);
ClusterBoneIndexes[i] = BoneInfluence.BoneIndex;
ClusterBoneWeightMin[i] = BoneInfluence.MinWeight;
ClusterBoneWeightMax[i] = BoneInfluence.MaxWeight;
ClusterBoneBoundsMin[i] = BoneInfluence.BoundMin;
ClusterBoneBoundsMax[i] = BoneInfluence.BoundMax;
}
// Deform each of the cluster bone bboxes by their associated bone transform.
uint ClusterBoneCount = 0;
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
{
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
{
break;
}
ClusterBoneCount++;
float3 BoneBoundsMin = ClusterBoneBoundsMin[ClusterBoneIndex];
float3 BoneBoundsMax = ClusterBoneBoundsMax[ClusterBoneIndex];
float3 BoneBoundsCorners[8] = {
float3(BoneBoundsMin.x, BoneBoundsMin.y, BoneBoundsMin.z),
float3(BoneBoundsMax.x, BoneBoundsMin.y, BoneBoundsMin.z),
float3(BoneBoundsMin.x, BoneBoundsMax.y, BoneBoundsMin.z),
float3(BoneBoundsMax.x, BoneBoundsMax.y, BoneBoundsMin.z),
float3(BoneBoundsMin.x, BoneBoundsMin.y, BoneBoundsMax.z),
float3(BoneBoundsMax.x, BoneBoundsMin.y, BoneBoundsMax.z),
float3(BoneBoundsMin.x, BoneBoundsMax.y, BoneBoundsMax.z),
float3(BoneBoundsMax.x, BoneBoundsMax.y, BoneBoundsMax.z)
};
uint BoneIndex = ClusterBoneIndexes[ClusterBoneIndex];
if (BoneIndex == IBI)
{
break;
}
float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
ClusterBoneBoundsMin[ClusterBoneIndex] = float(999999999.0f).xxx;
ClusterBoneBoundsMax[ClusterBoneIndex] = float(-999999999.0f).xxx;
for (uint CornerIndex = 0; CornerIndex < 8; ++CornerIndex)
{
float3 SkinnedClusterBoxCorner = mul(float4(BoneBoundsCorners[CornerIndex], 1.0f), BoneTransform);
ClusterBoneBoundsMin[ClusterBoneIndex] = min(ClusterBoneBoundsMin[ClusterBoneIndex], SkinnedClusterBoxCorner);
ClusterBoneBoundsMax[ClusterBoneIndex] = max(ClusterBoneBoundsMax[ClusterBoneIndex], SkinnedClusterBoxCorner);
}
}
for (uint Axis = 0; Axis < 6; ++Axis)
{
// Find the extrema and second extrema bone values for this axis based on their deformed bbox.
uint ExtremaIndex = IBI;
uint ExtremaIndex2 = IBI;
float ExtremaValue = (Axis % 2 == 0) ? 999999999.0f : -999999999.0f;
float ExtremaValue2 = (Axis % 2 == 0) ? 999999999.0f : -999999999.0f;
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
{
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
{
break;
}
float BBoxValue = GetMinMaxValueByAxis(Axis, ClusterBoneBoundsMin[ClusterBoneIndex], ClusterBoneBoundsMax[ClusterBoneIndex]);
if (Axis % 2 == 0) // Negative
{
if (BBoxValue < ExtremaValue)
{
if (ExtremaValue < ExtremaValue2)
{
ExtremaIndex2 = ExtremaIndex;
ExtremaValue2 = ExtremaValue;
}
ExtremaIndex = ClusterBoneIndex;
ExtremaValue = BBoxValue;
}
else if (BBoxValue < ExtremaValue2)
{
ExtremaIndex2 = ClusterBoneIndex;
ExtremaValue2 = BBoxValue;
}
}
else
{
if (BBoxValue > ExtremaValue)
{
if (ExtremaValue > ExtremaValue2)
{
ExtremaIndex2 = ExtremaIndex;
ExtremaValue2 = ExtremaValue;
}
ExtremaIndex = ClusterBoneIndex;
ExtremaValue = BBoxValue;
}
else if (BBoxValue > ExtremaValue2)
{
ExtremaIndex2 = ClusterBoneIndex;
ExtremaValue2 = BBoxValue;
}
}
}
float SkinnedValue = ClusterBoneWeightMax[ExtremaIndex] * ExtremaValue;
if (ExtremaIndex2 != IBI)
{
float Weight2 = 1.0f - ClusterBoneWeightMax[ExtremaIndex];
for (uint ClusterBoneIndex = 0; ClusterBoneIndex < NANITE_MAX_CLUSTER_BONE_INFLUENCES; ++ClusterBoneIndex)
{
if (ClusterBoneIndexes[ClusterBoneIndex] == IBI)
{
break;
}
if (ClusterBoneIndex != ExtremaIndex && ClusterBoneIndex != ExtremaIndex2)
{
float MinWeight = ClusterBoneWeightMin[ClusterBoneIndex];
Weight2 -= MinWeight;
SkinnedValue += MinWeight * GetMinMaxValueByAxis(Axis, ClusterBoneBoundsMin[ClusterBoneIndex], ClusterBoneBoundsMax[ClusterBoneIndex]);
}
}
SkinnedValue += Weight2 * ExtremaValue2;
}
SetMinMaxValueByAxis(SkinnedValue, Axis, MinBounds, MaxBounds);
}
OutBoxCenter = (MaxBounds + MinBounds) * 0.5f;
OutBoxExtent = (MaxBounds - MinBounds) * 0.5f;
}
#else
void SkinClusterBounds(FCluster Cluster, FInstanceSceneData InstanceData, FNaniteSkinningHeader SkinningHeader, inout float3 BoxCenter, inout float3 BoxExtent)
{
float3 BoundsMin = 1e20f;
float3 BoundsMax = -1e20f;
for (uint i = 0; i < Cluster.NumClusterBoneInfluences; ++i)
{
const FClusterBoneInfluence BoneInfluence = DecodeClusterBoneInfluence(Cluster, i);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneInfluence.BoneIndex);
const float3 Center = mul(float4(BoxCenter, 1.0f), BoneTransform);
const float3 Delta = mul(BoxExtent, abs((float3x3)BoneTransform));
BoundsMin = min(BoundsMin, Center - Delta);
BoundsMax = max(BoundsMax, Center + Delta);
}
BoxCenter = (BoundsMax + BoundsMin) * 0.5f;
BoxExtent = (BoundsMax - BoundsMin) * 0.5f;
}
#endif
ENCODED_VELOCITY_TYPE CalculateNaniteVelocity(FNaniteView NaniteView, FInstanceSceneData InstanceData, FCluster Cluster, FVisibleCluster VisibleCluster, float4 SvPosition, uint TriIndex, uint PrimitiveFlags, bool bWPOEnabled)
{
#if VELOCITY_EXPORT
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
const float4 ScreenPos = SvPositionToScreenPosition(SvPosition);
float4 ScreenPosPrev;
const bool bOutputVelocity = !bWPOEnabled && (PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_OUTPUT_VELOCITY) != 0;
if (!bOutputVelocity)
{
return (ENCODED_VELOCITY_TYPE)0;
}
#if USE_SKINNING
// TODO: Skipping this means the AA smears the foliage such that it looks like there's movement, is this a good idea or a bug?
bool bActiveSkinning = Cluster.bSkinning && GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId).bIsDeforming;
BRANCH
if ((PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && bActiveSkinning)
{
FNaniteSkinningHeader SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
BRANCH
if (Cluster.bVoxel)
{
const FDFVector3 WorldPos = SvPositionToWorld(SvPosition);
const float3 SkinnedLocalPos = DFMultiplyDemote(WorldPos, InstanceData.WorldToLocal);
float4x3 LocalToSkinned = 0;
float4x3 PrevLocalToSkinned = 0;
uint VertIndex = 0;
#if NANITE_PER_VOXEL_BRICK_SKINNING
VertIndex = DecodeBrick(Cluster, TriIndex).VertOffset;
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumVertexBoneInfluences; ++InfluenceIndex)
{
uint BoneIndex = 0;
float BoneWeight = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, VertIndex, InfluenceIndex, BoneIndex, BoneWeight);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex);
const float4x3 PrevBoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex);
LocalToSkinned += BoneTransform * BoneWeight;
PrevLocalToSkinned += PrevBoneTransform * BoneWeight;
}
#else
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < Cluster.NumClusterBoneInfluences; ++InfluenceIndex)
{
FVoxelBoneInfluence BoneInfluence = DecodeVoxelBoneInfluence(Cluster, InfluenceIndex);
const float4x3 BoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneInfluence.BoneIndex);
const float4x3 PrevBoneTransform = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneInfluence.BoneIndex);
LocalToSkinned += BoneTransform * BoneInfluence.Weight;
PrevLocalToSkinned += PrevBoneTransform * BoneInfluence.Weight;
}
#endif
const float3x3 SkinnedToLocal3x3 = Inverse(float3x3(LocalToSkinned[0], LocalToSkinned[1], LocalToSkinned[2]));
const float3 LocalPos = mul(SkinnedLocalPos - LocalToSkinned[3], SkinnedToLocal3x3);
const float3 PrevSkinnedLocalPos = mul(float4(LocalPos, 1.0f), PrevLocalToSkinned).xyz;
const float3 PrevWorldPos = mul(float4(PrevSkinnedLocalPos, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
ScreenPosPrev = mul(float4(PrevWorldPos, 1), NaniteView.PrevTranslatedWorldToClip);
}
else
{
const uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
float3 PointLocal[3];
FetchLocalNaniteTrianglePositions(InstanceData, Cluster, VisibleCluster, TriIndices, PointLocal);
float3 CurrentPosition[3];
CurrentPosition[0] = float3(0.0f, 0.0f, 0.0f);
CurrentPosition[1] = float3(0.0f, 0.0f, 0.0f);
CurrentPosition[2] = float3(0.0f, 0.0f, 0.0f);
float3 PreviousPosition[3];
PreviousPosition[0] = float3(0.0f, 0.0f, 0.0f);
PreviousPosition[1] = float3(0.0f, 0.0f, 0.0f);
PreviousPosition[2] = float3(0.0f, 0.0f, 0.0f);
LOOP
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
{
uint BoneIndex0 = 0;
float BoneWeight0 = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.x, InfluenceIndex, BoneIndex0, BoneWeight0);
uint BoneIndex1 = 0;
float BoneWeight1 = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.y, InfluenceIndex, BoneIndex1, BoneWeight1);
uint BoneIndex2 = 0;
float BoneWeight2 = 0.0f;
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.z, InfluenceIndex, BoneIndex2, BoneWeight2);
float4x3 CurrentBoneTransform[3];
CurrentBoneTransform[0] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex0);
CurrentBoneTransform[1] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex1);
CurrentBoneTransform[2] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex2);
float4x3 PreviousBoneTransform[3];
PreviousBoneTransform[0] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex0);
PreviousBoneTransform[1] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex1);
PreviousBoneTransform[2] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + SkinningHeader.MaxTransformCount + InstanceData.SkinningData + BoneIndex2);
CurrentPosition[0] += mul(float4(PointLocal[0], 1.0f), CurrentBoneTransform[0]) * BoneWeight0;
CurrentPosition[1] += mul(float4(PointLocal[1], 1.0f), CurrentBoneTransform[1]) * BoneWeight1;
CurrentPosition[2] += mul(float4(PointLocal[2], 1.0f), CurrentBoneTransform[2]) * BoneWeight2;
PreviousPosition[0] += mul(float4(PointLocal[0], 1.0f), PreviousBoneTransform[0]) * BoneWeight0;
PreviousPosition[1] += mul(float4(PointLocal[1], 1.0f), PreviousBoneTransform[1]) * BoneWeight1;
PreviousPosition[2] += mul(float4(PointLocal[2], 1.0f), PreviousBoneTransform[2]) * BoneWeight2;
}
const float3 PointWorld0 = mul(float4(CurrentPosition[0], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
const float3 PointWorld1 = mul(float4(CurrentPosition[1], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
const float3 PointWorld2 = mul(float4(CurrentPosition[2], 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
const float4 PointClip0 = mul(float4(PointWorld0, 1), NaniteView.TranslatedWorldToClip);
const float4 PointClip1 = mul(float4(PointWorld1, 1), NaniteView.TranslatedWorldToClip);
const float4 PointClip2 = mul(float4(PointWorld2, 1), NaniteView.TranslatedWorldToClip);
// Calculate perspective correct barycentric coordinates with screen derivatives
const float2 PixelClip = (SvPosition.xy - NaniteView.ViewRect.xy) * NaniteView.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
const FBarycentrics Barycentrics = CalculateTriangleBarycentrics(PixelClip, PointClip0, PointClip1, PointClip2, NaniteView.ViewSizeAndInvSize.zw);
const float3 PrevPointLocal = Lerp(PreviousPosition[0], PreviousPosition[1], PreviousPosition[2], Barycentrics).Value;
float3 PrevPointWorld = mul(float4(PrevPointLocal.xyz, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
ScreenPosPrev = mul(float4(PrevPointWorld, 1), NaniteView.PrevTranslatedWorldToClip);
}
}
else
#endif
{
const FDFVector3 WorldPos = SvPositionToWorld(SvPosition);
const float3 LocalPos = DFMultiplyDemote(WorldPos, InstanceData.WorldToLocal);
const float3 WorldPosPrev = mul(float4(LocalPos, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
ScreenPosPrev = mul(float4(WorldPosPrev, 1), NaniteView.PrevTranslatedWorldToClip);
}
const float3 Velocity = Calculate3DVelocity(ScreenPos, ScreenPosPrev);
return EncodeVelocityToTexture(Velocity, (PrimitiveFlags & PRIMITIVE_SCENE_DATA_FLAG_HAS_PIXEL_ANIMATION) != 0);
#else
return (ENCODED_VELOCITY_TYPE)0;
#endif
}