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

226 lines
5.5 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "HairStrandsBindingCommon.ush"
#if SHADER_FOLLICLE_MASK
float2 OutputResolution;
uint MaxRootCount;
uint MaxUniqueTriangleIndex;
uint Channel;
uint KernelSizeInPixels;
#define UV_MESH 0
#define UV_ROOT 1
#if PERMUTATION_UV_TYPE == UV_MESH
Buffer<float4> UniqueTrianglePositionBuffer;
Buffer<uint> RootToUniqueTriangleIndexBuffer;
Buffer<uint> RootBarycentricBuffer;
#endif
#if PERMUTATION_UV_TYPE == UV_ROOT
Buffer<float2> RootUVsBuffer;
#endif
void MainVS(
uint VertexId : SV_VertexID,
out float2 OutVertexUV : TRI_UVs,
out float2 OutRootUV : ROOT_UV,
out float4 OutPosition : SV_POSITION)
{
OutVertexUV = 0;
OutRootUV = 0;
OutPosition = float4(POSITIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY, 1);
ResolvedView = ResolveView();
const uint RootIndex = VertexId / 3;
const uint TriangleVertex = (VertexId % 3);
#if PERMUTATION_UV_TYPE == UV_MESH
uint UniqueTriangleIndex = 0;
if (RootIndex < MaxRootCount)
{
UniqueTriangleIndex = RootToUniqueTriangleIndexBuffer[RootIndex];
}
// TODO -> follicle requires UVs
if (UniqueTriangleIndex > MaxUniqueTriangleIndex) return;
const FHairMeshBasis Tri = GetHairMeshBasis(UniqueTriangleIndex, UniqueTrianglePositionBuffer, float3(0,0,0));
const uint EncodedBarycentric = RootBarycentricBuffer[RootIndex];
const float3 Barycentric = UnpackBarycentrics(EncodedBarycentric);
float2 RootUV = float2(0, 0); // TODO Barycentric.x * Tri.UV0 + Barycentric.y * Tri.UV1 + Barycentric.z * Tri.UV2;
#endif
#if PERMUTATION_UV_TYPE == UV_ROOT
float2 RootUV = RootUVsBuffer[RootIndex];
#endif
const float2 KernelRadius = float2(KernelSizeInPixels, KernelSizeInPixels);
const float2 QuadExtent = (KernelRadius / OutputResolution);
float2 UV = RootUV;
if (TriangleVertex == 0)
{
UV += float2(-QuadExtent.x, -QuadExtent.y);
}
if (TriangleVertex == 1)
{
UV += float2(3 * QuadExtent.x, -QuadExtent.y);
}
if (TriangleVertex == 2)
{
UV += float2(-QuadExtent.x, 3 * QuadExtent.y);
}
OutRootUV = RootUV;
OutVertexUV = UV;
OutPosition = float4(OutVertexUV * 2 - 1, 0.5, 1);
}
void MainPS(
in float2 VertexUV : TRI_UVs,
in float2 RootUV : ROOT_UV,
out float4 OutColor : SV_Target0)
{
// In a distance of 2 pixels, write a gradient with its pic at the root
OutColor = 0;
const float KernelRadius = KernelSizeInPixels;
const float UVDistance = length(VertexUV - RootUV) * OutputResolution.x;
const float Value = 1 - saturate(UVDistance / KernelRadius);
if (Value == 0)
{
discard;
}
if (Channel==0)
{
OutColor = float4(Value, 0, 0, 0);
}
else if (Channel==1)
{
OutColor = float4(0, Value, 0, 0);
}
else if (Channel==2)
{
OutColor = float4(0, 0, Value, 0);
}
else if (Channel==3)
{
OutColor = float4(0, 0, 0, Value);
}
}
#endif // SHADER_FOLLICLE_MASK
#if SHADER_GENERATE_MIPS
uint Resolution;
uint SourceMip;
uint TargetMip;
Texture2D<float4> InTexture;
RWTexture2D<float4> OutTexture;
SamplerState LinearSampler;
struct Gaussian3x3
{
float Weight[9];
int Offset;
int Width;
};
Gaussian3x3 Initialize3x3()
{
Gaussian3x3 O;
O.Weight[0] = 1.0; O.Weight[1] = 2.0; O.Weight[2] = 1.0;
O.Weight[3] = 2.0; O.Weight[4] = 4.0; O.Weight[5] = 2.0;
O.Weight[6] = 1.0; O.Weight[7] = 2.0; O.Weight[8] = 1.0;
O.Offset = 1;
O.Width = 3;
return O;
}
struct Gaussian5x5
{
float Weight[25];
int Offset;
int Width;
};
Gaussian5x5 Initialize5x5()
{
Gaussian5x5 O;
O.Weight[0] = 1; O.Weight[1] = 4; O.Weight[2] = 7; O.Weight[3] = 4; O.Weight[4] = 1;
O.Weight[5] = 4; O.Weight[6] = 16; O.Weight[7] = 26; O.Weight[8] = 16; O.Weight[9] = 4;
O.Weight[10] = 7; O.Weight[11] = 26; O.Weight[12] = 41; O.Weight[13] = 26; O.Weight[14] = 7;
O.Weight[15] = 4; O.Weight[16] = 16; O.Weight[17] = 26; O.Weight[18] = 16; O.Weight[19] = 4;
O.Weight[20] = 1; O.Weight[21] = 4; O.Weight[22] = 7; O.Weight[23] = 4; O.Weight[24] = 1;
O.Offset = 2;
O.Width = 5;
return O;
}
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Gaussian
#if 1
//const Gaussian3x3 Kernel = Initialize3x3();
const Gaussian5x5 Kernel = Initialize5x5();
const uint2 OutCoord = DispatchThreadId.xy;
const uint OutResolution = Resolution >> TargetMip;
if (OutCoord.x >= OutResolution || OutCoord.y >= OutResolution)
return;
const float InvResolution = 1.f / float(OutResolution);
const float KernelDistance = 0.3f;
float4 Filtered = 0;
float AccW = 0;
const float2 CenterUV = (float2(OutCoord)) * InvResolution;
for (int y = -Kernel.Offset; y <= Kernel.Offset; ++y)
for (int x = -Kernel.Offset; x <= Kernel.Offset; ++x)
{
const uint Index = (Kernel.Offset+x) + (Kernel.Offset+y) * Kernel.Width;
const float2 UV = CenterUV + float2(x, y) * KernelDistance * InvResolution;
const float W = Kernel.Weight[Index];
AccW += W;
Filtered += W * InTexture.SampleLevel(LinearSampler, UV, 0);
}
OutTexture[OutCoord] = Filtered / AccW;
#endif
// Box
#if 0
if (OutCoord.x >= OutResolution || OutCoord.y >= OutResolution)
return;
const uint2 OutCoord = DispatchThreadId.xy;
const uint2 InCoord0 = uint2(OutCoord.x << 1, OutCoord.y << 1);
const uint2 InCoord1 = InCoord0 + uint2(1, 0);
const uint2 InCoord2 = InCoord0 + uint2(0, 1);
const uint2 InCoord3 = InCoord0 + uint2(1, 1);
const float4 V0 = InTexture[InCoord0];
const float4 V1 = InTexture[InCoord1];
const float4 V2 = InTexture[InCoord2];
const float4 V3 = InTexture[InCoord3];
OutTexture[OutCoord] = float4(V0 + V1 + V2 + V3) * 0.25f;
#endif
}
#endif // SHADER_GENERATE_MIPS