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

3356 lines
101 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "../Common.ush"
#include "/Engine/Shared/HairStrandsDefinitions.h"
#include "../HairShadingCommon.ush"
#include "../ColorMap.ush"
#if SHADER_PRINT_INSTANCE || SHADER_PRINT || SHADER_PRINT_MEMORY || SHADER_CLUSTERAABB || SHADER_DOMINFO || SHADER_PRINT_HAIR_SKIN_CACHE
/////////////////////////////////////////////////////////////////////////////////////////
// Utils function
#include "../ShaderPrint.ush"
// Shared color
float4 GetGPUSceneBoundColor() { return ColorDarkGreen; }
float4 GetInstanceGroupScreenBoundColor() { return ColorGreen; }
float4 GetGPUAABBInstanceGroupBoundColor() { return ColorDarkOrange; }
float4 GetGPUAABBMacroGroupBoundColor() { return ColorRed; }
float4 GetGPUAABBMacroGroupBoundVoxelAlignedColor() { return ColorLightRed; }
// Print float number in 0..1000 aligned on the right
void PrintNumberAlignedRight(inout FShaderPrintContext Context, float Value, FFontColor InColor)
{
if (Value >= 100.f)
{
Print(Context, Value, InColor, 5, 1);
}
else if (Value >= 10.f)
{
Print(Context, TEXT(" "), InColor);
Print(Context, Value, InColor, 4, 1);
}
else
{
Print(Context, TEXT(" "), InColor);
Print(Context, Value, InColor, 3, 1);
}
}
// Print
void PrintLargeNumber(inout FShaderPrintContext Context, uint Value, FFontColor InColor)
{
if (Value > 1000000000)
{
const float fValue = Value / 1000000000.f;
//Print(Context, fValue, InColor, 5, 1);
PrintNumberAlignedRight(Context, fValue, InColor);
Print(Context, TEXT("B"), InColor);
}
else if (Value > 1000000)
{
const float fValue = Value / 1000000.f;
//Print(Context, fValue, InColor, 5, 1);
PrintNumberAlignedRight(Context, fValue, InColor);
Print(Context, TEXT("M"), InColor);
}
else if (Value > 1000)
{
const float fValue = Value / 1000.f;
//Print(Context, fValue, InColor, 5, 1);
PrintNumberAlignedRight(Context, fValue, InColor);
Print(Context, TEXT("k"), InColor);
}
else
{
Print(Context, TEXT(" "), InColor);
Print(Context, Value, InColor, 3);
}
}
void PrintPercentage(inout FShaderPrintContext Context, float Value, FFontColor InColor)
{
const uint iValue = ceil(clamp(Value * 100.f, 0.f, 100.f));
if (iValue < 10)
{
Print(Context, TEXT(" "), InColor);
Print(Context, iValue, InColor, 1);
}
else if (iValue < 100)
{
Print(Context, TEXT(" "), InColor);
Print(Context, iValue, InColor, 2);
}
else
{
Print(Context, iValue, InColor, 3);
}
Print(Context, TEXT("% "), InColor);
}
#endif // SHADER_PRINT_INSTANCE || SHADER_PRINT || SHADER_PRINT_MEMORY || SHADER_CLUSTERAABB || SHADER_DOMINFO || SHADER_PRINT_HAIR_SKIN_CACHE
/////////////////////////////////////////////////////////////////////////////////////////
// Visualize hair strands data as full screen view mode (BaseColor, Tangent, Roughness, ...)
#if SHADER_DEBUG_MODE
#include "../ColorMap.ush"
#include "HairStrandsVisibilityCommon.ush"
float2 OutputResolution;
uint FastResolveMask;
uint DebugMode;
int SampleIndex;
Texture2D<uint2> DepthStencilTexture;
SamplerState LinearSampler;
#define DEBUG_MODE_SAMPLE_COUNT 0
#define DEBUG_MODE_COVERAGE_TYPE 1
#define DEBUG_MODE_TAA_RESOLVE_TYPE 2
#define DEBUG_MODE_COVERAGE 3
#define DEBUG_MODE_DEPTH 4
#define DEBUG_MODE_BASECOLOR 5
#define DEBUG_MODE_ROUGHNESS 6
#define DEBUG_MODE_SPECULAR 7
#define DEBUG_MODE_TANGENT 8
#define DEBUG_MODE_TILE 9
void MainPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0)
{
const float2 UV = Input.UV;
const float2 PixelPos = UV * OutputResolution;
const float HairPixelCoverage = HairStrands.HairCoverageTexture.Load(uint3(PixelPos, 0));
const bool bIsValid = HairPixelCoverage > 0 || DebugMode == DEBUG_MODE_TILE;
if (!bIsValid)
discard;
const bool bIsDepthValid = HairStrands.HairOnlyDepthTexture.Load(uint3(PixelPos, 0)).x > 0;
if (!bIsDepthValid)
{
discard;
}
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(uint3(PixelPos, 0)));
float3 Color = 0;
float Alpha = 1;
if (DebugMode == DEBUG_MODE_SAMPLE_COUNT)
{
Color = GetHSVDebugColor(NodeDesc.Count/float(HairStrands.MaxSamplePerPixelCount));
}
if (DebugMode == DEBUG_MODE_COVERAGE_TYPE)
{
const bool bIsFullyCovered = HairPixelCoverage >= 1;
Color = bIsFullyCovered ? float3(0.25,0.85,0.25) : float3(0.85,0.25,0.25);
}
if (DebugMode == DEBUG_MODE_TAA_RESOLVE_TYPE)
{
const uint Stencil = DepthStencilTexture.Load(uint3(PixelPos,0)).y;
const bool bIsFastResolve = (Stencil & FastResolveMask) != 0;
Color = bIsFastResolve ? float3(1,1,0) : float3(0.15f, 0.25f, 0.75f);
}
if (DebugMode == DEBUG_MODE_COVERAGE)
{
Color = GetHSVDebugColor(saturate(HairPixelCoverage));
}
const bool bIsDebugModeMaterial =
DebugMode == DEBUG_MODE_DEPTH ||
DebugMode == DEBUG_MODE_BASECOLOR ||
DebugMode == DEBUG_MODE_ROUGHNESS ||
DebugMode == DEBUG_MODE_SPECULAR ||
DebugMode == DEBUG_MODE_TANGENT;
if (NodeDesc.Count > 0 && bIsDebugModeMaterial)
{
const int LocalSampleIndex = SampleIndex;
const bool bAverageAllSamples = LocalSampleIndex < 0;
const uint SelectedIndex = LocalSampleIndex >= 0 ? clamp(LocalSampleIndex, 0, int(NodeDesc.Count - 1)) : 0;
float Depth = 0;
float3 Tangent = 0;
float3 BaseColor = 0;
float Roughness = 0;
float Specular = 0;
for (uint NodeIt = 0; NodeIt < NodeDesc.Count; ++NodeIt)
{
const uint NodeOffset = NodeDesc.Offset + NodeIt;
const FPackedHairSample Data = HairStrands.HairSampleData[NodeOffset];
const FHairSample Sample = UnpackHairSample(Data);
if (bAverageAllSamples)
{
Depth += Sample.Depth;
Tangent += Sample.Tangent;
BaseColor += Sample.BaseColor;
Roughness += Sample.Roughness;
Specular += Sample.Specular;
}
else if (SelectedIndex == NodeIt)
{
Depth = Sample.Depth;
Tangent = Sample.Tangent;
BaseColor = Sample.BaseColor;
Roughness = Sample.Roughness;
Specular = Sample.Specular;
}
}
if (bAverageAllSamples)
{
Depth /= NodeDesc.Count;
Tangent /= NodeDesc.Count;
BaseColor /= NodeDesc.Count;
Roughness /= NodeDesc.Count;
Specular /= NodeDesc.Count;
}
if (DebugMode == DEBUG_MODE_DEPTH)
{
Color = Depth;
}
if (DebugMode == DEBUG_MODE_BASECOLOR)
{
Color = BaseColor;
}
if (DebugMode == DEBUG_MODE_ROUGHNESS)
{
Color = Roughness;
}
if (DebugMode == DEBUG_MODE_SPECULAR)
{
Color = Specular;
}
if (DebugMode == DEBUG_MODE_TANGENT)
{
Color = (Tangent+1) * 0.5f;
}
}
OutColor = float4(Color, Alpha);
}
#endif // SHADER_DEBUG_MODE
/////////////////////////////////////////////////////////////////////////////////////////
// Visualize hair strands control points
#if SHADER_CVS
#include "HairStrandsVertexFactoryCommon.ush"
#include "../ShaderPrint.ush"
float4x4 LocalToWorld;
uint MaxVertexCount;
uint InstanceIt;
float3 GroupColor;
SamplerState LinearSampler;
Texture2D<float> DepthTexture;
RWTexture2D<float4> ColorTexture;
[numthreads(256, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint VertexId = DispatchThreadId.x;
if (VertexId > MaxVertexCount)
return;
uint VertexIndex = VertexId;
if (HairStrandsVF.bCullingEnable)
{
VertexIndex = HairStrandsVF.CullingIndexBuffer[VertexIndex];
}
const float3 HairStrandsVF_RenPositionOffset = ReadRenPositionOffset(HairStrandsVF.PositionOffsetBuffer, HairStrandsVF.RegisteredIndex);
FShaderPrintContext CtxWidget = InitShaderPrintContext(InstanceIt == 0 && VertexId == 0, uint2(50, 50));
Print(CtxWidget, TEXT("Controls"), FontOrange); Newline(CtxWidget);
const bool bOffsetPosition = AddCheckbox(CtxWidget, TEXT("Position Offset"), false, FontWhite); Newline(CtxWidget);
const bool bControlPoints = AddCheckbox(CtxWidget, TEXT("Control Points"), true, FontWhite); Newline(CtxWidget);
const bool bGroupColor = AddCheckbox(CtxWidget, TEXT("Group Color"), true, FontWhite); Newline(CtxWidget);
const bool bLargePoint = AddCheckbox(CtxWidget, TEXT("Large Point"), false, FontWhite); Newline(CtxWidget);
// int PointSize = AddSlider(CtxWidget, TEXT("Point Size"), 1, FontWhite, 1, 3); Print(CtxWidget, PointSize, FontYellow); Newline(CtxWidget);
FShaderPrintContext Ctx = InitShaderPrintContext(VertexId == 0, uint2(50, 50));
if (Ctx.bIsActive && bOffsetPosition)
{
const float3 P_Local = HairStrandsVF_RenPositionOffset;
const float3 P_World = mul(float4(P_Local, 1), LocalToWorld).xyz;
AddCrossWS(P_World, 10.f, ColorRed);
}
if (bControlPoints)
{
const float3 P_Local = ReadHairControlPoint(
HairStrandsVF.PositionBuffer,
VertexIndex,
HairStrandsVF_RenPositionOffset,
HairStrandsVF.Radius,
HairStrandsVF.RootScale,
HairStrandsVF.TipScale).Position;
const float3 P_World = mul(float4(P_Local, 1), LocalToWorld).xyz;
float4 SamplePosition = float4(P_World + DFHackToFloat(PrimaryView.PreViewTranslation), 1);
float4 ClipPosition = mul(SamplePosition, View.TranslatedWorldToClip);
float2 ScreenPosition = ClipPosition.xy / ClipPosition.w;
float2 ScreenUV = ScreenPosition * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
float SampleSceneDepth = ClipPosition.w;
const float S = 1;
const float4 Color = bGroupColor ? float4(GroupColor, 1) : float4(S, S, 0, 1);
const float SceneDepth = ConvertFromDeviceZ(DepthTexture.SampleLevel(LinearSampler, ScreenUV, 0));
const bool bIsVisible = SampleSceneDepth <= SceneDepth;
if (bIsVisible)
{
int2 OutCoord = ScreenUV * View.BufferSizeAndInvSize.xy;
OutCoord = clamp(OutCoord, int2(0, 0), View.BufferSizeAndInvSize.xy-1);
if (bLargePoint)
{
int PointSize = 1;
for (int y = -PointSize; y <= PointSize; ++y)
for (int x = -PointSize; x <= PointSize; ++x)
{
int2 SplatCoord = OutCoord + int2(x, y);
SplatCoord = clamp(SplatCoord, int2(0, 0), View.BufferSizeAndInvSize.xy-1);
ColorTexture[SplatCoord] = Color;
}
}
else
{
ColorTexture[OutCoord] = Color;
}
}
}
}
#endif // SHADER_CVS
/////////////////////////////////////////////////////////////////////////////////////////
// Visualize (Sim/Render & Rest/Deformed):
// * hair strands root points
// * skin triangle on which strands are attached to
// * sample point used for RBF
#if SHADER_MESH_PROJECTION_HAIR
#include "HairStrandsBindingCommon.ush"
#include "../ShaderPrint.ush"
#define INPUT_ROOTS 0
#define INPUT_SAMPLES 1
uint bSimPass;
uint bRestPass;
uint DrawIndex;
uint MaxUniqueSection;
uint MaxUniqueTriangleCount;
uint MaxRootCount;
float4x4 RootLocalToWorld;
uint DeformedFrameEnable;
uint bOverride;
uint Render_HairRestTriangles;
uint Render_HairRestFrames;
uint Render_HairDeformedTriangles;
uint Render_HairDeformedFrames;
uint Sim_HairRestTriangles;
uint Sim_HairRestFrames;
uint Sim_HairRestSamples;
uint Sim_HairDeformedTriangles;
uint Sim_HairDeformedFrames;
uint Sim_HairDeformedSamples;
#if PERMUTATION_INPUT_TYPE == INPUT_ROOTS
Buffer<uint> RootBarycentricBuffer;
Buffer<float4> RestPositionBuffer;
Buffer<float4> DeformedPositionBuffer;
Buffer<uint> RootToUniqueTriangleIndexBuffer;
#endif
#if PERMUTATION_INPUT_TYPE == INPUT_SAMPLES
StructuredBuffer<float4> RestSamplePositionsBuffer;
StructuredBuffer<float4> DeformedSamplePositionsBuffer;
#endif
StructuredBuffer<float4> RestPositionOffset;
StructuredBuffer<float4> DeformedPositionOffset;
void Move(inout FShaderPrintContext Ctx, uint2 InPos)
{
if (Ctx.bIsActive)
{
float2 fPos = float2(InPos) / float2(Ctx.Config.Resolution);
Ctx.StartPos = fPos;
Ctx.Pos = fPos;
}
}
float2 ToScreenUV(float3 TWSPos)
{
const float4 NDC = mul(float4(TWSPos.xyz, 1), View.TranslatedWorldToClip);
const float2 ScreenUv = (NDC.xy / NDC.ww) * float2(0.5f, -0.5f) + 0.5f;
return ScreenUv;
}
float4 GetScreenMinMax(float3 WP0, float3 WP1, float3 WP2)
{
const float3 PreViewTranslation = DFDemote(MakeDFVector3(View.PreViewTranslationHigh, View.PreViewTranslationLow));
const float3 TWSP0 = WP0 + PreViewTranslation;
const float3 TWSP1 = WP1 + PreViewTranslation;
const float3 TWSP2 = WP2 + PreViewTranslation;
const float2 UV0 = ToScreenUV(TWSP0);
const float2 UV1 = ToScreenUV(TWSP1);
const float2 UV2 = ToScreenUV(TWSP2);
float2 MinUV;
float2 MaxUV;
MinUV.x = min3(UV0.x, UV1.x, UV2.x);
MinUV.y = min3(UV0.y, UV1.y, UV2.y);
MaxUV.x = max3(UV0.x, UV1.x, UV2.x);
MaxUV.y = max3(UV0.y, UV1.y, UV2.y);
return float4(MinUV, MaxUV);
}
bool IsInRect(float4 MinMaxUV, float2 UV)
{
return all(UV >= MinMaxUV.xy) && all(UV <= MinMaxUV.zw);
}
[numthreads(256, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
ResolvedView = ResolveView();
float NormalScale = 0.2f;
const float2 CursorPixelCoord = GetCursorPos();
const float2 CursorUV = CursorPixelCoord * View.ViewSizeAndInvSize.zw;
bool bDrawTriangle= false;
bool bDrawNormal = false;
bool bDrawRoot = false;
// Widgets for draw configuration
{
uint2 StartPos = uint2(500, 50);
uint2 OffsetPos = uint2(180, 0);
FShaderPrintContext Ctx = InitShaderPrintContext(DispatchThreadId.x == 0 && DrawIndex == 0, StartPos);
bool bValid = false;
// Guides
{
bool bRest = false;
bool bDeform = false;
bool bTriangle= false;
bool bNormal = false;
bool bRoot = false;
Print(Ctx, TEXT("Guides "), FontYellow); Newline(Ctx);
if (bOverride>0)
{
bRest = Sim_HairRestTriangles || Sim_HairRestFrames;
bDeform = Sim_HairDeformedTriangles || Sim_HairDeformedFrames;
bTriangle= Sim_HairRestTriangles || Sim_HairDeformedTriangles;
bNormal = false;
bRoot = Sim_HairRestFrames || Sim_HairDeformedFrames;
Print(Ctx, TEXT("Sim Rest "), FontWhite); PrintBool(Ctx, bRest); Newline(Ctx);
Print(Ctx, TEXT("Sim Deformed "), FontWhite); PrintBool(Ctx, bDeform); Newline(Ctx);
Newline(Ctx);
Print(Ctx, TEXT("Sim Triangles "), FontWhite); PrintBool(Ctx, bTriangle); Newline(Ctx);
Print(Ctx, TEXT("Sim Normals "), FontWhite); PrintBool(Ctx, bNormal); Newline(Ctx);
Print(Ctx, TEXT("Sim Roots "), FontWhite); PrintBool(Ctx, bRoot); Newline(Ctx);
Newline(Ctx);
}
else
{
bRest = AddCheckbox(Ctx, TEXT("Sim Rest"), false, FontWhite); Newline(Ctx);
bDeform = AddCheckbox(Ctx, TEXT("Sim Deformed"), false, FontWhite); Newline(Ctx);
Newline(Ctx);
bTriangle = AddCheckbox(Ctx, TEXT("Sim Triangles"), false, FontWhite); Newline(Ctx);
bNormal = AddCheckbox(Ctx, TEXT("Sim Normals"), false, FontWhite); Newline(Ctx);
bRoot = AddCheckbox(Ctx, TEXT("Sim Roots"), false, FontWhite); Newline(Ctx);
Newline(Ctx);
}
if (bSimPass == 1)
{
if (PERMUTATION_INPUT_TYPE == INPUT_ROOTS)
{
bValid = (bRoot || bNormal || bTriangle) && ((bRestPass==1 && bRest) || (bRestPass==0 && bDeform));
bDrawTriangle = bDrawTriangle || bTriangle;
bDrawNormal = bDrawNormal || bNormal;
bDrawRoot = bDrawRoot || bRoot;
}
}
}
// Strands/Cards
{
bool bRest = false;
bool bDeform = false;
bool bTriangle= false;
bool bNormal = false;
bool bRoot = false;
Move(Ctx, StartPos + OffsetPos);
Print(Ctx, TEXT("Strands / Cards"), FontYellow); Newline(Ctx);
if (bOverride>0)
{
bRest = Render_HairRestTriangles || Render_HairRestFrames;
bDeform = Render_HairDeformedTriangles || Render_HairDeformedFrames;
bTriangle= Render_HairRestTriangles || Render_HairDeformedTriangles;
bNormal = false;
bRoot = Render_HairRestFrames || Render_HairDeformedFrames;
Print(Ctx, TEXT("Ren Rest "), FontWhite); PrintBool(Ctx, bRest); Newline(Ctx);
Print(Ctx, TEXT("Ren Deformed "), FontWhite); PrintBool(Ctx, bDeform); Newline(Ctx);
Newline(Ctx);
Print(Ctx, TEXT("Ren Triangles "), FontWhite); PrintBool(Ctx, bTriangle); Newline(Ctx);
Print(Ctx, TEXT("Sim Normals "), FontWhite); PrintBool(Ctx, bNormal); Newline(Ctx);
Print(Ctx, TEXT("Ren Roots "), FontWhite); PrintBool(Ctx, bRoot); Newline(Ctx);
Newline(Ctx);
}
else
{
bRest = AddCheckbox(Ctx, TEXT("Ren Rest"), false, FontWhite); Newline(Ctx);
bDeform = AddCheckbox(Ctx, TEXT("Ren Deformed"), true, FontWhite) ; Newline(Ctx);
Newline(Ctx);
bTriangle = AddCheckbox(Ctx, TEXT("Ren Triangles"), false, FontWhite); Newline(Ctx);
bNormal = AddCheckbox(Ctx, TEXT("Ren Normals"), false, FontWhite); Newline(Ctx);
bRoot = AddCheckbox(Ctx, TEXT("Ren Roots"), true, FontWhite); Newline(Ctx);
Newline(Ctx);
}
if (bSimPass == 0)
{
if (PERMUTATION_INPUT_TYPE == INPUT_ROOTS)
{
bValid = (bRoot || bNormal || bTriangle) && ((bRestPass==1 && bRest) || (bRestPass==0 && bDeform));
bDrawTriangle = bDrawTriangle || bTriangle;
bDrawNormal = bDrawNormal || bNormal;
bDrawRoot = bDrawRoot || bRoot;
}
}
}
// RBF
{
bool bRest = false;
bool bDeform = false;
Move(Ctx, StartPos + OffsetPos*2);
Print(Ctx, TEXT("RBF Samples"), FontYellow); Newline(Ctx);
if (bOverride>0)
{
bRest = Sim_HairRestSamples;
bDeform = Sim_HairDeformedSamples;
Print(Ctx, TEXT("RBF Rest "), FontWhite); Print(Ctx, TEXT("|| "), FontPurple); PrintBool(Ctx, bRest) Newline(Ctx);
Print(Ctx, TEXT("RBF Deformed "), FontWhite); Print(Ctx, TEXT("|| "), FontYellow); PrintBool(Ctx, bDeform) Newline(Ctx);
Newline(Ctx);
}
else
{
bRest = AddCheckbox(Ctx, TEXT("RBF Rest "), false, FontWhite); Print(Ctx, TEXT("||"), FontPurple); Newline(Ctx);
bDeform = AddCheckbox(Ctx, TEXT("RBF Deformed"), false, FontWhite); Print(Ctx, TEXT("||"), FontYellow); Newline(Ctx);
Newline(Ctx);
}
if (bSimPass == 1 && (PERMUTATION_INPUT_TYPE == INPUT_SAMPLES))
{
bValid = (bRestPass==1 && bRest) || (bRestPass==0 && bDeform);
}
}
// Settings
NormalScale = AddSlider(Ctx, TEXT("Size"), 0.02f, FontWhite) * 10.f;
// Position Offsets
{
bool bRest = false;
bool bDeform = false;
Move(Ctx, StartPos + OffsetPos*3);
Print(Ctx, TEXT("Position Offsets"), FontYellow); Newline(Ctx);
{
bRest = AddCheckbox(Ctx, TEXT("Offset Rest "), false, FontWhite); Print(Ctx, TEXT("||"), FontCyan); Newline(Ctx);
bDeform = AddCheckbox(Ctx, TEXT("Offset Deformed"), false, FontWhite); Print(Ctx, TEXT("||"), FontMagenta); Newline(Ctx);
Newline(Ctx);
}
if (DispatchThreadId.x == 0)
{
if (bRest)
{
const float3 PositionOffset = mul(RestPositionOffset[0], RootLocalToWorld).xyz;
AddCrossWS(PositionOffset, NormalScale, ColorCyan);
}
if (bDeform)
{
const float3 PositionOffset = mul(DeformedPositionOffset[0], RootLocalToWorld).xyz;
AddCrossWS(PositionOffset, NormalScale, ColorMagenta);
}
}
}
if (!bValid)
{
return;
}
}
#if PERMUTATION_INPUT_TYPE == INPUT_ROOTS
const uint RootIndex = DispatchThreadId.x;
if (RootIndex < MaxRootCount)
{
const float3 B = UnpackBarycentrics(RootBarycentricBuffer[RootIndex]);
const uint TriangleIndex = RootToUniqueTriangleIndexBuffer[RootIndex];
FHairMeshBasis Basis;
FHairMeshTriangle Tri;
if (DeformedFrameEnable > 0)
{
Tri = GetHairMeshTriangle(TriangleIndex, DeformedPositionBuffer);
Basis = GetHairMeshBasis(TriangleIndex, DeformedPositionBuffer, B);
}
else
{
Tri = GetHairMeshTriangle(TriangleIndex, RestPositionBuffer);
Basis = GetHairMeshBasis(TriangleIndex, RestPositionBuffer, B);
}
// Triangle positions
const float3 WP0 = mul(float4(Tri.P0, 1), RootLocalToWorld).xyz;
const float3 WP1 = mul(float4(Tri.P1, 1), RootLocalToWorld).xyz;
const float3 WP2 = mul(float4(Tri.P2, 1), RootLocalToWorld).xyz;
// Triangle normals
const float3 WN0 = mul(float4(Tri.N0, 0), RootLocalToWorld).xyz;
const float3 WN1 = mul(float4(Tri.N1, 0), RootLocalToWorld).xyz;
const float3 WN2 = mul(float4(Tri.N2, 0), RootLocalToWorld).xyz;
// Basis
const float3 WO = mul(float4(Basis.BaseO, 1), RootLocalToWorld).xyz;
const float3 WT = mul(float4(Basis.BaseT, 0), RootLocalToWorld).xyz;
const float3 WB = mul(float4(Basis.BaseB, 0), RootLocalToWorld).xyz;
const float3 WN = mul(float4(Basis.BaseN, 0), RootLocalToWorld).xyz;
// Face normal
float3 WFaceN;
{
const float3 E0 = (Tri.P1 - Tri.P0).xyz;
const float3 E1 = (Tri.P2 - Tri.P0).xyz;
const float3 FaceN = normalize(cross(E1, E0));
WFaceN = mul(float4(FaceN, 0), RootLocalToWorld).xyz;
}
// Selection
const float4 MinMaxUV = GetScreenMinMax(WP0, WP1, WP2);
const bool bIsInRect = IsInRect(MinMaxUV, CursorUV);
if (bIsInRect)
{
// Selected triangles
{
AddLineWS(WP0, WP1, ColorYellow);
AddLineWS(WP1, WP2, ColorYellow);
AddLineWS(WP2, WP0, ColorYellow);
}
// Vertex normal
{
AddLineWS(WP1, WP1 + WN1 * NormalScale, ColorBlue);
AddLineWS(WP2, WP2 + WN2 * NormalScale, ColorBlue);
AddLineWS(WP0, WP0 + WN0 * NormalScale, ColorBlue);
}
// Triangle normal
if (0)
{
AddLineWS(WP0, WP0 + WFaceN * NormalScale, ColorGreen);
AddLineWS(WP1, WP1 + WFaceN * NormalScale, ColorGreen);
AddLineWS(WP2, WP2 + WFaceN * NormalScale, ColorGreen);
}
// Root basis
{
AddLineWS(WO, WO + WT * NormalScale * 0.3f, ColorRed);
AddLineWS(WO, WO + WB * NormalScale * 0.3f, ColorGreen);
AddLineWS(WO, WO + WN * NormalScale * 0.3f, ColorBlue);
}
}
else
{
if (bDrawTriangle)
{
AddLineWS(WP0, WP1, ColorWhite);
AddLineWS(WP1, WP2, ColorWhite);
AddLineWS(WP2, WP0, ColorWhite);
}
// Vertex normal
if (bDrawNormal)
{
AddLineWS(WP1, WP1 + WN1 * NormalScale, ColorBlue);
AddLineWS(WP2, WP2 + WN2 * NormalScale, ColorBlue);
AddLineWS(WP0, WP0 + WN0 * NormalScale, ColorBlue);
}
// Triangle normal
if (bDrawNormal && 0)
{
AddLineWS(WP0, WP0 + WFaceN * NormalScale, ColorGreen);
AddLineWS(WP1, WP1 + WFaceN * NormalScale, ColorGreen);
AddLineWS(WP2, WP2 + WFaceN * NormalScale, ColorGreen);
}
// Root basis
if (bDrawRoot)
{
AddLineWS(WO, WO + WN * NormalScale * 0.3f, ColorYellow);
}
}
}
#endif // INPUT_ROOTS
#if PERMUTATION_INPUT_TYPE == INPUT_SAMPLES
const uint PointIndex = DispatchThreadId.x;
if (PointIndex < MaxRootCount)
{
float3 P = (DeformedFrameEnable > 0) ? DeformedSamplePositionsBuffer[PointIndex].xyz : RestSamplePositionsBuffer[PointIndex].xyz;
P = mul(float4(P, 1), RootLocalToWorld).xyz;
const float4 RBFColor = bRestPass==1 ? ColorPurple : ColorYellow;
AddCrossWS(P, 1.f, RBFColor);
}
#endif // INPUT_SAMPLES
}
#endif // SHADER_MESH_PROJECTION_HAIR
/////////////////////////////////////////////////////////////////////////////////////////
// Visualized stats for PPLL primary visibility
#if SHADER_PPLL_DEBUG
#include "../ShaderPrint.ush"
#include "HairStrandsVisibilityCommon.ush"
float PPLLMeanListElementCountPerPixel;
float PPLLMaxTotalListElementCount;
Texture2D<uint> PPLLCounter;
Texture2D<uint> PPLLNodeIndex;
StructuredBuffer<FPackedHairVisPPLL> PPLLNodeData;
RWTexture2D<float4> SceneColorTextureUAV;
FFontColor GetOccupancyColor(float In)
{
float3 Color = lerp(float3(0, 1, 0), float3(1, 0, 0), saturate(In));
return InitFontColor(Color);
}
[numthreads(8, 8, 1)]
void VisibilityDebugPPLLCS(uint GroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
int2 PixelPos = DispatchThreadId.xy;
float PPLLListElementcount = PPLLCounter[uint2(0, 0)];
uint NextNodeIndex = PPLLNodeIndex[PixelPos];
if (NextNodeIndex != 0xFFFFFFFF)
{
uint StrandCount = 0;
do
{
StrandCount++;
FPackedHairVisPPLL Node = PPLLNodeData[NextNodeIndex];
NextNodeIndex = Node.NextNodeIndex;
} while (NextNodeIndex != 0xFFFFFFFF);
float l = saturate(StrandCount / PPLLMeanListElementCountPerPixel);
SceneColorTextureUAV[PixelPos] = float4(l, 1.0f - l, 0.0f, 0.0f);
}
const float2 BarOrigin = 100.0f;
const float2 BarSize = float2(200.0f, 20.0f);
if (all(PixelPos >= BarOrigin) && all(PixelPos <= (BarOrigin + BarSize)))
{
float2 LocalPos = PixelPos - BarOrigin;
float MemoryUsage = PPLLListElementcount / PPLLMaxTotalListElementCount;
if (MemoryUsage > LocalPos.x / BarSize.x)
{
SceneColorTextureUAV[PixelPos] = float4(MemoryUsage, 1.0f - MemoryUsage, 1.0f - MemoryUsage, 0.0f);
}
else
{
SceneColorTextureUAV[PixelPos] = float4(0.2f, 0.2f, 0.2f, 0.0f);
}
if (any(PixelPos == BarOrigin) || any(PixelPos == (BarOrigin + BarSize)))
{
SceneColorTextureUAV[PixelPos] = float4(1.0f, 1.0f, 1.0f, 0.0f);
}
}
if (DispatchThreadId.x == 0 && DispatchThreadId.y == 0)
{
const float PPLLNodeSizeInMb = (4 + 4 + 4) / 1000000.f;
const float NodePercentage = float(PPLLListElementcount) / float(PPLLMaxTotalListElementCount);
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(BarOrigin + BarSize*uint2(0, 2)));
Print(Context, TEXT("Mean Node Per Pixel : "));
Print(Context, PPLLMeanListElementCountPerPixel, FontEmerald);
Newline(Context);
Print(Context, TEXT("Nodes Used (%) : "));
Print(Context, NodePercentage * 100.f, GetOccupancyColor(NodePercentage));
Newline(Context);
Print(Context, TEXT("Nodes Used : "));
Print(Context, PPLLListElementcount, FontSilver);
Newline(Context);
Print(Context, TEXT("Allocated Nodes (Mb): "));
Print(Context, PPLLMaxTotalListElementCount * PPLLNodeSizeInMb, FontSilver);
Newline(Context);
Print(Context, TEXT("Total : "));
Print(Context, PPLLMaxTotalListElementCount, FontOrange);
Newline(Context);
}
}
#endif // SHADER_PPLL_DEBUG
//////////////////////////////////////////////////////////////////////////////////////////
// Visualized Deep Opacity Map stats and buffer
#if SHADER_VISUALIZEDOM
#include "../PostProcessCommon.ush"
float DomScale;
float2 OutputResolution;
float2 InvOutputResolution;
int4 HairViewRect;
Texture2D<float> DeepShadowDepthTexture;
Texture2D<float4> DeepShadowLayerTexture;
SamplerState LinearSampler;
bool IsInsideRect(float4 Rect, float2 PixelPos, inout float2 LocalUV)
{
LocalUV = (PixelPos - Rect.xy) / Rect.zw;
return PixelPos.x > Rect.x && PixelPos.x < Rect.x + Rect.z &&
PixelPos.y > Rect.y && PixelPos.y < Rect.y + Rect.w;
}
bool IsOnBorder(float2 LocalUV, float4 Rect)
{
float2 PixelPos = LocalUV * Rect.zw;
float BorderThickness = 1;
return
PixelPos.x < BorderThickness || PixelPos.x > Rect.z - 1 - BorderThickness ||
PixelPos.y < BorderThickness || PixelPos.y > Rect.w - 1 - BorderThickness;
}
float3 AddBorder(float2 LocalUV, float4 Rect)
{
const bool bIsOnBorder = IsOnBorder(LocalUV, Rect);
return bIsOnBorder ? float3(1,1,0) : float3(0,0,0);
}
// Display the DOM textures into some insert rect
#if PERMUTATION_OUTPUT_TYPE == 0
void VisualizeDomPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0)
{
const float TileSize = 48;
const float2 UV = Input.UV;
const float2 PixelPos = UV * OutputResolution;
float2 PixelPosAtTileCenter = PixelPos - (frac(PixelPos / TileSize) - 0.5f) * TileSize;
float2 UVAtTileCenter = PixelPosAtTileCenter * InvOutputResolution;
const float OffsetX = 450;
const float OffsetY = 50;
const float RectSize = 300;
const float4 DepthRect = float4(OffsetX, OffsetY, RectSize, RectSize);
const float4 DomRect = float4(OffsetX + RectSize, OffsetY, RectSize, RectSize);
float3 Color = 0;
float2 LocalUV = 0;
if (IsInsideRect(DepthRect, PixelPos, LocalUV))
{
const float2 AtlasUV = LocalUV;
Color = DeepShadowDepthTexture.SampleLevel(LinearSampler, AtlasUV, 0).xxx;
Color += AddBorder(LocalUV, DepthRect);
}
else if (IsInsideRect(DomRect, PixelPos, LocalUV))
{
const float2 AtlasUV = LocalUV;
Color = DeepShadowLayerTexture.SampleLevel(LinearSampler, AtlasUV, 0).xyz / DomScale;
Color += AddBorder(LocalUV, DomRect);
#if 0
float4 Weight = PostprocessInput2.SampleLevel(LinearSampler, LocalUV, 0);
if (Weight.w > 0) Color = float3(0, 0, 1);
if (Weight.z > 0) Color = float3(0, 1, 0);
if (Weight.y > 0) Color = float3(1, 1, 0);
if (Weight.x > 0) Color = float3(1, 0, 0);
Color *= 1000;
#endif
}
else
{
discard;
}
OutColor = float4(Color, 1);
}
#endif // PERMUTATION_OUTPUT_TYPE == 0
// Display screen space rect for vizualizing projected viewrect of each hair macro group
#if PERMUTATION_OUTPUT_TYPE == 1
void VisualizeDomPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0)
{
const float2 UV = Input.UV;
const float2 PixelPos = UV * OutputResolution;
float3 Color = 0;
float2 LocalUV = 0;
if (IsInsideRect(HairViewRect, PixelPos, LocalUV) && IsOnBorder(LocalUV, HairViewRect))
{
Color += float3(0, 1, 0);
}
else
{
discard;
}
OutColor = float4(Color, 1);
}
#endif // PERMUTATION_OUTPUT_TYPE == 1
#endif // SHADER_VISUALIZEDOM
//////////////////////////////////////////////////////////////////////////////////////////
// Print Deep Opacity Map stats (to be merge with prev. pass)
#if SHADER_DOMINFO
#include "../ShaderPrint.ush"
#include "../Matrices.ush"
#include "../ShadingCommon.ush"
#include "HairStrandsDeepShadowCommonStruct.ush"
#include "HairStrandsAABBCommon.ush"
uint AllocatedSlotCount;
uint MacroGroupCount;
uint bViewRectOptimizeEnabled;
uint bVoxelizationEnabled;
int2 AtlasResolution;
Buffer<int> MacroGroupAABBBuffer;
StructuredBuffer<FDeepShadowViewInfo> ShadowViewInfoBuffer;
[numthreads(1, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (DispatchThreadId.x > 0)
{
return;
}
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 40));
FFontColor FontColor = FontSilver;
Print(Context, TEXT("Hair Components "), FontYellow); Newline(Context);
Print(Context, TEXT("R : "), FontColor); PrintBool(Context, View.HairComponents & HAIR_COMPONENT_R); Newline(Context);
Print(Context, TEXT("TT : "), FontColor); PrintBool(Context, View.HairComponents & HAIR_COMPONENT_TT); Newline(Context);
Print(Context, TEXT("TRT : "), FontColor); PrintBool(Context, View.HairComponents & HAIR_COMPONENT_TRT); Newline(Context);
Print(Context, TEXT("Global Scatt. : "), FontColor); PrintBool(Context, View.HairComponents & HAIR_COMPONENT_GS); Newline(Context);
Print(Context, TEXT("Local Scatt. : "), FontColor); PrintBool(Context, View.HairComponents & HAIR_COMPONENT_LS); Newline(Context);
Newline(Context);
Print(Context, TEXT("Misc "), FontYellow); Newline(Context);
Print(Context, TEXT("Voxelization : "), FontColor); PrintBool(Context, bVoxelizationEnabled); Newline(Context);
Print(Context, TEXT("View rect optim. : "), FontColor); PrintBool(Context, bViewRectOptimizeEnabled); Newline(Context);
Newline(Context);
Print(Context, TEXT("DOM "), FontYellow); Newline(Context);
Print(Context, TEXT("Atlas resolution : "), FontColor); Print(Context, AtlasResolution.x, FontColor, 4, 0); Print(Context, TEXT(" x "), FontColor); Print(Context, AtlasResolution.y, FontColor, 4, 0); Newline(Context);
Print(Context, TEXT("Atlas slots : "), FontColor); Print(Context, AllocatedSlotCount, FontColor); Newline(Context);
Print(Context, TEXT("GPU driven : "), FontColor); PrintBool(Context, true); Newline(Context);
Newline(Context);
Print(Context, TEXT("Bounds "), FontYellow); Newline(Context);
const bool bMacroAABB = AddCheckbox(Context, TEXT("Macro AABB "), false, FontColor); Newline(Context);
const bool bShadowAABB = AddCheckbox(Context, TEXT("Shadow AABB "), false, FontColor); Newline(Context);
if (bMacroAABB)
{
for (uint MacroGroupIt = 0; MacroGroupIt < MacroGroupCount; ++MacroGroupIt)
{
const FHairAABB Bound = ReadHairAABB(MacroGroupIt, MacroGroupAABBBuffer);
AddAABBTWS(Bound.Min, Bound.Max, GetGPUAABBMacroGroupBoundVoxelAlignedColor());
}
}
if (bShadowAABB)
{
const float4 FrustumColor = float4(0.5f, 1, 0, 1);
LOOP
for (uint SlotIt = 0; SlotIt < AllocatedSlotCount; ++SlotIt)
{
const float4x4 TranslatedWorldToClip = ShadowViewInfoBuffer[SlotIt].TranslatedWorldToClip;
const float4x4 ClipToTranslatedWorld = Inverse(TranslatedWorldToClip);
float4 P01 = float4(-1, -1, 0, 1);
float4 P11 = float4(-1, 1, 0, 1);
float4 P21 = float4( 1, 1, 0, 1);
float4 P31 = float4( 1, -1, 0, 1);
float4 P00 = float4(-1, -1, 1, 1);
float4 P10 = float4(-1, 1, 1, 1);
float4 P20 = float4( 1, 1, 1, 1);
float4 P30 = float4( 1, -1, 1, 1);
P00 = mul(P00, ClipToTranslatedWorld); P00 /= P00.w;
P10 = mul(P10, ClipToTranslatedWorld); P10 /= P10.w;
P20 = mul(P20, ClipToTranslatedWorld); P20 /= P20.w;
P30 = mul(P30, ClipToTranslatedWorld); P30 /= P30.w;
P01 = mul(P01, ClipToTranslatedWorld); P01 /= P01.w;
P11 = mul(P11, ClipToTranslatedWorld); P11 /= P11.w;
P21 = mul(P21, ClipToTranslatedWorld); P21 /= P21.w;
P31 = mul(P31, ClipToTranslatedWorld); P31 /= P31.w;
AddLineTWS(P00.xyz, P10.xyz, FrustumColor, FrustumColor);
AddLineTWS(P10.xyz, P20.xyz, FrustumColor, FrustumColor);
AddLineTWS(P20.xyz, P30.xyz, FrustumColor, FrustumColor);
AddLineTWS(P30.xyz, P00.xyz, FrustumColor, FrustumColor);
AddLineTWS(P00.xyz, P01.xyz, FrustumColor, FrustumColor);
AddLineTWS(P10.xyz, P11.xyz, FrustumColor, FrustumColor);
AddLineTWS(P20.xyz, P21.xyz, FrustumColor, FrustumColor);
AddLineTWS(P30.xyz, P31.xyz, FrustumColor, FrustumColor);
AddLineTWS(P01.xyz, P11.xyz, FrustumColor, FrustumColor);
AddLineTWS(P11.xyz, P21.xyz, FrustumColor, FrustumColor);
AddLineTWS(P21.xyz, P31.xyz, FrustumColor, FrustumColor);
AddLineTWS(P31.xyz, P01.xyz, FrustumColor, FrustumColor);
}
}
}
#endif // SHADER_DOMINFO
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// Print hair strands stats & material infos
#if SHADER_PRINT
#include "../SceneData.ush"
#include "../ShaderPrint.ush"
#include "../CommonViewUniformBuffer.ush"
#include "../SceneTextureParameters.ush"
#include "../DeferredShadingCommon.ush"
#include "../ShaderPrint.ush"
#include "../PositionReconstructionCommon.ush"
#include "HairStrandsVisibilityCommon.ush"
#include "HairStrandsAABBCommon.ush"
float ResolutionScale;
int2 MaxResolution;
uint FastResolveMask;
uint HairMacroGroupCount;
uint2 GroupSize;
uint HairVisibilityNodeGroupSize;
uint AllocatedSampleCount;
uint HairInstanceCount;
Buffer<uint> HairInstanceDataBuffer;
Buffer<int> InstanceAABBBuffer;
Texture2D<float> HairCountTexture;
Texture2D<uint> HairCountUintTexture;
Texture2D<uint2> StencilTexture;
SamplerState LinearSampler;
Buffer<uint> HairVisibilityIndirectArgsBuffer;
Buffer<int> HairMacroGroupAABBBuffer;
Buffer<int> HairMacroGroupVoxelAlignedAABBBuffer;
#define MATERIAL_DATA 1
#define DETAIL_DATA 1
FFontColor GetOccupancyColor(float In)
{
float3 Color = lerp(float3(0, 1, 0), float3(1, 0, 0), saturate(In));
return InitFontColor(Color);
}
void DrawInstanceAABB(uint PrimitiveId)
{
// Since we don't have the actual instance ID (they are transient instance vs. persistent instance),
// we find the primitive with the matching PrimitiveID
uint InstanceID = ~0;
const uint TotalInstanceCount = min(8192u, uint(GetSceneData().MaxAllocatedInstanceId));
for (uint LocalInstanceID = 0; LocalInstanceID < TotalInstanceCount; ++LocalInstanceID)
{
FInstanceSceneData InstanceData = GetInstanceSceneData(LocalInstanceID);
if (InstanceData.PrimitiveId == PrimitiveId)
{
InstanceID = LocalInstanceID;
break;
}
}
if (InstanceID != ~0)
{
FInstanceSceneData InstanceData = GetInstanceSceneData(InstanceID);
AddOBBWS(InstanceData.LocalBoundsCenter - InstanceData.LocalBoundsExtent, InstanceData.LocalBoundsCenter + InstanceData.LocalBoundsExtent, GetGPUSceneBoundColor(), DFHackToFloat(InstanceData.LocalToWorld));
AddReferentialWS(DFHackToFloat(InstanceData.LocalToWorld), 50.f);
}
}
struct FData
{
uint InstanceID;
uint RegisteredIndex;
uint GeometryType;
float4 InstanceScreenSphereBound;
};
FData LoadInstanceData(uint InIndex)
{
const uint Index8 = InIndex*8;
FData Out;
Out.InstanceID = HairInstanceDataBuffer[Index8+0];
Out.RegisteredIndex = HairInstanceDataBuffer[Index8+1];
Out.GeometryType = HairInstanceDataBuffer[Index8+2];
Out.InstanceScreenSphereBound.x = asfloat(HairInstanceDataBuffer[Index8+4]);
Out.InstanceScreenSphereBound.y = asfloat(HairInstanceDataBuffer[Index8+5]);
Out.InstanceScreenSphereBound.z = asfloat(HairInstanceDataBuffer[Index8+6]);
Out.InstanceScreenSphereBound.w = asfloat(HairInstanceDataBuffer[Index8+7]);
return Out;
}
[numthreads(1, 1, 1)]
void MainCS(uint InGroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw AABB of the instance groups
if (all(DispatchThreadId == 0))
{
// Draw bound & legends
{
FShaderPrintContext CtxL = InitShaderPrintContext(true, uint2(900, 100));
bool bDrawGPUScene = false;
bool bDrawInstanceGroupScreen = false;
bool bDrawHairInstances = false;
bool bDrawMacroBounds = false;
bool bDrawVoxelAlignedMacroBounds = false;
// Bounds legends
{
bDrawGPUScene = AddCheckbox(CtxL, TEXT("Bound - GPU Scene"), true, FFontColor(GetGPUSceneBoundColor().xyz)); Newline(CtxL);
bDrawInstanceGroupScreen = AddCheckbox(CtxL, TEXT("Bound - Instance Group Screen"), true, FFontColor(GetInstanceGroupScreenBoundColor().xyz)); Newline(CtxL);
bDrawHairInstances = AddCheckbox(CtxL, TEXT("Bound - AABB Instance Group"), true, FFontColor(GetGPUAABBInstanceGroupBoundColor().xyz)); Newline(CtxL);
bDrawMacroBounds = AddCheckbox(CtxL, TEXT("Bound - AABB Macro Group"), true, FFontColor(GetGPUAABBMacroGroupBoundColor().xyz)); Newline(CtxL);
bDrawVoxelAlignedMacroBounds= AddCheckbox(CtxL, TEXT("Bound - AABB Macro Group (voxel-aligned)"), true, FFontColor(GetGPUAABBMacroGroupBoundVoxelAlignedColor().xyz)); Newline(CtxL);
}
// Macro groups bounds
if (bDrawMacroBounds)
{
for (uint GroupIndex =0;GroupIndex< HairMacroGroupCount; GroupIndex++)
{
FHairAABB Bound = ReadHairAABB(GroupIndex, HairMacroGroupAABBBuffer);
AddAABBTWS(Bound.Min, Bound.Max, GetGPUAABBMacroGroupBoundColor());
}
}
// Voxel aligned macro groups bounds
if (bDrawVoxelAlignedMacroBounds)
{
for (uint GroupIndex = 0; GroupIndex < HairMacroGroupCount; GroupIndex++)
{
FHairAABB Bound = ReadHairAABB(GroupIndex, HairMacroGroupVoxelAlignedAABBBuffer);
AddAABBTWS(Bound.Min, Bound.Max, GetGPUAABBMacroGroupBoundVoxelAlignedColor());
}
}
// Hair instance
for (uint HairInstanceIndex = 0; HairInstanceIndex < HairInstanceCount; HairInstanceIndex++)
{
const FData InstanceData = LoadInstanceData(HairInstanceIndex);
// GPU scene bounds
if (bDrawGPUScene)
{
if (InstanceData.InstanceID != ~0)
{
DrawInstanceAABB(InstanceData.InstanceID);
}
}
// Instance AABB
if (bDrawHairInstances && InstanceData.GeometryType == 0/*Strands*/)
{
const FHairAABB AABB = ReadHairAABB(InstanceData.RegisteredIndex, InstanceAABBBuffer);
AddAABBTWS(AABB.Min, AABB.Max, GetGPUAABBInstanceGroupBoundColor());
}
// Screen AABB/size
if (bDrawInstanceGroupScreen && InstanceData.GeometryType == 0/*Strands*/)
{
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 50));
AddCircleSS(Context, InstanceData.InstanceScreenSphereBound.xy, InstanceData.InstanceScreenSphereBound.w, ColorGreen);
}
}
}
const uint DispatchX = HairVisibilityIndirectArgsBuffer[0];
const uint DispatchY = HairVisibilityIndirectArgsBuffer[1];
const uint UsedSampleCount = HairStrands.HairSampleCount[0];
const uint DispatchedSampleCount = DispatchX * DispatchY * HairVisibilityNodeGroupSize;
const uint2 EquivalentResolution = GetHairSampleResolution(UsedSampleCount);
const uint2 AllocatedResolution = HairStrands.HairSampleViewportResolution;
// Pixel coord
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(450, 100));
Print(Context, TEXT("Sample Count (Used/Alloc) "), FontCyan);
PrintLargeNumber(Context, UsedSampleCount, FontCyan);
Print(Context, TEXT(" / "), FontCyan);
PrintLargeNumber(Context, AllocatedSampleCount, FontCyan);
Print(Context, TEXT(" "), FontCyan);
const float AllocationPercentage = float(UsedSampleCount) / float(AllocatedSampleCount);
PrintPercentage(Context, AllocationPercentage, GetOccupancyColor(AllocationPercentage));
Newline(Context);
Print(Context, TEXT("Sample Lighting Resolution "), FontCyan);
Print(Context, EquivalentResolution.x, FontCyan, 4);
Print(Context, TEXT("x"), FontSilver);
Print(Context, EquivalentResolution.y, FontCyan, 4);
Print(Context, TEXT(" | "), FontSilver);
Print(Context, AllocatedResolution.x, FontGrey, 4);
Print(Context, TEXT("x"), FontSilver);
Print(Context, AllocatedResolution.y, FontGrey, 4);
Newline(Context);
Print(Context, TEXT("MacroGroup Count "), FontYellow);
Print(Context, HairMacroGroupCount, FontYellow);
Newline(Context);
}
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 500));
const int2 PixelCoord = int2(ResolutionScale * GetCursorPos(Context));
if (any(PixelCoord < 0) || any(PixelCoord >= MaxResolution))
return;
const bool bIsValid = HairStrands.HairOnlyDepthTexture.Load(uint3(PixelCoord, 0)).x > 0;
if (!bIsValid)
return;
const float ViewHairCount = HairCountTexture.Load(uint3(PixelCoord, 0));
const float ViewHairCountUint = HairCountUintTexture.Load(uint3(PixelCoord, 0)) / 1000.f;
const float HairPixelCoverage = HairStrands.HairCoverageTexture.Load(uint3(PixelCoord, 0));
uint Total8BitCoverage = 0;
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(uint3(PixelCoord, 0)));
for (uint SampleIt0 = 0; SampleIt0 < NodeDesc.Count; SampleIt0++)
{
const uint LocalOffset = NodeDesc.Offset + SampleIt0;
const FPackedHairSample NodeData = HairStrands.HairSampleData[LocalOffset];
const FHairSample Sample = UnpackHairSample(NodeData);
Total8BitCoverage += Sample.Coverage8bit;
}
// Pixel coord
Print(Context, TEXT("Pixel "), FontTurquoise);
Print(Context, PixelCoord, FontTurquoise);
Newline(Context);
// Max sample
Print(Context, TEXT("Max Sample "));
Print(Context, HairStrands.MaxSamplePerPixelCount);
Newline(Context);
// Unique sample
Print(Context, TEXT("Sample "), FontOrange);
Print(Context, NodeDesc.Count, FontOrange);
Newline(Context);
// Accurate coverage (if coverage pass is activated)
Print(Context, TEXT("Hair Count "));
Print(Context, ViewHairCount);
Newline(Context);
// Accurate coverage (if coverage pass is activated) uint
Print(Context, TEXT("Hair Count Uint "));
Print(Context, ViewHairCountUint);
Newline(Context);
// Accurate coverage (if coverage pass is activated)
Print(Context, TEXT("Coverage "), FontOrange);
Print(Context, HairPixelCoverage, FontOrange);
Newline(Context);
// 8 bit Coverage (summed)
Print(Context, TEXT("Sum cov. 8bits "));
Print(Context, Total8BitCoverage);
Newline(Context);
// Resolve type
const uint Stencil = StencilTexture.Load(uint3(PixelCoord,0)).y;
const bool bIsFastResolve = (Stencil & FastResolveMask) != 0;
Print(Context, TEXT("Resolve "));
if (bIsFastResolve)
{
Print(Context, TEXT("Fast"), FontEmerald);
}
else
{
Print(Context, TEXT("Regular"), FontEmerald);
}
Newline(Context);
Newline(Context);
const float2 ColumnPos = Context.Pos;
// Sample Description
{
Print(Context, TEXT("Sample ID "), FontWhite); Newline(Context);
#if MATERIAL_DATA
Print(Context, TEXT(".Base Color R "), FontRed); Newline(Context);
Print(Context, TEXT(".Base Color G "), FontGreen); Newline(Context);
Print(Context, TEXT(".Base Color B "), FontBlue); Newline(Context);
Newline(Context);
Print(Context, TEXT(".Tangent X "), FontEmerald); Newline(Context);
Print(Context, TEXT(".Tangent Y "), FontEmerald); Newline(Context);
Print(Context, TEXT(".Tangent Z "), FontEmerald); Newline(Context);
Newline(Context);
Print(Context, TEXT(".Roughness "), FontOrange); Newline(Context);
Print(Context, TEXT(".Specular "), FontEmerald); Newline(Context);
Print(Context, TEXT(".Backlit "), FontOrange); Newline(Context);
Newline(Context);
#endif
#if DETAIL_DATA
Print(Context, TEXT(".Light Channels "), FontEmerald); Newline(Context);
Print(Context, TEXT(".Scatter Scene "), FontOrange); Newline(Context);
Print(Context, TEXT(".Depth "), FontEmerald); Newline(Context);
Newline(Context);
Print(Context, TEXT(".Macro Group ID "), FontYellow); Newline(Context);
Print(Context, TEXT(".Primitive "), FontEmerald); Newline(Context);
#endif
Print(Context, TEXT(".Coverage "), FontOrange); Newline(Context);
}
// Per sample description
for (uint SampleIt = 0; SampleIt < NodeDesc.Count; SampleIt++)
{
const uint LocalOffset = NodeDesc.Offset + SampleIt;
const FHairSample Sample = UnpackHairSample(HairStrands.HairSampleData[LocalOffset]);
FShaderPrintContext Context2 = InitShaderPrintContext(true, ColumnPos + float2(150.f + 80.f * SampleIt, 0.f) / float2(MaxResolution));
Print(Context2, SampleIt, FontWhite); Newline(Context2);
#if MATERIAL_DATA
Print(Context2, Sample.BaseColor.x, FontRed); Newline(Context2);
Print(Context2, Sample.BaseColor.y, FontGreen); Newline(Context2);
Print(Context2, Sample.BaseColor.z, FontBlue); Newline(Context2);
Newline(Context2);
Print(Context2, Sample.Tangent.x, FontEmerald); Newline(Context2);
Print(Context2, Sample.Tangent.y, FontEmerald); Newline(Context2);
Print(Context2, Sample.Tangent.z, FontEmerald); Newline(Context2);
Newline(Context2);
Print(Context2, Sample.Roughness, FontOrange); Newline(Context2);
Print(Context2, Sample.Specular, FontEmerald); Newline(Context2);
Print(Context2, Sample.Backlit, FontOrange); Newline(Context2);
Newline(Context2);
// Draw tangent vector (strands)
{
//const float2 PixelCoord = GetCursorPos();// DispatchThreadId.xy + 0.5f;
const float2 UV = PixelCoord / float2(MaxResolution);
const float SceneDepth = ConvertFromDeviceZ(Sample.Depth);
const float3 WorldPosition = ReconstructTranslatedWorldPositionFromDepth(UV, SceneDepth);
AddLineTWS(WorldPosition, WorldPosition + Sample.Tangent, ColorRed, ColorYellow);
}
#endif
#if DETAIL_DATA
Print(Context2,(Sample.LightChannelMask & 0x1) ? 1u : 0u, FontEmerald); Newline(Context2);
// Print(Context2,(Sample.LightChannelMask & 0x1) ? 1u : 0u); Newline(Context2);
// Print(Context2,(Sample.LightChannelMask & 0x2) ? 1u : 0u); Newline(Context2);
// Print(Context2,(Sample.LightChannelMask & 0x4) ? 1u : 0u); Newline(Context2);
Print(Context2, HasHairFlags(Sample.Flags, HAIR_FLAGS_SCATTER_SCENE_LIGHT) ? 1u : 0u, FontOrange); Newline(Context2);
Print(Context2, Sample.Depth, FontEmerald); Newline(Context2);
Newline(Context2);
Print(Context2, Sample.MacroGroupId, FontYellow); Newline(Context2);
Print(Context2, Sample.ControlPointId, FontEmerald); Newline(Context2);
#endif
Print(Context2, Sample.Coverage8bit, FontOrange); Newline(Context2);
}
}
#endif // SHADER_PRINT
////////////////////////////////////////////////////////////////////////////////////////////////
// Print stats/info about hair instance (strands/cards/meshes)
#if SHADER_PRINT_INSTANCE
#include "HairStrandsAABBCommon.ush"
uint InstanceRegisteredIndex;
int2 MaxResolution;
uint InstanceCount;
uint InstanceCount_StrandsPrimaryView;
uint InstanceCount_StrandsShadowView;
uint InstanceCount_CardsOrMeshesPrimaryView;
uint InstanceCount_CardsOrMeshesShadowView;
Buffer<uint> Infos;
Buffer<int> InstanceAABB;
float4 InstanceScreenSphereBound;
FSTRINGS(InstanceNames)
FSTRINGS(AttributeNames)
struct FInstanceInfo
{
uint GroupIndex;
uint GroupCount;
uint LODCount;
uint GeometryType;
uint BindingType;
bool bHasSim;
bool bHasRBF;
bool bHasRTGeom;
float LOD;
float ClipValue;
// Strands data
uint StrandsCount;
uint VertexCount;
float ScreenSize;
uint ActivePointCount;
uint ActiveCurveCount;
uint Attributes;
float Radius;
float Density;
float TipScale;
float RootScale;
float Length;
float LengthScale;
float CoverageScale;
bool Voxel;
bool CacheRen;
bool CacheSim;
bool bHasLODSwitched;
bool bAutoLOD;
bool bCurveRemapping;
bool bPointRemapping;
uint Flags;
// Binding
uint UniqueTriangle;
uint UniqueSection;
uint RootCount;
uint ControlPointCount;
uint bIsVisibleInPrimaryView;
uint bIsVisibleInShadowView;
};
FInstanceInfo ReadInstanceInfo(uint Index)
{
const uint Index20 = Index * 20u;
uint4 Data0 = 0;
Data0.x = Infos[Index20 + 0];
Data0.y = Infos[Index20 + 1];
Data0.z = Infos[Index20 + 2];
Data0.w = Infos[Index20 + 3];
uint4 Data1 = 0;
Data1.x = Infos[Index20 + 4];
Data1.y = Infos[Index20 + 5];
Data1.z = Infos[Index20 + 6];
Data1.w = Infos[Index20 + 7];
uint4 Data2 = 0;
Data2.x = Infos[Index20 + 8];
Data2.y = Infos[Index20 + 9];
Data2.z = Infos[Index20 + 10];
Data2.w = Infos[Index20 + 11];
uint4 Data3 = 0;
Data3.x = Infos[Index20 + 12];
Data3.y = Infos[Index20 + 13];
Data3.z = Infos[Index20 + 14];
Data3.w = Infos[Index20 + 15];
uint4 Data4 = 0;
Data4.x = Infos[Index20 + 16];
Data4.y = Infos[Index20 + 17];
Data4.z = Infos[Index20 + 18];
Data4.w = Infos[Index20 + 19];
FInstanceInfo Out = (FInstanceInfo)0;
Out.GroupIndex = (Data0.x >> 0) & 0xFF;
Out.GroupCount = (Data0.x >> 8) & 0xFF;
Out.LODCount = (Data0.x >> 16) & 0xFF;
Out.GeometryType = (Data0.x >> 24) & 0x7;
Out.BindingType = (Data0.x >> 27) & 0x7;
Out.bHasSim = (Data0.x >> 30) & 0x1;
Out.bHasRBF = (Data0.x >> 31) & 0x1;
Out.LOD = f16tof32((Data0.y >> 0) & 0xFFFF);
Out.ClipValue = f16tof32((Data0.y >> 16) & 0xFFFF);
Out.StrandsCount = Data0.z;
Out.VertexCount = Data0.w;
// Strands binding data
Out.UniqueSection = Data1.x;
Out.UniqueTriangle = Data1.y;
Out.RootCount = Data1.z;
Out.ControlPointCount = Data1.w;
Out.bIsVisibleInPrimaryView = Data2.x & 0x1;
Out.bIsVisibleInShadowView = Data2.x & 0x2;
Out.Voxel = Data2.x & 0x4;
Out.CacheSim = Data2.x & 0x8;
Out.CacheRen = Data2.x & 0x10;
Out.Flags =(Data2.x>>8) & 0xFF;
Out.ScreenSize = f16tof32((Data2.x>>16) & 0xFFFF);
Out.ActivePointCount = Data2.y;
Out.ActiveCurveCount = Data2.z;
Out.Attributes = Data2.w;
Out.bAutoLOD = (Data3.w >> 16u) & 0x1;
Out.bCurveRemapping = (Data3.w >> 17u) & 0x1;
Out.bPointRemapping = (Data3.w >> 18u) & 0x1;
Out.Radius = f16tof32(Data3.x & 0xFFFF);
Out.Density = f16tof32(Data3.x >> 16u);
Out.RootScale = f16tof32(Data3.y & 0xFFFF);
Out.TipScale = f16tof32(Data3.y >> 16u);
Out.Length = f16tof32(Data3.z & 0xFFFF);
Out.LengthScale = f16tof32(Data3.z >> 16u);
Out.CoverageScale = f16tof32(Data3.w & 0xFFFF);
Out.bHasLODSwitched = Data4.x & 0x1;
Out.bHasRTGeom = Data4.x & 0x2;
return Out;
}
bool HasAttribute(FInstanceInfo In, uint InAttributeIt)
{
return (In.Attributes & (1u<< InAttributeIt)) != 0;
}
[numthreads(1, 1, 1)]
void MainCS(uint GroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw AABB of the instance groups
if (any(DispatchThreadId != 0))
{
return;
}
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 100));
bool bShowAdvancedStrandsData = false;
// Draw totals
const uint TotalGroup = InstanceCount;
uint TotalStrandsCount = 0;
uint TotalVertexCount = 0;
{
for (uint InstanceIndex = 0; InstanceIndex < InstanceCount; ++InstanceIndex)
{
const FInstanceInfo Info = ReadInstanceInfo(InstanceIndex);
TotalStrandsCount += Info.StrandsCount;
TotalVertexCount += Info.VertexCount;
}
}
{
Print(Context, TEXT("Total Groups : "), FontWhite);
Print(Context,TotalGroup, FontYellow, 8);
Newline(Context);
Print(Context, TEXT("Total Strand Curves : "), FontWhite);
PrintLargeNumber(Context,TotalStrandsCount, FontOrange);
Newline(Context);
Print(Context, TEXT("Total Strand Points : "), FontWhite);
PrintLargeNumber(Context,TotalVertexCount, FontSilver);
Newline(Context);
}
Newline(Context);
{
Print(Context, TEXT("Total Strand : "), FontWhite);
Print(Context, TEXT(" Visible:"), FontSilver);
Print(Context,InstanceCount_StrandsPrimaryView, FontYellow, 4);
Print(Context, TEXT("| Shadow:"), FontSilver);
Print(Context,InstanceCount_StrandsShadowView, FontYellow, 4);
Newline(Context);
Print(Context, TEXT("Total Cards/Meshes : "), FontWhite);
Print(Context, TEXT(" Visible:"), FontSilver);
Print(Context,InstanceCount_CardsOrMeshesPrimaryView, FontYellow, 4);
Print(Context, TEXT("| Shadow:"), FontSilver);
Print(Context, InstanceCount_CardsOrMeshesShadowView, FontYellow, 4);
Newline(Context);
bShowAdvancedStrandsData = AddCheckbox(Context, TEXT("Advanced"), false, FontSilver);
Newline(Context);
}
Newline(Context);
// Draw instance details
Print(Context, TEXT(" Group LOD Geometry Binding Cache Sim RBF Clip Screen CovSc. Strands Points Bind.Tri/Sect Vis. Shad. SwLOD RT Name"));
Newline(Context);
LOOP
for (uint InstanceIndex = 0; InstanceIndex < InstanceCount; ++InstanceIndex)
{
const FInstanceInfo Info = ReadInstanceInfo(InstanceIndex);
// Index
Print(Context,InstanceIndex, FontSilver, 2);
Print(Context, TEXT(" "));
// Group
Print(Context,Info.GroupIndex, FontYellow, 1);
Print(Context, TEXT("/"));
Print(Context,Info.GroupCount, FontYellow, 1);
Print(Context, TEXT(" "));
// LOD
if (Info.bAutoLOD)
{
Print(Context, TEXT("Auto "), FontYellow);
}
else
{
Print(Context,Info.LOD, FontYellow, 4, 2);
Print(Context, TEXT("/"));
Print(Context,Info.LODCount-1, FontYellow, 1);
Print(Context, TEXT(" "));
}
// Geometry
if (Info.GeometryType == 0) { Print(Context, TEXT("Strands "), FontEmerald); }
else if (Info.GeometryType == 1){ Print(Context, TEXT("Cards "), FontEmerald); }
else if (Info.GeometryType == 2){ Print(Context, TEXT("Meshes "), FontEmerald); }
Print(Context, TEXT(" "));
// Binding
if (Info.BindingType == 0) { Print(Context, TEXT("None "), FontTurquoise); }
else if (Info.BindingType == 1) { Print(Context, TEXT("Rigid "), FontTurquoise); }
else if (Info.BindingType == 2) { Print(Context, TEXT("Skinned "), FontTurquoise); }
// Cache
if (Info.CacheRen) { Print(Context, TEXT("Ren "), FontOrange); }
else if (Info.CacheSim) { Print(Context, TEXT("Sim "), FontOrange); }
else { Print(Context, TEXT("None "), FontSilver); }
Print(Context, TEXT(" "));
// Sim
PrintBool(Context, Info.bHasSim);
// RBF
PrintBool(Context, Info.bHasRBF);
// Clip
if (Info.ClipValue >= 0)
Print(Context,Info.ClipValue, FontGreen, 5,3);
else
Print(Context,0u, FontSilver, 5, 3);
// Strands effective
Print(Context, Info.ScreenSize, FontOrange, 4, 2);
Print(Context, TEXT(" "));
Print(Context, Info.CoverageScale, FontOrange, 4, 2);
Print(Context, TEXT(" "));
// Curve sCount
const float CurveRatio = Info.ActiveCurveCount / float(Info.StrandsCount);
PrintPercentage(Context, CurveRatio, InitFontColor(ColorMapTurbo(CurveRatio)));
PrintLargeNumber(Context, Info.ActiveCurveCount, FontOrange);
Print(Context, TEXT("/"));
PrintLargeNumber(Context, Info.StrandsCount, FontSilver);
Print(Context, TEXT(" "), FontWhite);
// Point sCount
const float VertexRatio = Info.ActivePointCount / float(Info.VertexCount);
PrintPercentage(Context, VertexRatio, InitFontColor(ColorMapTurbo(VertexRatio)));
PrintLargeNumber(Context, Info.ActivePointCount, FontOrange);
Print(Context, TEXT("/"));
PrintLargeNumber(Context, Info.VertexCount, FontSilver);
Print(Context, TEXT(" "), FontWhite);
// Binding data
Print(Context,Info.UniqueTriangle, FontOrange, 8);
Print(Context, TEXT(" "));
Print(Context,Info.UniqueSection, FontSilver, 3);
Print(Context, TEXT(" "));
// Primary view
PrintBool(Context, Info.bIsVisibleInPrimaryView);
Print(Context, TEXT(" "));
// Shadow view
PrintBool(Context, Info.bIsVisibleInShadowView);
Print(Context, TEXT(" "));
// Has LOD switched
PrintBool(Context, Info.bHasLODSwitched);
Print(Context, TEXT(" "));
// Has RT geom
PrintBool(Context, Info.bHasRTGeom);
Print(Context, TEXT(" "));
// Name
PrintInstanceNames(Context, InstanceIndex, FontWhite);
Newline(Context);
}
Newline(Context);
// Optional advanced debugging
if (bShowAdvancedStrandsData)
{
// Attribute
Print(Context, TEXT(" Group "), FontWhite);
Print(Context, TEXT("Voxel "), FontWhite);
Print(Context, TEXT("RT "), FontWhite);
Print(Context, TEXT("Scatter "), FontWhite);
Print(Context, TEXT("Radius "), FontWhite);
Print(Context, TEXT("Root "), FontWhite);
Print(Context, TEXT("Tip "), FontWhite);
Print(Context, TEXT("CvId PtId "), FontWhite);
const uint AttributeNameInfoCount = NumAttributeNames();
LOOP
for (uint AttributeIt=0; AttributeIt < AttributeNameInfoCount; ++AttributeIt)
{
PrintAttributeNames(Context, AttributeIt, FontWhite);
Print(Context, TEXT(" "));
}
Newline(Context);
LOOP
for (uint StrandInstanceIndex = 0; StrandInstanceIndex < InstanceCount; ++StrandInstanceIndex)
{
const FInstanceInfo Info = ReadInstanceInfo(StrandInstanceIndex);
if (Info.GeometryType > 0)
{
continue;
}
// Index
Print(Context, StrandInstanceIndex, FontSilver, 2);
Print(Context, TEXT(" "));
// Group
Print(Context, Info.GroupIndex, FontYellow, 1);
Print(Context, TEXT("/"));
Print(Context, Info.GroupCount, FontYellow, 1);
Print(Context, TEXT(" "));
PrintBool(Context, Info.Voxel); Print(Context, TEXT(" "));
PrintBool(Context, HasHairFlags(Info.Flags, HAIR_FLAGS_RAYTRACING_GEOMETRY));
PrintBool(Context, HasHairFlags(Info.Flags, HAIR_FLAGS_SCATTER_SCENE_LIGHT)); Print(Context, TEXT(" "));
Print(Context, Info.Radius, FontYellow, 5, 3); Print(Context, TEXT(" "));
Print(Context, Info.RootScale, FontYellow, 5, 3); Print(Context, TEXT(" "));
Print(Context, Info.TipScale, FontYellow, 5, 3); Print(Context, TEXT(" "));
PrintBool(Context, Info.bCurveRemapping); Print(Context, TEXT(" "));
PrintBool(Context, Info.bPointRemapping); Print(Context, TEXT(" "));
for (uint AttributeIt = 0; AttributeIt < AttributeNameInfoCount; ++AttributeIt)
{
PrintBool(Context, HasAttribute(Info, AttributeIt));
const uint AttributeLen = LenAttributeNames(AttributeIt);
if (AttributeLen > 3)
{
const uint SpaceCount = AttributeLen - 3u;
for (uint SpaceIt = 0; SpaceIt < SpaceCount; ++SpaceIt)
{
Print(Context, TEXT(" "));
}
}
}
PrintInstanceNames(Context, StrandInstanceIndex, FontWhite);
Newline(Context);
}
}
}
#endif // SHADER_PRINT_INSTANCE
////////////////////////////////////////////////////////////////////////////////////////////////
// Print tangent direction for strands/cards/meshes
#if SHADER_TANGENT
#include "../CommonViewUniformBuffer.ush"
#include "../SceneTextureParameters.ush"
#include "../DeferredShadingCommon.ush"
#include "../ShaderPrint.ush"
#include "../PositionReconstructionCommon.ush"
#include "HairStrandsVisibilityCommon.ush"
#include "HairStrandsAABBCommon.ush"
uint TileSize;
int2 TileCount;
float2 OutputResolution;
RWTexture2D<float4> OutputTexture;
#define HAIR_TANGENT_INVALID 0
#define HAIR_TANGENT_STRANDS 1
#define HAIR_TANGENT_CARDS 2
struct FTangentDesc
{
float3 Tangent;
uint Type;
};
FTangentDesc AddTangentPlot(uint2 PixelCoord, float2 UV)
{
FTangentDesc Out = (FTangentDesc)0;
// Strands
const float HairDeviceZ = HairStrands.HairOnlyDepthTexture.Load(uint3(PixelCoord,0)).x;
if (HairDeviceZ > 0)
{
// Find closest
float3 Closest_Tangent = 0;
float Closest_Depth = 0;
uint Total8BitCoverage = 0;
const FNodeDesc NodeDesc = DecodeNodeDesc(HairStrands.HairSampleOffset.Load(uint3(PixelCoord, 0)));
for (uint SampleIt0 = 0; SampleIt0 < NodeDesc.Count; SampleIt0++)
{
const uint LocalOffset = NodeDesc.Offset + SampleIt0;
const FPackedHairSample NodeData = HairStrands.HairSampleData[LocalOffset];
const FHairSample Sample = UnpackHairSample(NodeData);
if (Sample.Depth > Closest_Depth)
{
Closest_Depth = Sample.Depth;
Closest_Tangent = Sample.Tangent;
}
}
// Draw tangent vector (strands)
if (NodeDesc.Count > 0)
{
const float SceneDepth = ConvertFromDeviceZ(Closest_Depth);
const float3 WorldPosition = ReconstructTranslatedWorldPositionFromDepth(UV, SceneDepth);
AddLineTWS(WorldPosition, WorldPosition + Closest_Tangent, ColorRed, ColorYellow);
Out.Type = 1;
Out.Tangent = Closest_Tangent;
}
}
// Cards
if (Out.Type == 0)
{
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(UV);
const float SceneDepth = CalcSceneDepth(UV);
const float3 WorldPosition = ReconstructTranslatedWorldPositionFromDepth(UV, SceneDepth);
if (ScreenSpaceData.GBuffer.ShadingModelID == SHADINGMODELID_HAIR)
{
const float3 Tangent = ScreenSpaceData.GBuffer.WorldNormal;
AddLineTWS(WorldPosition, WorldPosition + Tangent, ColorPurple, ColorYellow);
Out.Type = 2;
Out.Tangent = Tangent;
}
}
return Out;
}
void AddTangentText(float2 UV, float3 Tangent, uint TangentType)
{
FShaderPrintContext Context = InitShaderPrintContext(true, UV);
if (TangentType == HAIR_TANGENT_STRANDS)
Print(Context, TEXT("Strands"), FontYellow);
else if (TangentType == HAIR_TANGENT_CARDS)
Print(Context, TEXT("Cards "), FontYellow);
else if (TangentType == HAIR_TANGENT_CARDS)
Print(Context, TEXT("Invalid"), FontRed);
Newline(Context);
Print(Context, Tangent.x, FontRed); Newline(Context);
Print(Context, Tangent.y, FontGreen); Newline(Context);
Print(Context, Tangent.z, FontBlue); Newline(Context);
}
[numthreads(8, 8, 1)]
void MainCS(int2 DispatchThreadId : SV_DispatchThreadID)
{
// Cursor plot
if (all(DispatchThreadId == 0))
{
const float2 PixelCoord = GetCursorPos();// DispatchThreadId.xy + 0.5f;
const float2 UV = PixelCoord / float2(OutputResolution);
const float2 UVText = (PixelCoord + float2(20, 0)) / float2(OutputResolution);
FTangentDesc Desc = AddTangentPlot(PixelCoord, UV);
if (Desc.Type == HAIR_TANGENT_STRANDS)
{
AddTangentText(UVText, Desc.Tangent, HAIR_TANGENT_STRANDS);
}
else if (Desc.Type == HAIR_TANGENT_CARDS)
{
AddTangentText(UVText, Desc.Tangent, HAIR_TANGENT_CARDS);
}
}
// Grid plot
if (all(DispatchThreadId.xy < TileCount))
{
const float2 PixelCoord = DispatchThreadId.xy * TileSize;
if (all(PixelCoord < OutputResolution))
{
const float2 UV = PixelCoord / float2(OutputResolution);
AddTangentPlot(PixelCoord, UV);
}
}
}
#endif // SHADER_TANGENT
////////////////////////////////////////////////////////////////////////////////////////////////
// Print LOD info/stats
#if SHADER_LOD_INFO
#include "../ShaderPrint.ush"
int2 MaxResolution;
uint GroupIndex;
uint GeometryType;
uint CurveCount;
uint PointCount;
float CoverageScale;
float LOD;
float ScreenSize;
float3 GroupColor;
[numthreads(1, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Legend
if (GroupIndex == 0)
{
FShaderPrintContext Ctx = InitShaderPrintContext(true, uint2(10, 50));
Print(Ctx, TEXT(" Group Geom. LOD Screen Size #Curve #Point Scale"), FontSilver); Newline(Ctx);
}
// Values
{
FShaderPrintContext Ctx = InitShaderPrintContext(true, uint2(10, 65 + GroupIndex * 15));
// Group Color
Print(Ctx, TEXT("* "), InitFontColor(GroupColor));
// Group index
Print(Ctx, GroupIndex, FontEmerald, 3, 0);
// Geometry Type
Print(Ctx, TEXT(" "));
if (GeometryType == 0)
{
Print(Ctx, TEXT("Strands "), FontOrange);
}
else if (GeometryType == 1)
{
Print(Ctx, TEXT("Cards "), FontOrange);
}
else if (GeometryType == 2)
{
Print(Ctx, TEXT("Meshes "), FontOrange);
}
Print(Ctx, LOD, FontWhite, 4, 2); Print(Ctx, TEXT(" "));
Print(Ctx, saturate(ScreenSize), FontWhite);
Print(Ctx, CurveCount, FontWhite);
Print(Ctx, PointCount, FontWhite);
Print(Ctx, CoverageScale, FontWhite);
Newline(Ctx);
}
}
#endif // SHADER_LOD_INFO
/////////////////////////////////////////////////////////////////////////////////////////
// Visualize hair cards atlas data
#if SHADER_CARDS_ATLAS
#include "../ShaderPrint.ush"
#define DEBUG_MODE_DEPTH 1
#define DEBUG_MODE_COVERAGE 2
#define DEBUG_MODE_TANGENT 3
#define DEBUG_MODE_ROOTUV 4
#define DEBUG_MODE_U 5
#define DEBUG_MODE_SEED 6
int2 OutputResolution;
int2 AtlasResolution;
int DebugMode;
Texture2D<float4> AtlasTexture;
RWTexture2D<float4> OutputTexture;
SamplerState LinearSampler;
[numthreads(8, 8, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const int2 PixelCoord = DispatchThreadId.xy;
if (any(PixelCoord >= OutputResolution))
return;
const int2 Offset = 50;
const int2 Resolution = 512 * float2(AtlasResolution.x, AtlasResolution.y)/max(AtlasResolution.x, AtlasResolution.y);
const float2 LocalUV = float2(PixelCoord - Offset) / float2(Resolution);
#if 1
if (all(LocalUV >= 0) && all(LocalUV <= 1))
{
float4 InData = AtlasTexture.SampleLevel(LinearSampler, float2(LocalUV.x,1-LocalUV.y), 0);
float3 Data = 0;
if (DebugMode == DEBUG_MODE_DEPTH) { Data = InData.xxx; }
if (DebugMode == DEBUG_MODE_COVERAGE) { Data = InData.xxx; }
if (DebugMode == DEBUG_MODE_TANGENT) { Data = InData.xyz; }
if (DebugMode == DEBUG_MODE_ROOTUV) { Data = float3(InData.xy,0); }
if (DebugMode == DEBUG_MODE_U) { Data = InData.zzz; }
if (DebugMode == DEBUG_MODE_SEED) { Data = InData.www; }
OutputTexture[PixelCoord] = float4(Data, 1);
}
#else
if (all(PixelCoord <= AtlasResolution))
{
float3 Data = AtlasTexture.Load(uint3(PixelCoord, 0)).xyz;
if (DebugMode == DEBUG_MODE_DEPTH) { Data = Data.xxx; }
if (DebugMode == DEBUG_MODE_COVERAGE) { Data = Data.xxx; }
if (DebugMode == DEBUG_MODE_TANGENT) {}
OutputTexture[PixelCoord] = float4(Data, 1);
}
#endif
#if PRINT_ATLAS_RESOLUTION
// Draw AABB of the instance groups
if (DispatchThreadId.x == 0 && DispatchThreadId.y == 0)
{
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 40));
Print(Context, TEXT("Atlas Resolution: "))
Print(Context, AtlasResolution);
Newline(Context);
}
#endif
}
#endif // SHADER_CARDS_ATLAS
/////////////////////////////////////////////////////////////////////////////////////////
// Visualize cards hair guides
#if SHADER_CARDS_GUIDE
#include "HairStrandsVertexFactoryCommon.ush"
#include "../ShaderPrint.ush"
uint InstanceRegisteredIndex;
uint CardLODIndex;
uint RenVertexCount;
float3 RenRestOffset;
uint SimVertexCount;
float3 SimRestOffset;
float4x4 LocalToWorld;
ByteAddressBuffer RenRestPosition;
ByteAddressBuffer RenDeformedPosition;
StructuredBuffer<float4> RenDeformedOffset;
ByteAddressBuffer SimRestPosition;
ByteAddressBuffer SimDeformedPosition;
StructuredBuffer<float4> SimDeformedOffset;
int DebugMode;
#define DEBUGMODE_REN_REST 1
#define DEBUGMODE_REN_DEFORMED 2
#define DEBUGMODE_SIM_REST 3
#define DEBUGMODE_SIM_DEFORMED 4
[numthreads(32, 1, 1)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const uint VertexId = DispatchThreadId.x;
const uint VertexCount = DebugMode == DEBUGMODE_REN_REST || DebugMode == DEBUGMODE_REN_DEFORMED ? RenVertexCount : SimVertexCount;
if (VertexId+1 >= VertexCount)
return;
FHairControlPoint P0 = (FHairControlPoint)0;
FHairControlPoint P1 = (FHairControlPoint)0;
float4 ColorRoot = ColorWhite;
float4 ColorTip = ColorWhite;
if (DebugMode == DEBUGMODE_REN_REST)
{
P0 = ReadHairControlPoint(RenRestPosition, VertexId, RenRestOffset, 1, 1, 1);
P1 = ReadHairControlPoint(RenRestPosition, VertexId + 1, RenRestOffset, 1, 1, 1);
ColorRoot= ColorYellow;
ColorTip = ColorPurple;
}
else if (DebugMode == DEBUGMODE_REN_DEFORMED)
{
const float3 Offset = ReadCardPositionOffset(RenDeformedOffset, InstanceRegisteredIndex, CardLODIndex);
P0 = ReadHairControlPoint(RenDeformedPosition, VertexId, Offset, 1, 1, 1);
P1 = ReadHairControlPoint(RenDeformedPosition, VertexId + 1, Offset, 1, 1, 1);
ColorRoot= ColorYellow;
ColorTip = ColorPurple;
}
else if (DebugMode == DEBUGMODE_SIM_REST)
{
P0 = ReadHairControlPoint(SimRestPosition, VertexId, SimRestOffset, 1, 1, 1);
P1 = ReadHairControlPoint(SimRestPosition, VertexId + 1, SimRestOffset, 1, 1, 1);
ColorRoot= ColorCyan;
ColorTip = ColorRed;
}
else if (DebugMode == DEBUGMODE_SIM_DEFORMED)
{
const float3 Offset = ReadSimPositionOffset(SimDeformedOffset, InstanceRegisteredIndex);
P0 = ReadHairControlPoint(SimDeformedPosition, VertexId, Offset, 1, 1, 1);
P1 = ReadHairControlPoint(SimDeformedPosition, VertexId + 1, Offset, 1, 1, 1);
ColorRoot= ColorCyan;
ColorTip = ColorRed;
}
if (P1.Type != HAIR_CONTROLPOINT_START)
{
const float4 Color0 = lerp(ColorRoot, ColorTip, P0.UCoord);
const float4 Color1 = lerp(ColorRoot, ColorTip, P1.UCoord);
AddLineWS(mul(float4(P0.Position, 1), LocalToWorld).xyz, mul(float4(P1.Position,1), LocalToWorld).xyz, Color0, Color1);
}
}
#endif // SHADER_CARDS_GUIDE
///////////////////////////////////////////////////////////////////////////////////////////////////
// Visualize directional occlusion from voxelized hair strands (used for cards generation)
#if SHADER_CARDS_DIRECTION_OCCLUSION
int3 Voxel_Resolution;
float Voxel_VoxelSize;
float3 Voxel_MinBound;
float3 Voxel_MaxBound;
Buffer<uint> Voxel_DensityBuffer;
RWBuffer<uint> OutBuffer;
[numthreads(4, 4, 4)]
void MainCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
const int3 VoxelCoord = DispatchThreadId.xyz;
if (any(VoxelCoord >= Voxel_Resolution))
return;
const uint VoxelLinearCoord = CoordToIndex(VoxelCoord, Voxel_Resolution, 0);
// Dilation
#if 0
uint OutValue = 0;
int KernelSize = 1;
uint ValidCount = 0;
for (int Z = -KernelSize; Z <= KernelSize; ++Z)
for (int Y = -KernelSize; Y <= KernelSize; ++Y)
for (int X = -KernelSize; X <= KernelSize; ++X)
{
int3 C = VoxelCoord + int3(X, Y, Z);
if (all(C >= 0) && all(C < Voxel_Resolution))
{
const uint LinearC = CoordToIndex(C, Voxel_Resolution, 0);
OutValue += Voxel_DensityBuffer[LinearC];
ValidCount++;
}
}
OutValue /= max(1, ValidCount);
#endif
// Occlusion
#if 1
const uint CenterDensity = Voxel_DensityBuffer[VoxelLinearCoord];
if (CenterDensity == 0)
return;
int KernelSize = 1;
uint ValidCount = 0;
float3 OutDirection = 0;
for (int Z = -KernelSize; Z <= KernelSize; ++Z)
for (int Y = -KernelSize; Y <= KernelSize; ++Y)
for (int X = -KernelSize; X <= KernelSize; ++X)
{
const int3 Offset = int3(X, Y, Z);
const int3 C = VoxelCoord + Offset;
if (all(C >= 0) && all(C < Voxel_Resolution) && !all(Offset == int3(0,0,0)))
{
const uint LinearC = CoordToIndex(C, Voxel_Resolution, 0);
const uint Density = Voxel_DensityBuffer[LinearC];
if (Density == 0)
{
OutDirection += normalize(float3(Offset));
ValidCount++;
}
}
}
if (ValidCount>0)
{
OutDirection = normalize(OutDirection);
}
const uint OutValue = PackDirection(OutDirection);
#endif
// Pass through
#if 0
const uint OutValue = Voxel_DensityBuffer[VoxelLinearCoord];
#endif
OutBuffer[VoxelLinearCoord] = OutValue;
}
#endif //SHADER_CARDS_DIRECTION_OCCLUSION
///////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_PLOTBSDF
#include "../SceneTexturesCommon.ush"
#include "../ShadingModels.ush"
#include "../DeferredShadingCommon.ush"
#include "../HairBsdf.ush"
#include "HairStrandsCommon.ush"
#include "HairStrandsVisibilityCommon.ush"
#include "HairStrandsEnvironmentLightingCommon.ush"
#include "HairStrandsDeepShadowCommon.ush"
#include "HairStrandsDeepTransmittanceCommon.ush"
#include "HairStrandsDeepTransmittanceDualScattering.ush"
int2 InputCoord;
int2 OutputOffset;
int2 OutputResolution;
int2 MaxResolution;
uint HairComponents;
float Roughness;
float BaseColor;
float Exposure;
RWTexture2D<float4> OutputTexture;
#define COMPARE_ROUGHNESS 0
#if COMPARE_ROUGHNESS
float DefaultLit(float3 N, float3 V, float3 L, float InRoughnesss)
{
FGBufferData GBuffer;
GBuffer.Roughness = InRoughnesss;
GBuffer.SpecularColor = 1;
GBuffer.DiffuseColor = 0;
FAreaLight AreaLight;
AreaLight.IsRectAndDiffuseMicroReflWeight = 0;
AreaLight.FalloffColor = 1;
AreaLight.SphereSinAlpha = 0;
AreaLight.SphereSinAlphaSoft = 0;
AreaLight.LineCosSubtended = 1;
FShadowTerms Shadow;
if (dot(V,N) < 0)
V.z = -V.z;
const float NoL = saturate(dot(N, L));
const float3 Fs = DefaultLitBxDF(GBuffer, N, V, L, 1, NoL, AreaLight, Shadow).Specular;
return Fs.x;
}
#endif
void MainPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0)
{
const int2 PixelCoord = Input.Position.xy;
if (PixelCoord.x < OutputOffset.x || PixelCoord.x >= OutputOffset.x+OutputResolution.x ||
PixelCoord.y < OutputOffset.y || PixelCoord.y >= OutputOffset.x+OutputResolution.y )
discard;
const float2 UV = ((PixelCoord-OutputOffset) + float2(0.5f,0.5f)) / float2(OutputResolution);
float2 InputUV = saturate(float2(InputCoord - OutputOffset) / float2(OutputResolution));
// InputUV = float2(0.5, 0.5);
float V_Phi = (UV.x*2-1) * PI; //[-Pi..Pi]
float V_Theta = (UV.y*2-1) * PI * 0.5f;
float L_Phi = (InputUV.x*2-1) * PI; //[-Pi..Pi]
float L_Theta = (InputUV.y*2-1) * PI * 0.5f;
const float3 Axis_U = float3(0,0,1); // Tangent
const float3 Axis_V = float3(1,0,0); // Perp. to the view direction
const float3 Axis_W = float3(0,1,0); // Ortho.
const float3 L_Local = float3(sin(L_Theta), cos(L_Theta)*cos(L_Phi), cos(L_Theta)*sin(L_Phi));
const float3 V_Local = float3(sin(V_Theta), cos(V_Theta)*cos(V_Phi), cos(V_Theta)*sin(V_Phi));
const float3 T = float3(1,0,0);
const float3 V_World =
V_Local.x * Axis_U +
V_Local.y * Axis_V +
V_Local.z * Axis_W;
FHairTransmittanceData TransmittanceData = (FHairTransmittanceData)0;
TransmittanceData.LocalScattering = 0;
TransmittanceData.GlobalScattering = 1;
TransmittanceData.ScatteringComponent = HairComponents;
TransmittanceData.OpaqueVisibility = 1;
const float Backlit = 1;
const float Specular = 0.5f;
FGBufferData HairGBuffer = (FGBufferData)0;
HairGBuffer.WorldNormal = T;
HairGBuffer.BaseColor = BaseColor.xxx;
HairGBuffer.Roughness = Roughness;
HairGBuffer.ShadingModelID = SHADINGMODELID_HAIR;
HairGBuffer.DiffuseColor = 0;
HairGBuffer.SpecularColor = 0;
HairGBuffer.Specular = Specular;
HairGBuffer.Metallic = 0;
HairGBuffer.Depth = 0.5f;
HairGBuffer.GBufferAO = 1;
HairGBuffer.CustomData = float4(0,0,Backlit,0);
HairGBuffer.IndirectIrradiance = 1000000;
OutColor = float4(0,0,0,1);
#if COMPARE_ROUGHNESS
const float3 Fs_Lit_R = DefaultLit(float3(0, 0,-1), V_Local, L_Local, HairToGGXRoughness_R (HairGBuffer.Roughness));
const float3 Fs_Lit_TT = DefaultLit(float3(0, 0,-1), V_Local, L_Local, HairToGGXRoughness_TT (HairGBuffer.Roughness));
const float3 Fs_Lit_TRT = DefaultLit(float3(0, 0,-1), V_Local, L_Local, HairToGGXRoughness_TRT(HairGBuffer.Roughness));
#endif
const float3 Fs_Hair = HairShading(HairGBuffer, L_Local, V_Local, T, 1, TransmittanceData, 1, 0, uint2(0,0) );
#if COMPARE_ROUGHNESS
OutColor.xyz += Fs_Lit_R * float3(1, 0, 0);
OutColor.xyz += Fs_Lit_TRT * float3(0, 1, 1);
OutColor.xyz += Fs_Lit_TT * float3(0, 0, 1);
#endif
OutColor.xyz += Fs_Hair;
OutColor.xyz *= Exposure;
//const float3 ColorV = (V_World+1)* 0.5f;
//OutColor = float4(ColorV, 1);
//OutColor = float4(UV, 0, 1);
}
#endif // SHADER_PLOTBSDF
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if SHADER_PLOTSAMPLE
#include "HairStrandsDebugCommonStruct.ush"
#include "../SceneTexturesCommon.ush"
#include "../ShadingModels.ush"
#include "../DeferredShadingCommon.ush"
#include "../HairBsdf.ush"
#include "HairStrandsCommon.ush"
#include "HairStrandsVisibilityCommon.ush"
#include "HairStrandsEnvironmentLightingCommon.ush"
#include "HairStrandsDeepShadowCommon.ush"
#include "HairStrandsDeepTransmittanceCommon.ush"
#include "HairStrandsDeepTransmittanceDualScattering.ush"
StructuredBuffer<FDebugShadingInfo> Debug_ShadingPointBuffer;
Buffer<uint> Debug_ShadingPointCounter;
StructuredBuffer<FDebugSample> Debug_SampleBuffer;
Buffer<uint> Debug_SampleCounter;
uint Debug_MaxShadingPointCount;
uint Debug_MaxSampleCount;
Texture3D<float4> HairScatteringLUTTexture;
SamplerState HairLUTSampler;
int2 OutputOffset;
int2 OutputResolution;
int2 MaxResolution;
uint HairComponents;
float Exposure;
struct FPlotRect
{
int2 Offset;
int2 Resolution;
};
bool IsInside(const int2 Coord, const FPlotRect Rect)
{
return all(Coord >= Rect.Offset) && all(Coord < Rect.Offset + Rect.Resolution);
}
float2 ToUV(const int2 Coord, const FPlotRect Rect)
{
return ((Coord - Rect.Offset) + float2(0.5f, 0.5f)) / float2(OutputResolution);
}
RWTexture2D<float4> OutputTexture;
float2 CartesianToUnwrap(float3 D)
{
const float Theta = asin(D.x);
const float Phi = atan2(D.z, D.y);
return float2(Theta, Phi);
}
float2 CartesianToLatlong(float3 D)
{
const float Theta = asin(D.x);
const float Phi = atan2(D.z, D.y);
return float2(((Phi/PI)+1)*0.5f, Theta/PI);
}
void MainPS(
in FScreenVertexOutput Input,
out float4 OutColor : SV_Target0)
{
OutColor = float4(0, 0, 0, 1);
// Prune all pixel which are not within the band horizontal band
const int2 PixelCoord = Input.Position.xy;
if (PixelCoord.y < OutputOffset.y || PixelCoord.y >= OutputOffset.x + OutputResolution.y)
{
discard;
}
const float CosAngleThreshold = 0.999f;
const float PointSize = 3.f / OutputResolution.x;
// Local
// Plot sample points
bool bIsValid = false;
const uint ShadingPointCount = Debug_ShadingPointCounter[0];
for (uint PointIt = 0; PointIt < ShadingPointCount; PointIt++)
{
FPlotRect Rect;
Rect.Offset = OutputOffset + PointIt * int2(OutputResolution.x + 10, 0);
Rect.Resolution = OutputResolution;
if (!IsInside(PixelCoord, Rect))
continue;
bIsValid = true;
const float2 UV = ToUV(PixelCoord, Rect);
const float Pixel_Phi = (UV.x * 2 - 1) * PI + PI*0.5; //[3PI/2, 0, 2PI, 3PI/2 ], so that the hair fiber is aligned on the center
const float Pixel_Theta= (UV.y * 2 - 1) * PI * 0.5f;
const float3 PixelDir_Local = float3(sin(Pixel_Theta), cos(Pixel_Theta)*cos(Pixel_Phi), cos(Pixel_Theta)*sin(Pixel_Phi));
const FDebugShadingInfo P = Debug_ShadingPointBuffer[PointIt];
// Shading frame T, V
const float3 Local_X = P.T;
const float3 Local_Y = normalize(cross(Local_X, P.V));
const float3 Local_Z = normalize(cross(Local_Y, Local_X));
const float3x3 WorldToLocal = transpose(float3x3(Local_X, Local_Y, Local_Z));
const float3 V_Local = mul(P.V, WorldToLocal);
const float3 T_Local = mul(P.T, WorldToLocal); // Should be float3(1,0,0)
{
const float SampleBacklit = 1;
const float SampleSpecular = 0.5f;
FHairTransmittanceData TransmittanceData = InitHairStrandsTransmittanceData();
uint InHairComponents = HairComponents;
const bool bPlotDualScatteringContribution = true;
if (!bPlotDualScatteringContribution)
{
TransmittanceData.LocalScattering = 1;
TransmittanceData.GlobalScattering = 1;
TransmittanceData.ScatteringComponent = InHairComponents;
TransmittanceData.OpaqueVisibility = 1;
}
else
{
const float3 L = PixelDir_Local;
const float3 T = T_Local;
const float3 V = V_Local;
const float SinLightAngle = dot(L, T);
// Compute the transmittance based on precompute Hair transmittance LUT
FHairAverageScattering AverageScattering = SampleHairLUT(View.HairScatteringLUTTexture, HairScatteringLUTSampler, P.BaseColor, P.Roughness, SinLightAngle);
TransmittanceData = ComputeDualScatteringTerms(
InitHairTransmittanceMask(),
AverageScattering,
P.Roughness,
SampleBacklit,
V,
L,
T,
InHairComponents);
}
FGBufferData HairGBuffer = (FGBufferData)0;
HairGBuffer.WorldNormal = P.T;
HairGBuffer.BaseColor = P.BaseColor;
HairGBuffer.Roughness = P.Roughness;
HairGBuffer.ShadingModelID = SHADINGMODELID_HAIR;
HairGBuffer.DiffuseColor = 0;
HairGBuffer.SpecularColor = 0;
HairGBuffer.Specular = SampleSpecular;
HairGBuffer.Metallic = 0;
HairGBuffer.Depth = 0.5f;
HairGBuffer.GBufferAO = 1;
HairGBuffer.CustomData = float4(0,0,SampleBacklit,0);
HairGBuffer.IndirectIrradiance = 1000000;
const float Area = 0;
const float BackLit = 1;
OutColor.xyz = HairShading(HairGBuffer, PixelDir_Local, V_Local, T_Local, 1, TransmittanceData, BackLit, Area, uint2(0, 0)) * Exposure;
}
for (uint SampleIt = 0; SampleIt < P.SampleCount; SampleIt++)
{
const FDebugSample S_World = Debug_SampleBuffer[P.SampleOffset + SampleIt];
const float3 S_Local = mul(S_World.Direction, WorldToLocal);
if (dot(PixelDir_Local, S_Local) > CosAngleThreshold)
{
OutColor.xyz = float3(1, 0, 0);
}
}
}
if (!bIsValid)
{
discard;
}
}
#endif // SHADER_PLOTSAMPLE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Display groom culling from shadow perspective
#if SHADER_SHADOW_CULLING
#include "../ShaderPrint.ush"
#include "../Matrices.ush"
void AddFrustumWS(float4x4 In, float4 InColor, bool bInv)
{
const float Near = 0.0001f;
const float Far = 0.9999f;
float4x4 T = In;
if (bInv)
{
T = Inverse(In);
}
float4 P00 = mul(float4(-1, -1, Near, 1), T); P00 /= P00.w;
float4 P10 = mul(float4( 1, -1, Near, 1), T); P10 /= P10.w;
float4 P20 = mul(float4( 1, 1, Near, 1), T); P20 /= P20.w;
float4 P30 = mul(float4(-1, 1, Near, 1), T); P30 /= P30.w;
float4 P01 = mul(float4(-1, -1, Far, 1), T); P01 /= P01.w;
float4 P11 = mul(float4( 1, -1, Far, 1), T); P11 /= P11.w;
float4 P21 = mul(float4( 1, 1, Far, 1), T); P21 /= P21.w;
float4 P31 = mul(float4(-1, 1, Far, 1), T); P31 /= P31.w;
AddLineWS(P00.xyz, P10.xyz, InColor);
AddLineWS(P10.xyz, P20.xyz, InColor);
AddLineWS(P20.xyz, P30.xyz, InColor);
AddLineWS(P30.xyz, P00.xyz, InColor);
AddLineWS(P01.xyz, P11.xyz, InColor);
AddLineWS(P11.xyz, P21.xyz, InColor);
AddLineWS(P21.xyz, P31.xyz, InColor);
AddLineWS(P31.xyz, P01.xyz, InColor);
AddLineWS(P00.xyz, P01.xyz, InColor);
AddLineWS(P10.xyz, P11.xyz, InColor);
AddLineWS(P20.xyz, P21.xyz, InColor);
AddLineWS(P30.xyz, P31.xyz, InColor);
}
Buffer<float> InstanceBoundInLightSpace;
Buffer<float> InstanceBoundInWorldSpace;
Buffer<uint> InstanceIntersection;
uint InstanceCount;
float3 LightCenter;
float3 LightExtent;
float4x4 LightToWorld;
float4x4 ViewWorldToProj;
float4x4 ViewProjToWorld;
[numthreads(1, 1, 1)]
void MainCS(uint GroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw AABB of the instance groups
if (any(DispatchThreadId != 0))
{
return;
}
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 50));
// View Frustum
//AddFrustumWS(ViewProjToWorld, ColorBlue, false);
AddFrustumWS(ViewWorldToProj, ColorBlue, true);
// Light bound
AddAABBWS(LightCenter - LightExtent, LightCenter + LightExtent, ColorPurple);
for (uint InstanceIndex = 0; InstanceIndex < InstanceCount; ++InstanceIndex)
{
const uint Index6 = InstanceIndex * 6;
// In light space
{
const bool bIntersect = InstanceIntersection[InstanceIndex] > 0;
const float3 MinP = float3(InstanceBoundInLightSpace[Index6 + 0], InstanceBoundInLightSpace[Index6 + 1], InstanceBoundInLightSpace[Index6 + 2]);
const float3 MaxP = float3(InstanceBoundInLightSpace[Index6 + 3], InstanceBoundInLightSpace[Index6 + 4], InstanceBoundInLightSpace[Index6 + 5]);
// Transform back from light space to world space
AddOBBWS(MinP, MaxP, bIntersect ? ColorGreen : ColorYellow, LightToWorld);
}
// In world space
{
const float3 MinP = float3(InstanceBoundInWorldSpace[Index6 + 0], InstanceBoundInWorldSpace[Index6 + 1], InstanceBoundInWorldSpace[Index6 + 2]);
const float3 MaxP = float3(InstanceBoundInWorldSpace[Index6 + 3], InstanceBoundInWorldSpace[Index6 + 4], InstanceBoundInWorldSpace[Index6 + 5]);
AddAABBWS(MinP, MaxP, ColorRed);
}
}
}
#endif // SHADER_SHADOW_CULLING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Display hair picking attribute
#if SHADER_ATTRIBUTE_DEBUG
#include "../Common.ush"
#include "../ShaderPrint.ush"
#include "HairStrandsVisibilityCommon.ush"
#include "HairStrandsVertexFactoryCommon.ush"
HAIR_STRANDS_INSTANCE_PARAMETERS(HairInstance)
#include "HairStrandsAttributeCommon.ush"
#define HAIR_STRANDS_ATTRIBUTE_ACCESSORS(Name) HairInstance_##Name
#include "/Engine/Private/HairStrands/HairStrandsAttributeTemplate.ush"
uint MaterialId;
Texture2D<uint> NodeIndexTexture;
StructuredBuffer<FPackedHairVis> NodeVisBuffer;
[numthreads(1, 1, 1)]
void CSMain(
uint DispatchThreadID : SV_DispatchThreadID,
uint GroupThreadID : SV_GroupThreadID,
uint GroupID : SV_GroupID)
{
const int2 PixelCoord = ShaderPrintData.CursorCoord;
if (all(PixelCoord < 0))
{
return;
}
const FNodeDesc NodeDesc = DecodeNodeDesc(NodeIndexTexture.Load(uint3(PixelCoord, 0)));
if (NodeDesc.Count == 0)
{
return;
}
if (NodeDesc.Count > 0)
{
// Only show the first sample properties for simplicity
const uint SampleIt = 0;
const uint SampleIndex = NodeDesc.Offset + SampleIt;
const FHairVis VisNode = UnpackHairVis(NodeVisBuffer[SampleIndex]);
if (VisNode.MaterialId == MaterialId)
{
FHairControlPoint Out = ReadHairControlPoint(
HairInstance_PositionBuffer,
VisNode.ControlPointId,
HairInstance_GetHairInstancePositionOffset(),
HairInstance_Radius,
HairInstance_RootScale,
HairInstance_TipScale);
FFontColor FontValue = FontYellow;
FFontColor FontName = FontWhite;
FShaderPrintContext Ctx = InitShaderPrintContext(true, uint2(PixelCoord) + uint2(20, 0));
Print(Ctx, TEXT("Group : "), FontName); Print(Ctx, GetHairStrandsGroupIndex(), FontValue); Newline(Ctx);
Print(Ctx, TEXT("Radius : "), FontName); Print(Ctx, GetHairStrandsDimensions(VisNode.ControlPointId, 0).y, FontValue); Newline(Ctx);
Print(Ctx, TEXT("Seed : "), FontName); Print(Ctx, GetHairStrandsSeed(VisNode.ControlPointId), FontValue); Newline(Ctx);
Print(Ctx, TEXT("CoordU : "), FontName); Print(Ctx, GetHairStrandsUV(VisNode.ControlPointId, float2(0, 0.5f)).x, FontValue); Newline(Ctx);
Newline(Ctx);
if (HasHairStrandsRootUV()) { Print(Ctx, TEXT("RootUV : "), FontName); Print(Ctx, GetHairStrandsRootUV(VisNode.ControlPointId), FontValue); Newline(Ctx); }
if (HasHairStrandsClumpId()) { Print(Ctx, TEXT("Clump ID : "), FontName); Print(Ctx, GetHairStrandsClumpID(VisNode.ControlPointId).x, FontValue); Newline(Ctx); }
if (HasHairStrandsClumpId3()) { Print(Ctx, TEXT("Clump IDs: "), FontName); Print(Ctx, GetHairStrandsClumpID(VisNode.ControlPointId), FontValue); Newline(Ctx); }
if (HasHairStrandsColor()) { Print(Ctx, TEXT("Color : "), FontName); Print(Ctx, GetHairStrandsColor(VisNode.ControlPointId, 0), FontValue); Newline(Ctx); }
if (HasHairStrandsRoughness()) { Print(Ctx, TEXT("Roughness: "), FontName); Print(Ctx, GetHairStrandsRoughness(VisNode.ControlPointId, 0), FontValue); Newline(Ctx); }
if (HasHairStrandsAO()) { Print(Ctx, TEXT("AO : "), FontName); Print(Ctx, GetHairStrandsAO(VisNode.ControlPointId, 0), FontValue); Newline(Ctx); }
Newline(Ctx);
}
}
}
#endif // SHADER_ATTRIBUTE_DEBUG
////////////////////////////////////////////////////////////////////////////////////////////////
// Print memory
#if SHADER_PRINT_MEMORY
FSTRINGS(GroomNames)
FSTRINGS(BindingNames)
FSTRINGS(ComponentNames)
uint ComponentCount;
uint GroomCount;
uint BindingCount;
Buffer<uint> ComponentBuffer;
Buffer<uint> GroomBuffer;
Buffer<uint> BindingBuffer;
struct FInfo
{
uint4 Data0;
uint4 Data1;
uint4 Data2;
uint4 Data3;
uint4 Data4;
};
FInfo ReadInfo(uint Index, Buffer<uint> InBuffer)
{
const uint Index20 = Index * 20u;
FInfo Out;
Out.Data0.x = InBuffer[Index20 + 0];
Out.Data0.y = InBuffer[Index20 + 1];
Out.Data0.z = InBuffer[Index20 + 2];
Out.Data0.w = InBuffer[Index20 + 3];
Out.Data1.x = InBuffer[Index20 + 4];
Out.Data1.y = InBuffer[Index20 + 5];
Out.Data1.z = InBuffer[Index20 + 6];
Out.Data1.w = InBuffer[Index20 + 7];
Out.Data2.x = InBuffer[Index20 + 8];
Out.Data2.y = InBuffer[Index20 + 9];
Out.Data2.z = InBuffer[Index20 + 10];
Out.Data2.w = InBuffer[Index20 + 11];
Out.Data3.x = InBuffer[Index20 + 12];
Out.Data3.y = InBuffer[Index20 + 13];
Out.Data3.z = InBuffer[Index20 + 14];
Out.Data3.w = InBuffer[Index20 + 15];
Out.Data4.x = InBuffer[Index20 + 16];
Out.Data4.y = InBuffer[Index20 + 17];
Out.Data4.z = InBuffer[Index20 + 18];
Out.Data4.w = InBuffer[Index20 + 19];
return Out;
}
struct FComponent
{
uint GroupIndex;
uint GroupCount;
uint RestCurve;
uint ActiveCurve;
uint GPU_Guides;
uint GPU_Strands;
uint GPU_Cards;
uint GPU_Meshes;
};
FComponent GetComponent(uint Index)
{
const FInfo In = ReadInfo(Index, ComponentBuffer);
FComponent Out;
Out.GroupIndex =In.Data0.x;
Out.GroupCount =In.Data0.y;
Out.ActiveCurve =In.Data0.z;
Out.RestCurve =In.Data0.w;
Out.GPU_Guides =In.Data1.x;
Out.GPU_Strands =In.Data1.y;
Out.GPU_Cards =In.Data1.z;
Out.GPU_Meshes =In.Data1.w;
return Out;
}
struct FGroom
{
uint GroupIndex;
uint GroupCount;
uint RestCurve;
uint ActiveCurve;
uint CPU_Guides;
uint CPU_Strands;
uint CPU_Cards;
uint CPU_Meshes;
uint GPU_Guides;
uint GPU_Strands;
uint GPU_Cards;
uint GPU_Meshes;
uint GPU_Rest;
uint GPU_Interpolation;
uint GPU_Cluster;
uint GPU_Raytracing;
uint Curve_Rest;
uint Curve_Interpolation;
uint Curve_Cluster;
uint Curve_Raytracing;
};
FGroom GetGroom(uint Index)
{
const FInfo In = ReadInfo(Index, GroomBuffer);
FGroom Out;
Out.GroupIndex = In.Data0.x;
Out.GroupCount = In.Data0.y;
Out.ActiveCurve = In.Data0.z;
Out.RestCurve = In.Data0.w;
Out.CPU_Guides = In.Data1.x;
Out.CPU_Strands = In.Data1.y;
Out.CPU_Cards = In.Data1.z;
Out.CPU_Meshes = In.Data1.w;
Out.GPU_Guides = In.Data2.x;
Out.GPU_Strands = In.Data2.y;
Out.GPU_Cards = In.Data2.z;
Out.GPU_Meshes = In.Data2.w;
Out.GPU_Rest = In.Data3.x;
Out.GPU_Interpolation = In.Data3.y;
Out.GPU_Cluster = In.Data3.z;
Out.GPU_Raytracing = In.Data3.w;
Out.Curve_Rest = In.Data4.x;
Out.Curve_Interpolation = In.Data4.x;
Out.Curve_Cluster = In.Data4.x;
Out.Curve_Raytracing = In.Data4.x;
return Out;
}
struct FBinding
{
uint GroupIndex;
uint GroupCount;
uint RestCurve;
uint ActiveCurve;
uint CPU_Guides;
uint CPU_Strands;
uint CPU_Cards;
uint CPU_Meshes;
uint GPU_Guides;
uint GPU_Strands;
uint GPU_Cards;
uint GPU_Meshes;
};
FBinding GetBinding(uint Index)
{
const FInfo In = ReadInfo(Index, BindingBuffer);
FBinding Out;
Out.GroupIndex = In.Data0.x;
Out.GroupCount = In.Data0.y;
Out.ActiveCurve = In.Data0.z;
Out.RestCurve = In.Data0.w;
Out.CPU_Guides = In.Data1.x;
Out.CPU_Strands = In.Data1.y;
Out.CPU_Cards = In.Data1.z;
Out.CPU_Meshes = In.Data1.w;
Out.GPU_Guides = In.Data2.x;
Out.GPU_Strands = In.Data2.y;
Out.GPU_Cards = In.Data2.z;
Out.GPU_Meshes = In.Data2.w;
return Out;
}
void PrintGroup(inout FShaderPrintContext Ctx, uint GroupIndex, uint GroupCount)
{
Print(Ctx, GroupIndex, FontEmerald, 2, 0);
Print(Ctx, TEXT("/"), FontWhite);
Print(Ctx, GroupCount, FontEmerald, 2, 0);
}
FFontColor GetOccupancyColor(float In)
{
float3 Color = lerp(float3(0, 1, 0), float3(1, 0, 0), saturate(In));
return InitFontColor(Color);
}
void PrintCurve(inout FShaderPrintContext Ctx, uint ActiveCurve, uint RestCurve)
{
const float Curve = float(ActiveCurve) / float(RestCurve);
PrintPercentage(Ctx, Curve, GetOccupancyColor(Curve));
//PrintLargeNumber(Ctx, Info.ActiveCurve, FontOrange); Print(Ctx, TEXT(" "), FontWhite);
//Print(Ctx, TEXT("/"), FontWhite);
//PrintLargeNumber(Ctx, Info.RestCurve, FontOrange); Print(Ctx, TEXT(" "), FontWhite);
}
[numthreads(1, 1, 1)]
void MainCS(uint GroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw AABB of the instance groups
if (any(DispatchThreadId != 0))
{
return;
}
// Widgets
FShaderPrintContext Ctx2 = InitShaderPrintContext(true, uint2(900, 100));
Print(Ctx2, TEXT("Controls"), FontYellow); Newline(Ctx2);
const bool bComponent = AddCheckbox(Ctx2, TEXT("Components"), false, FontWhite); Newline(Ctx2);
const bool bGroom = AddCheckbox(Ctx2, TEXT("Grooms"), true, FontWhite); Newline(Ctx2);
const bool bBinding = AddCheckbox(Ctx2, TEXT("Bindings"), false, FontWhite); Newline(Ctx2);
const bool bStrandsOnly = AddCheckbox(Ctx2, TEXT("Strands Only"), true, FontWhite); Newline(Ctx2);
// Component
FShaderPrintContext Ctx = InitShaderPrintContext(true, uint2(50, 100));
uint Component_TotalGPU = 0;
if (bComponent)
{
Print(Ctx, TEXT("Components"), FontYellow); Newline(Ctx);
{
Print(Ctx, TEXT("Grp. Common Guides Strands Cards Meshes Total Curv Name"), FontSilver); Newline(Ctx);
LOOP
for (uint It = 0; It < ComponentCount; ++It)
{
const FComponent Info = GetComponent(It);
const uint Total
= Info.GPU_Guides
+ Info.GPU_Strands
+ Info.GPU_Cards;
PrintGroup(Ctx, Info.GroupIndex, Info.GroupCount);
PrintLargeNumber(Ctx, Info.GPU_Guides, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Strands, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Cards, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Meshes, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Total, FontOrange); Print(Ctx, TEXT(" "), FontWhite);
PrintCurve(Ctx, Info.ActiveCurve, Info.RestCurve);
PrintComponentNames(Ctx, It, FontWhite);
Newline(Ctx);
}
}
Newline(Ctx);
}
// Grooms
if (bGroom)
{
Print(Ctx, TEXT("Grooms"), FontYellow); Newline(Ctx);
if (!bStrandsOnly)
{
Print(Ctx, TEXT("Grp. Guides Strands Cards Meshes Total Curv Name"), FontSilver); Newline(Ctx);
LOOP
for (uint It = 0; It < GroomCount; ++It)
{
const FGroom Info = GetGroom(It);
PrintGroup(Ctx, Info.GroupIndex, Info.GroupCount);
PrintLargeNumber(Ctx, Info.GPU_Guides, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Strands, FontWhite);Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Cards, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Meshes, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintCurve(Ctx, Info.ActiveCurve, Info.RestCurve);
PrintGroomNames(Ctx, It, FontWhite);
Newline(Ctx);
}
}
else
{
FFontColor FontMemory = FontWhite;
FFontColor FontCurve = FontSilver;
Print(Ctx, TEXT("Grp. Rest. Intrp. Cluster RT. Total Curv Name"), FontSilver); Newline(Ctx);
LOOP
for (uint It = 0; It < GroomCount; ++It)
{
const FGroom Info = GetGroom(It);
const uint GPUStrandsTotal
= Info.GPU_Rest
+ Info.GPU_Interpolation
+ Info.GPU_Cluster
+ Info.GPU_Raytracing;
PrintGroup(Ctx, Info.GroupIndex, Info.GroupCount);
PrintLargeNumber(Ctx, Info.GPU_Rest, FontMemory); Print(Ctx, TEXT(" "), FontMemory);
PrintLargeNumber(Ctx, Info.GPU_Interpolation, FontMemory);Print(Ctx, TEXT(" "), FontMemory);
PrintLargeNumber(Ctx, Info.GPU_Cluster, FontMemory); Print(Ctx, TEXT(" "), FontMemory);
PrintLargeNumber(Ctx, Info.GPU_Raytracing, FontMemory); Print(Ctx, TEXT(" "), FontMemory);
PrintLargeNumber(Ctx, GPUStrandsTotal, FontOrange); Print(Ctx, TEXT(" "), FontMemory);
PrintCurve(Ctx, Info.ActiveCurve, Info.RestCurve);
PrintGroomNames(Ctx, It, FontMemory);
Newline(Ctx);
// Curve count detail per resource type
#if 0
Print(Ctx, TEXT(" "), FontCurve);
PrintLargeNumber(Ctx, Info.Curve_Rest, FontCurve); Print(Ctx, TEXT(" "), FontCurve);
PrintLargeNumber(Ctx, Info.Curve_Interpolation, FontCurve);Print(Ctx, TEXT(" "), FontCurve);
PrintLargeNumber(Ctx, Info.Curve_Cluster, FontCurve); Print(Ctx, TEXT(" "), FontCurve);
PrintLargeNumber(Ctx, Info.Curve_Raytracing, FontCurve); Print(Ctx, TEXT(" "), FontCurve);
Newline(Ctx);
#endif
}
}
Newline(Ctx);
}
// Bindings
if (bBinding)
{
Print(Ctx, TEXT("Bindings"), FontYellow); Newline(Ctx);
Print(Ctx, TEXT("Grp. Guides Strands Cards Meshes Total Curv Name"), FontSilver); Newline(Ctx);
LOOP
for (uint It = 0; It < BindingCount; ++It)
{
const FBinding Info = GetBinding(It);
const uint Total
= Info.GPU_Guides
+ Info.GPU_Strands
+ Info.GPU_Cards
+ Info.GPU_Meshes;
PrintGroup(Ctx, Info.GroupIndex, Info.GroupCount);
PrintLargeNumber(Ctx, Info.GPU_Guides, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Strands, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Cards, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Info.GPU_Meshes, FontWhite); Print(Ctx, TEXT(" "), FontWhite);
PrintLargeNumber(Ctx, Total, FontOrange); Print(Ctx, TEXT(" "), FontWhite);
PrintCurve(Ctx, Info.ActiveCurve, Info.RestCurve);
PrintBindingNames(Ctx, It, FontWhite);
Newline(Ctx);
}
Newline(Ctx);
}
}
#endif // SHADER_PRINT_MEMORY
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Debug draw clusters
#ifdef SHADER_CLUSTERAABB
#include "HairStrandsAABBCommon.ush"
uint InstanceRegisteredIndex;
uint ClusterOffset;
uint ClusterCount;
uint HairGroupId;
uint PointCount;
uint CurveCount;
uint bDrawAABB;
Buffer<int> GroupAABBBuffer;
Buffer<int> ClusterAABBBuffer;
[numthreads(GROUP_SIZE, 1, 1)]
void MainDrawDebugAABBCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (DispatchThreadId.x == 0)
{
const bool bDrawClusterInfo = true;
const uint HairGroupHeight = 15;
FShaderPrintContext Context = InitShaderPrintContext(true, uint2(50, 50 + (HairGroupId == 0 ? 0 : HairGroupHeight) + HairGroupId * HairGroupHeight));
FFontColor FontValue = FontWhite;
FFontColor FontPercent = FontSilver;
if (DispatchThreadId.x == 0)
{
if (HairGroupId == 0)
{
Print(Context, TEXT("Reg.Idx Clusters Curves Points"), FontYellow);
Newline(Context);
}
Print(Context, InstanceRegisteredIndex, FontOrange, 8, 0);
Print(Context, ClusterCount, FontValue, 9, 0);
PrintLargeNumber(Context, CurveCount, FontValue);
Print(Context, TEXT(" "), FontYellow);
PrintLargeNumber(Context, PointCount, FontValue);
}
}
if (bDrawAABB)
{
// Group
if (DispatchThreadId.x == 0)
{
const FHairAABB GroupBound = ReadHairAABB(InstanceRegisteredIndex, GroupAABBBuffer);
AddAABBTWS(GroupBound.Min, GroupBound.Max, float4(1.0f, 0.5f, 0.0f, 1.0f));
}
// Cluster
if (DispatchThreadId.x < ClusterCount)
{
const FHairAABB ClusterBound = ReadHairAABB(ClusterOffset + DispatchThreadId.x, ClusterAABBBuffer, 1);
AddAABBTWS(ClusterBound.Min, ClusterBound.Max, float4(0.5f, 1.0f, 0.0f, 0.3f));
}
}
}
#endif // SHADER_CLUSTERAABB
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Print skin cache stats
#if SHADER_PRINT_HAIR_SKIN_CACHE
uint HairSkinCacheEnable;
uint GPUSkinCacheEnable;
uint InstanceCount;
uint UniqueMeshCount;
Buffer<uint> Infos;
FSTRINGS(UniqueMeshNames)
struct FInstanceInfo
{
uint InstanceIndex;
uint GeometryType;
uint CacheType;
uint InstanceCount;
uint ActiveSectionCount;
uint TotalSectionCount;
uint LOD;
uint Pad;
};
FInstanceInfo ReadInstanceInfo(uint Index)
{
const uint Index8 = Index * 8u;
uint4 Data0 = 0;
Data0.x = Infos[Index8 + 0];
Data0.y = Infos[Index8 + 1];
Data0.z = Infos[Index8 + 2];
Data0.w = Infos[Index8 + 3];
uint4 Data1 = 0;
Data1.x = Infos[Index8 + 4];
Data1.y = Infos[Index8 + 5];
Data1.z = Infos[Index8 + 6];
Data1.w = Infos[Index8 + 7];
FInstanceInfo Out = (FInstanceInfo)0;
Out.InstanceIndex = Data0.x;
Out.GeometryType = Data0.y;
Out.CacheType = Data0.z;
Out.InstanceCount = Data0.w;
Out.ActiveSectionCount = Data1.x;
Out.TotalSectionCount = Data1.y;
Out.LOD = Data1.z;
Out.Pad = Data1.w;
return Out;
}
[numthreads(1, 1, 1)]
void MainCS(uint GroupIndex : SV_GroupIndex, uint3 DispatchThreadId : SV_DispatchThreadID)
{
// Draw AABB of the instance groups
if (any(DispatchThreadId != 0))
{
return;
}
FShaderPrintContext Ctx = InitShaderPrintContext(true, uint2(50, 100));
// Common
bool bAdvanced = false;
{
Print(Ctx, TEXT("Instance count : "), FontWhite);
Print(Ctx, InstanceCount, FontYellow, 8);
Newline(Ctx);
Print(Ctx, TEXT("Unique mesh count : "), FontWhite);
Print(Ctx, UniqueMeshCount, FontYellow, 8);
Newline(Ctx);
Print(Ctx, TEXT("GPU skin cache : "), FontWhite);
PrintBool(Ctx, GPUSkinCacheEnable);
Newline(Ctx);
Print(Ctx, TEXT("Hair skin cache : "), FontWhite);
PrintBool(Ctx, HairSkinCacheEnable);
Newline(Ctx);
bAdvanced = AddCheckbox(Ctx, TEXT("Advanced"), false, FontSilver);
Newline(Ctx);
}
Newline(Ctx);
// Draw instance details
if (bAdvanced)
{
Print(Ctx, TEXT(" Inst. Guide Strands Cards CacheType Sections LOD Name"));
Newline(Ctx);
}
LOOP
for (uint UniqueMeshIndex = 0; UniqueMeshIndex < UniqueMeshCount; ++UniqueMeshIndex)
{
const FInstanceInfo Info = ReadInstanceInfo(UniqueMeshIndex);
if (!bAdvanced && Info.ActiveSectionCount == 0)
{
continue;
}
// Index
Print(Ctx,UniqueMeshIndex, FontSilver, 2);
Print(Ctx, TEXT(" "));
// Instance count
Print(Ctx,Info.InstanceCount, FontYellow, 3, 0);
Print(Ctx, TEXT(" "));
// Geometry type
if (Info.GeometryType & 1) { Print(Ctx, TEXT("x "), FontGreen); } else { Print(Ctx, TEXT(". "), FontRed); }
if (Info.GeometryType & 2) { Print(Ctx, TEXT("x "), FontGreen); } else { Print(Ctx, TEXT(". "), FontRed); }
if (Info.GeometryType & 4) { Print(Ctx, TEXT("x "), FontGreen); } else { Print(Ctx, TEXT(". "), FontRed); }
// Cache type
if (Info.CacheType == 1){ Print(Ctx, TEXT("Skin"), FontEmerald); }
else if (Info.CacheType == 2){ Print(Ctx, TEXT("Hair"), FontEmerald); }
else if (Info.CacheType == 4){ Print(Ctx, TEXT("Geom"), FontEmerald); }
else { Print(Ctx, TEXT("Unkw"), FontRed); }
Print(Ctx, TEXT(" "));
// Section
Print(Ctx,Info.ActiveSectionCount, FontYellow, 3, 0);
Print(Ctx, TEXT("/"));
Print(Ctx,Info.TotalSectionCount, FontYellow, 3, 0);
Print(Ctx, TEXT(" "));
// LOD
Print(Ctx,Info.LOD, FontYellow, 2, 0);
// Name
PrintUniqueMeshNames(Ctx, UniqueMeshIndex, FontWhite);
Newline(Ctx);
}
Newline(Ctx);
}
#endif // SHADER_PRINT_HAIR_SKIN_CACHE