Files
UnrealEngine/Engine/Shaders/Private/HairStrands/HairStrandsMesh.usf
2025-05-18 13:04:45 +08:00

381 lines
12 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "HairStrandsBindingCommon.ush"
#include "HairstrandsVertexFactoryCommon.ush"
////////////////////////////////////////////////////////////////////////
// Deform hair mesh basd on RBF weights
#if SHADER_HAIRMESHES
uint VertexCount;
uint MaxSampleCount;
StructuredBuffer<float4> RestSamplePositionsBuffer;
StructuredBuffer<float4> MeshSampleWeightsBuffer;
Buffer<float4> RestPositionBuffer;
RWBuffer<float4> OutDeformedPositionBuffer;
[numthreads(128, 1, 1)]
void MainHairMeshesCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint VertexIndex = DispatchThreadId.x;
if (VertexIndex >= VertexCount)
return;
const float3 RestPosition = RestPositionBuffer[VertexIndex].xyz;
const float3 DeformedPosition = ApplyRBF(RestPosition, MaxSampleCount, RestSamplePositionsBuffer, MeshSampleWeightsBuffer);
OutDeformedPositionBuffer[VertexIndex] = float4(DeformedPosition,1);
}
#endif // SHADER_HAIRMESHES
////////////////////////////////////////////////////////////////////////
// Init RBF point based on mesh position
#if SHADER_SAMPLE_INIT
#if PERMUTATION_POSITION_TYPE == 0
Buffer<float> MeshPositionBuffer0;
Buffer<float> MeshPositionBuffer1;
#elif PERMUTATION_POSITION_TYPE == 1
Buffer<float> RDGMeshPositionBuffer0;
Buffer<float> RDGMeshPositionBuffer1;
#define MeshPositionBuffer0 RDGMeshPositionBuffer0
#define MeshPositionBuffer1 RDGMeshPositionBuffer1
#endif
uint MaxSectionCount;
uint MaxVertexCount;
uint MaxSampleCount;
Buffer<uint> SampleIndicesAndSectionsBuffer;
StructuredBuffer<uint> MeshSectionBuffer;
RWStructuredBuffer<float4> OutSamplePositionsBuffer;
[numthreads(GROUP_SIZE, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint SampleIndex = DispatchThreadId.x;
if (SampleIndex >= MaxSampleCount)
return;
const FHairTriangleIndex IndexAndSection = UnpackTriangleIndex(SampleIndicesAndSectionsBuffer[SampleIndex]);
const uint VertexIndex = IndexAndSection.TriangleIndex;
const uint SectionIndex = IndexAndSection.SectionIndex;
if (VertexIndex >= MaxVertexCount || SectionIndex >= MaxSectionCount)
return;
const bool bBuffer0 = MeshSectionBuffer[SectionIndex] == 0;
float3 SamplePosition = float3(0,0,0);
if (bBuffer0)
{
SamplePosition.x = MeshPositionBuffer0.Load(VertexIndex * 3 + 0);
SamplePosition.y = MeshPositionBuffer0.Load(VertexIndex * 3 + 1);
SamplePosition.z = MeshPositionBuffer0.Load(VertexIndex * 3 + 2);
}
else
{
SamplePosition.x = MeshPositionBuffer1.Load(VertexIndex * 3 + 0);
SamplePosition.y = MeshPositionBuffer1.Load(VertexIndex * 3 + 1);
SamplePosition.z = MeshPositionBuffer1.Load(VertexIndex * 3 + 2);
}
OutSamplePositionsBuffer[SampleIndex] = float4(SamplePosition,1);
}
#endif // SHADER_SAMPLE_INIT
////////////////////////////////////////////////////////////////////////
// Update RBF points
#if SHADER_SAMPLE_UPDATE
// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
uint MaxSampleCount;
StructuredBuffer<float> InterpolationWeightsBuffer;
StructuredBuffer<float4> SampleRestPositionsBuffer;
StructuredBuffer<float4> SampleDeformedPositionsBuffer;
RWStructuredBuffer<float4> OutSampleDeformationsBuffer;
[numthreads(128, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint SampleIndex = DispatchThreadId.x;
if (SampleIndex >= HAIR_RBF_ENTRY_COUNT(MaxSampleCount))
{
return;
}
uint WeightsBaseOffset = SampleIndex * HAIR_RBF_ENTRY_COUNT(MaxSampleCount);
float3 SampleDeformation = float3(0,0,0);
for(uint i = 0; i < MaxSampleCount; ++i)
{
//@todo-laura.hermanns - Incrementing '++WeightsBaseOffset' instead of using primary iterator '+i' results in visual artifacts with Metal backend
SampleDeformation += InterpolationWeightsBuffer[WeightsBaseOffset + i] * (SampleDeformedPositionsBuffer[i] - SampleRestPositionsBuffer[i]).xyz;
}
OutSampleDeformationsBuffer[SampleIndex] = float4(SampleDeformation,1);
}
#endif // SHADER_SAMPLE_UPDATE
////////////////////////////////////////////////////////////////////////
// Update strands root triangle positions
#if SHADER_MESH_UPDATE
#ifndef GROUP_SIZE
#error GROUP_SIZE needs to be defined
#endif
struct FMeshSectionData
{
uint MaxIndexCount;
uint MaxVertexCount;
uint IndexOffset;
uint UVsChannelOffset;
uint UVsChannelCount;
uint bIsSwapped;
uint bUseFaceNormal;
};
uint TangentFormat;
uint MaxSectionCount;
uint MaxUniqueTriangleCount;
Buffer<uint> MeshIndexBuffer;
Buffer<uint> UniqueTriangleIndices;
StructuredBuffer<uint4> MeshSectionBuffer;
// Note on buffer input type
// There are two different input buffer permutation, as if skin cache is enabled, we are using raw RHI SRV
// (while the skin cache system is not converted to RDG). If skin cache is disable, a manual skin compute
// update happens in the hair system, and its output are RDG buffers.
#if PERMUTATION_POSITION_TYPE == 0
Buffer<float4> MeshTangentBuffer;
Buffer<float> MeshPositionBuffer;
Buffer<float> MeshPreviousPositionBuffer;
#else
Buffer<float4> RDGMeshTangentBuffer;
Buffer<float> RDGMeshPositionBuffer;
Buffer<float> RDGMeshPreviousPositionBuffer;
#define MeshTangentBuffer RDGMeshTangentBuffer
#define MeshPositionBuffer RDGMeshPositionBuffer
#define MeshPreviousPositionBuffer RDGMeshPreviousPositionBuffer
#endif
RWBuffer<float4> OutUniqueTriangleCurrPosition;
#if PERMUTATION_PREVIOUS
RWBuffer<float4> OutUniqueTrianglePrevPosition;
#endif
FMeshSectionData ReadMeshSectionData(uint InSectionIndex)
{
const uint4 Packed = MeshSectionBuffer[InSectionIndex];
FMeshSectionData Out;
Out.MaxIndexCount = Packed.x;
Out.MaxVertexCount = Packed.y;
Out.IndexOffset = Packed.z;
Out.UVsChannelOffset = BitFieldExtractU32(Packed.w, 8, 0);
Out.UVsChannelCount = BitFieldExtractU32(Packed.w, 8, 8);
Out.bIsSwapped = BitFieldExtractU32(Packed.w, 1, 16);
Out.bUseFaceNormal = BitFieldExtractU32(Packed.w, 1, 17);
return Out;
}
FHairMeshTriangle GetHairMeshTriangle(uint InTriangleIndex, FMeshSectionData MeshSectionData, bool bPrevious, bool bUseVertexTangent)
{
const bool bSamplePrev = (MeshSectionData.bIsSwapped > 0 && !bPrevious) || (MeshSectionData.bIsSwapped == 0 && bPrevious);
if (bSamplePrev)
{
return GetHairMeshTriangle(
InTriangleIndex,
MeshPreviousPositionBuffer,
MeshTangentBuffer,
MeshIndexBuffer,
MeshSectionData.IndexOffset,
MeshSectionData.MaxIndexCount,
MeshSectionData.MaxVertexCount,
TangentFormat,
bUseVertexTangent);
}
else
{
return GetHairMeshTriangle(
InTriangleIndex,
MeshPositionBuffer,
MeshTangentBuffer,
MeshIndexBuffer,
MeshSectionData.IndexOffset,
MeshSectionData.MaxIndexCount,
MeshSectionData.MaxVertexCount,
TangentFormat,
bUseVertexTangent);
}
}
float3 GetTriangleFaceNormal(FHairMeshTriangle Tri)
{
const float3 E0 = Tri.P1 - Tri.P0;
const float3 E1 = Tri.P2 - Tri.P0;
return normalize(cross(E1, E0));
}
[numthreads(GROUP_SIZE, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint UniqueTriangleIndex = DispatchThreadId.x;
if (UniqueTriangleIndex >= MaxUniqueTriangleCount)
{
return;
}
const FHairTriangleIndex TriangleIndex = UnpackTriangleIndex(UniqueTriangleIndices[UniqueTriangleIndex]);
if (TriangleIndex.SectionIndex < MaxSectionCount)
{
const FMeshSectionData MeshSectionData = ReadMeshSectionData(TriangleIndex.SectionIndex);
// Compute the slot index of the resources for the section index (this is done in multiple pass
{
FHairMeshTriangle Tri = GetHairMeshTriangle(TriangleIndex.TriangleIndex, MeshSectionData, false, !MeshSectionData.bUseFaceNormal);
if (MeshSectionData.bUseFaceNormal)
{
Tri.N0 = GetTriangleFaceNormal(Tri);
Tri.N1 = Tri.N0;
Tri.N2 = Tri.N0;
}
OutUniqueTriangleCurrPosition[UniqueTriangleIndex * 3 + 0] = float4(Tri.P0, PackVertexNormalToFloat(Tri.N0));
OutUniqueTriangleCurrPosition[UniqueTriangleIndex * 3 + 1] = float4(Tri.P1, PackVertexNormalToFloat(Tri.N1));
OutUniqueTriangleCurrPosition[UniqueTriangleIndex * 3 + 2] = float4(Tri.P2, PackVertexNormalToFloat(Tri.N2));
}
#if PERMUTATION_PREVIOUS
{
FHairMeshTriangle Tri = GetHairMeshTriangle(TriangleIndex.TriangleIndex, MeshSectionData, true, !MeshSectionData.bUseFaceNormal);
if (MeshSectionData.bUseFaceNormal)
{
Tri.N0 = GetTriangleFaceNormal(Tri);
Tri.N1 = Tri.N0;
Tri.N2 = Tri.N0;
}
OutUniqueTrianglePrevPosition[UniqueTriangleIndex * 3 + 0] = float4(Tri.P0, PackVertexNormalToFloat(Tri.N0));
OutUniqueTrianglePrevPosition[UniqueTriangleIndex * 3 + 1] = float4(Tri.P1, PackVertexNormalToFloat(Tri.N1));
OutUniqueTrianglePrevPosition[UniqueTriangleIndex * 3 + 2] = float4(Tri.P2, PackVertexNormalToFloat(Tri.N2));
}
#endif
}
}
#endif // SHADER_MESH_UPDATE
////////////////////////////////////////////////////////////////////////
// Update hair strands offset position
#if SHADER_OFFSET_UPDATE
#if PERMUTATION_USE_GPU_OFFSET
Buffer<float4> RootTriangleCurrPositionBuffer;
float3 GetOffsetCurrPosition() { return RootTriangleCurrPositionBuffer[0].xyz; }
#if PERMUTATION_PREVIOUS
Buffer<float4> RootTrianglePrevPositionBuffer;
float3 GetOffsetPrevPosition() { return RootTrianglePrevPositionBuffer[0].xyz; }
#endif
#else
float3 CPUCurrPositionOffset;
float3 CPUPrevPositionOffset;
float3 GetOffsetCurrPosition() { return CPUCurrPositionOffset; }
float3 GetOffsetPrevPosition() { return CPUPrevPositionOffset; }
#endif
RWStructuredBuffer<float4> OutCurrOffsetBuffer;
#if PERMUTATION_PREVIOUS
RWStructuredBuffer<float4> OutPrevOffsetBuffer;
#endif
uint UpdateType;
uint CardLODIndex;
uint InstanceRegisteredIndex;
[numthreads(1, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (DispatchThreadId.x > 0)
return;
WritePositionOffset(OutCurrOffsetBuffer, InstanceRegisteredIndex, UpdateType, HAIR_CURR, CardLODIndex, GetOffsetCurrPosition());
#if PERMUTATION_PREVIOUS
WritePositionOffset(OutPrevOffsetBuffer, InstanceRegisteredIndex, UpdateType, HAIR_PREV, CardLODIndex, GetOffsetPrevPosition());
#endif
}
#endif // SHADER_OFFSET_UPDATE
////////////////////////////////////////////////////////////////////////
// Update hair strands offset position
#if SHADER_RESOURCE_TRANSITION
#define TEMPLATE_BUFFERS(TPrefix, TSuffix) \
TPrefix##Buffers_0##TSuffix;\
TPrefix##Buffers_1##TSuffix;\
TPrefix##Buffers_2##TSuffix;\
TPrefix##Buffers_3##TSuffix;\
TPrefix##Buffers_4##TSuffix;\
TPrefix##Buffers_5##TSuffix;\
TPrefix##Buffers_6##TSuffix;\
TPrefix##Buffers_7##TSuffix;\
TPrefix##Buffers_8##TSuffix;\
TPrefix##Buffers_9##TSuffix;\
TPrefix##Buffers_10##TSuffix;\
TPrefix##Buffers_11##TSuffix;\
TPrefix##Buffers_12##TSuffix;\
TPrefix##Buffers_13##TSuffix;\
TPrefix##Buffers_14##TSuffix;\
TPrefix##Buffers_15##TSuffix;
TEMPLATE_BUFFERS(StructuredBuffer<float4> Structured, )
TEMPLATE_BUFFERS(Buffer<uint> VertexUInt, )
TEMPLATE_BUFFERS(Buffer<uint4> VertexUInt4, )
TEMPLATE_BUFFERS(Buffer<float4> VertexFloat4, )
TEMPLATE_BUFFERS(ByteAddressBuffer ByteAddress, )
RWStructuredBuffer<uint> DummyOutput;
uint DummyValue;
uint NumVertexUint;
uint NumVertexUint4;
uint NumVertexFloat4;
[numthreads(1, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (DummyValue > 0)
{
uint Acc = 0;
#if PERMUTATION_BUFFER_TYPE == 0
TEMPLATE_BUFFERS(Acc += Structured, [0].x)
#elif PERMUTATION_BUFFER_TYPE == 1
TEMPLATE_BUFFERS(Acc += VertexUInt, [0])
#elif PERMUTATION_BUFFER_TYPE == 2
TEMPLATE_BUFFERS(Acc += VertexUInt4, [0].x)
#elif PERMUTATION_BUFFER_TYPE == 3
TEMPLATE_BUFFERS(Acc += VertexFloat4, [0].x)
#elif PERMUTATION_BUFFER_TYPE == 4
TEMPLATE_BUFFERS(Acc += ByteAddress, .Load(0))
#else
#error Not implemented
#endif
DummyOutput[0] = Acc;
}
}
#endif // SHADER_RESOURCE_TRANSITION