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

233 lines
7.1 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Common.ush"
#include "ShaderPrint.ush"
#include "ColorMap.ush"
////////////////////////////////////////////////////////////////////////////////////////////////
// Add texture
#if SHADER_ADD_TEXTURE_CS
Texture2D<float4> InTexture_0;
Texture2D<float4> InTexture_1;
Texture2D<float4> InTexture_2;
Texture2D<float4> InTexture_3;
Texture2D<float4> InTexture_4;
Texture2D<float4> InTexture_5;
Texture2D<float4> InTexture_6;
Texture2D<float4> InTexture_7;
uint4 InSliceIndex[8];
SamplerState InSampler_0;
SamplerState InSampler_1;
SamplerState InSampler_2;
SamplerState InSampler_3;
SamplerState InSampler_4;
SamplerState InSampler_5;
SamplerState InSampler_6;
SamplerState InSampler_7;
int2 AtlasResolution;
uint AtlasSliceCount;
uint ValidCount;
RWTexture2DArray<float> OutAtlasTexture;
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (all(DispatchThreadId.xy < uint2(AtlasResolution)))
{
const uint2 DstPixelPos = DispatchThreadId.xy;
uint DstSlice = 0;
const float2 SrcUV = (DstPixelPos + 0.5) / float2(AtlasResolution);
const uint SrcSlice = DispatchThreadId.z;
if (SrcSlice < ValidCount)
{
float Color = 0;
switch (SrcSlice)
{
case 0: Color = InTexture_0.SampleLevel(InSampler_0, SrcUV, 0).x; DstSlice = InSliceIndex[0].x; break;
case 1: Color = InTexture_1.SampleLevel(InSampler_1, SrcUV, 0).x; DstSlice = InSliceIndex[1].x; break;
case 2: Color = InTexture_2.SampleLevel(InSampler_2, SrcUV, 0).x; DstSlice = InSliceIndex[2].x; break;
case 3: Color = InTexture_3.SampleLevel(InSampler_3, SrcUV, 0).x; DstSlice = InSliceIndex[3].x; break;
case 4: Color = InTexture_4.SampleLevel(InSampler_4, SrcUV, 0).x; DstSlice = InSliceIndex[4].x; break;
case 5: Color = InTexture_5.SampleLevel(InSampler_5, SrcUV, 0).x; DstSlice = InSliceIndex[5].x; break;
case 6: Color = InTexture_6.SampleLevel(InSampler_6, SrcUV, 0).x; DstSlice = InSliceIndex[6].x; break;
case 7: Color = InTexture_7.SampleLevel(InSampler_7, SrcUV, 0).x; DstSlice = InSliceIndex[7].x; break;
}
// Ensure there is no NaN value
Color = -min(-Color, 0);
DstSlice = min(DstSlice, AtlasSliceCount-1);
OutAtlasTexture[uint3(DstPixelPos, DstSlice)] = Color;
}
}
}
#endif // SHADER_ADD_TEXTURE_CS
#if SHADER_ADD_TEXTURE_PS
Texture2D<float4> InTexture;
SamplerState InSampler;
int2 AtlasResolution;
void MainPS(float4 SvPosition : SV_POSITION,
out float OutColor : SV_Target0)
{
const float2 SrcUV = SvPosition.xy / float2(AtlasResolution);
float Color = InTexture.SampleLevel(InSampler, SrcUV, 0).x;
// Ensure there is no NaN value
OutColor = -min(-Color, 0);
}
#endif // SHADER_ADD_TEXTURE_PS
////////////////////////////////////////////////////////////////////////////////////////////////
// Debug
#if SHADER_DEBUG
int2 AtlasResolution;
uint AtlasSliceCount;
uint TotalSlotCount;
uint ValidSlotCount;
uint UsedSliceCount;
uint bForceUpdate;
int2 OutputResolution;
Buffer<uint> ValidSliceBuffer;
SamplerState AtlasSampler;
Texture2DArray<float4> AtlasTexture;
RWTexture2D<float4> OutputTexture;
FFontColor GetOccupancyColor(float In)
{
float3 Color = lerp(float3(0, 1, 0), float3(1, 0, 0), saturate(In));
return InitFontColor(Color);
}
bool IsInSlot(float2 Coord, float2 MinRect, float2 MaxRect)
{
return all(Coord >= MinRect) && all(Coord <= MaxRect);
}
bool IsOnBorder(float2 Coord, float2 MinRect, float2 MaxRect, float BorderSize)
{
const bool bIsWithin = IsInSlot(Coord, MinRect, MaxRect);
const bool bIsOnBorder = any(Coord - MinRect <= BorderSize) || any(MaxRect - Coord <= BorderSize);
return bIsWithin && bIsOnBorder;
}
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw stats
if (all(DispatchThreadId == 0))
{
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 100));
Print(Context, TEXT("Atlas resolution : "), FontSilver);
Print(Context, AtlasResolution.x, FontOrange, 3, 0);
Print(Context, TEXT("x"), FontSilver);
Print(Context, AtlasResolution.y, FontOrange, 3, 0);
Newline(Context);
Print(Context, TEXT("Atlas slice : "), FontSilver);
Print(Context, UsedSliceCount, FontOrange, 3, 0);
Print(Context, TEXT("/"), FontSilver);
Print(Context, AtlasSliceCount, FontOrange, 3, 0);
Newline(Context);
const float Occupancy = UsedSliceCount / float(AtlasSliceCount);
const FFontColor OccupancyColor = GetOccupancyColor(Occupancy);
Print(Context, TEXT("Atlas occupancy : "), FontSilver);
Print(Context, Occupancy * 100.f, OccupancyColor, 4, 1);
Print(Context, TEXT("% "), FontSilver);
Newline(Context);
Print(Context, TEXT("Slot count : "), FontSilver);
Print(Context, ValidSlotCount, FontOrange);
Newline(Context);
if (TotalSlotCount > AtlasSliceCount)
{
Print(Context, TEXT("Slot over budget : "), FontSilver);
Print(Context, TotalSlotCount - AtlasSliceCount, FontRed, 3, 0);
Print(Context, TEXT(" - "), FontRed);
Print(Context, TotalSlotCount, FontRed, 3, 0);
Print(Context, TEXT("/"), FontRed);
Print(Context, AtlasSliceCount, FontRed, 3, 0);
Print(Context, TEXT(" "), FontRed);
Newline(Context);
}
Print(Context, TEXT("Force Update : "), FontSilver);
if (bForceUpdate)
{
Print(Context, TEXT("True"), FontGreen);
}
else
{
Print(Context, TEXT("False"), FontRed);
}
Newline(Context);
Print(Context, TEXT("Atlas format : "), FontSilver);
Print(Context, TEXT("PF_R16"), FontOrange);
Newline(Context);
}
// Draw atlas
const uint2 GridRes = ceil(sqrt(AtlasSliceCount)).xx;
const uint2 SlotResolution = AtlasResolution / GridRes;
const uint2 Coord = DispatchThreadId.xy;
const float2 OutputUV = float2(DispatchThreadId.xy) / float2(OutputResolution);
const float AtlasRatio = AtlasResolution.y / float(AtlasResolution.x);
const float OutputRatio = OutputResolution.y / float(OutputResolution.x);
const float ScaleFactor = 0.25f;
const uint2 DisplayAtlasOffset = uint2(50, 220);
const uint2 DisplayAtlasResolution = uint2(OutputResolution.x * ScaleFactor, OutputResolution.x * ScaleFactor * AtlasResolution.y / AtlasResolution.x);
if (all(Coord > DisplayAtlasOffset) && all(Coord < DisplayAtlasOffset + DisplayAtlasResolution))
{
const float2 AtlasUV = (Coord - DisplayAtlasOffset) / float2(DisplayAtlasResolution);
const float2 AtlasCoord = AtlasUV * AtlasResolution;
if (all(AtlasUV < 1.0f))
{
const float BorderSize = float(AtlasResolution.x) / float(DisplayAtlasResolution.x) * 2.f;
const float2 IESSlotUV = frac(AtlasUV * GridRes);
const uint IESIndex = floor(AtlasUV.x * GridRes.x) + floor(AtlasUV.y * GridRes.y) * GridRes.x;
float4 Color = 0;
if (IESIndex < AtlasSliceCount)
{
const bool bIsValid = ValidSliceBuffer[IESIndex] > 0;
if (bIsValid)
{
Color = AtlasTexture.SampleLevel(AtlasSampler, float3(IESSlotUV, IESIndex), 0);
}
if (IsOnBorder(IESSlotUV * SlotResolution, float2(0,0), SlotResolution.xx, BorderSize))
{
Color = bIsValid ? float4(0, 1, 0, 1) : float4(0.5, 0.5, 0.5, 1);
}
}
OutputTexture[DispatchThreadId.xy] = float4(Color.xyz, 1.0f);
}
}
else
{
OutputTexture[DispatchThreadId.xy] = 0.f;
}
}
#endif // SHADER_DEBUG