1027 lines
35 KiB
HLSL
1027 lines
35 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "../ShaderPrint.ush"
|
|
#include "../SceneData.ush"
|
|
#include "../DeferredShadingCommon.ush"
|
|
#include "../ColorMap.ush"
|
|
#include "../Visualization.ush"
|
|
#include "../VirtualShadowMaps/VirtualShadowMapPageCacheCommon.ush"
|
|
#include "../VirtualShadowMaps/VirtualShadowMapVisualize.ush"
|
|
#include "../SplineMeshCommon.ush"
|
|
#include "../MeshPaintTextureCommon.ush"
|
|
|
|
#define NUM_VIRTUALTEXTURE_SAMPLES 1
|
|
#include "../VirtualTextureCommon.ush"
|
|
|
|
#include "NaniteDataDecode.ush"
|
|
#include "NaniteAttributeDecode.ush"
|
|
#include "NaniteCullingCommon.ush"
|
|
#include "NaniteVertexFetch.ush"
|
|
#include "NaniteVertexDeformation.ush"
|
|
#include "NaniteEditorCommon.ush"
|
|
|
|
RWTexture2D<float4> DebugOutput;
|
|
|
|
Texture2D<UlongType> VisBuffer64;
|
|
Texture2D<UlongType> DbgBuffer64;
|
|
Texture2D<uint> DbgBuffer32;
|
|
Texture2D<float> SceneDepth;
|
|
Texture2D<float> SceneZDecoded;
|
|
Texture2D<uint4> SceneZLayout;
|
|
Texture2D<uint> ShadingMask;
|
|
Texture2D<uint> FastClearTileVis;
|
|
Texture2D<float4> MeshPaintTexture;
|
|
|
|
StructuredBuffer<FNaniteRasterBinMeta> RasterBinMeta;
|
|
ByteAddressBuffer ShadingBinData;
|
|
ByteAddressBuffer AssemblyMeta;
|
|
|
|
int4 VisualizeConfig;
|
|
int4 VisualizeScales;
|
|
uint RegularMaterialRasterBinCount;
|
|
int2 PickingPixelPos;
|
|
uint MeshPaintTextureCoordinate;
|
|
|
|
// NOTE: Just hardcoded to ENaniteMeshPass::BasePass as an optimization, as all these shaders are only concerned with base pass materials
|
|
static const uint MeshPassIndex = 0;
|
|
|
|
RWStructuredBuffer<FNanitePickingFeedback> FeedbackBuffer;
|
|
|
|
uint GetVisualizeMode()
|
|
{
|
|
return VisualizeConfig.x;
|
|
}
|
|
|
|
uint GetPickingDomain()
|
|
{
|
|
return select(
|
|
GetVisualizeMode() == NANITE_VISUALIZE_PICKING,
|
|
VisualizeConfig.y,
|
|
NANITE_PICKING_DOMAIN_TRIANGLE
|
|
);
|
|
}
|
|
|
|
uint GetPixelProgrammableVisMode()
|
|
{
|
|
return select(
|
|
GetVisualizeMode() == NANITE_VISUALIZE_PIXEL_PROGRAMMABLE_RASTER,
|
|
VisualizeConfig.y,
|
|
NANITE_PIXEL_PROG_VIS_MODE_DEFAULT
|
|
);
|
|
}
|
|
|
|
uint GetMeshPaintingShowMode()
|
|
{
|
|
return select(
|
|
GetVisualizeMode() == NANITE_VISUALIZE_VERTEX_COLOR || GetVisualizeMode() == NANITE_VISUALIZE_MESH_PAINT_TEXTURE,
|
|
VisualizeConfig.y & 0x1,
|
|
NANITE_MESH_PAINTING_SHOW_ALL
|
|
);
|
|
}
|
|
|
|
uint GetMeshPaintingChannelMode()
|
|
{
|
|
return select(
|
|
GetVisualizeMode() == NANITE_VISUALIZE_VERTEX_COLOR || GetVisualizeMode() == NANITE_VISUALIZE_MESH_PAINT_TEXTURE,
|
|
(VisualizeConfig.y >> 1) & 0x7,
|
|
NANITE_MESH_PAINTING_CHANNELS_RGB
|
|
);
|
|
}
|
|
|
|
uint GetMeshPaintingTextureMode()
|
|
{
|
|
return select(
|
|
GetVisualizeMode() == NANITE_VISUALIZE_MESH_PAINT_TEXTURE,
|
|
(VisualizeConfig.y >> 4) & 0x1,
|
|
NANITE_MESH_PAINTING_TEXTURE_DEFAULT
|
|
);
|
|
}
|
|
|
|
float3 ApplyMeshPaintingChannelMode(uint Mode, float4 Color)
|
|
{
|
|
switch (Mode)
|
|
{
|
|
case NANITE_MESH_PAINTING_CHANNELS_R: return float3(Color.r, 0, 0);
|
|
case NANITE_MESH_PAINTING_CHANNELS_G: return float3(0, Color.g, 0);
|
|
case NANITE_MESH_PAINTING_CHANNELS_B: return float3(0, 0, Color.b);
|
|
case NANITE_MESH_PAINTING_CHANNELS_A: return Color.aaa;
|
|
case NANITE_MESH_PAINTING_CHANNELS_RGB:
|
|
default: return Color.rgb;
|
|
}
|
|
}
|
|
|
|
// Apply this to color output because we composite the visualization result with post-tone-mapped space.
|
|
// Call this for visualization modes where it matters, but possibly we should do this in all modes.
|
|
float3 LinearToPostTonemapSpace(float3 Color)
|
|
{
|
|
return LinearToSrgbBranchless(Color);
|
|
}
|
|
|
|
float GetOverdrawScale()
|
|
{
|
|
return clamp(float(VisualizeScales.x), 0.0f, 100.0f) / 100.0f;
|
|
}
|
|
|
|
float GetComplexityScale()
|
|
{
|
|
return clamp(float(VisualizeScales.y), 0.0f, 100.0f) / 100.0f;
|
|
}
|
|
|
|
uint GetShadingExportCount()
|
|
{
|
|
return uint(VisualizeScales.z);
|
|
}
|
|
|
|
bool GetCompositeWithSceneDepth()
|
|
{
|
|
return VisualizeConfig.z != 0;
|
|
}
|
|
|
|
bool ShouldApplySobelFilter()
|
|
{
|
|
return (VisualizeConfig.w & NANITE_VISUALIZE_FLAG_ENABLE_OUTLINE) != 0;
|
|
}
|
|
|
|
bool ShouldApplyDebugShading()
|
|
{
|
|
return (VisualizeConfig.w & NANITE_VISUALIZE_FLAG_ENABLE_DEBUG_SHADING) != 0;
|
|
}
|
|
|
|
struct FVisualizeEffects
|
|
{
|
|
float3 OutlineColor;
|
|
bool bDarkOutline;
|
|
bool bApplySobel;
|
|
bool bApplyDebugShading;
|
|
};
|
|
|
|
FVisualizeEffects GetVisualizeModeEffects(uint VisualizeMode)
|
|
{
|
|
FVisualizeEffects Output;
|
|
Output.OutlineColor = 1;
|
|
Output.bApplySobel = true;
|
|
Output.bDarkOutline = false;
|
|
Output.bApplyDebugShading = true;
|
|
|
|
switch (VisualizeMode)
|
|
{
|
|
case NANITE_VISUALIZE_TRIANGLES:
|
|
case NANITE_VISUALIZE_PATCHES:
|
|
case NANITE_VISUALIZE_CLUSTERS:
|
|
case NANITE_VISUALIZE_ASSEMBLIES:
|
|
Output.bDarkOutline = true;
|
|
break;
|
|
|
|
case NANITE_VISUALIZE_SCENE_Z_MIN:
|
|
case NANITE_VISUALIZE_SCENE_Z_MAX:
|
|
case NANITE_VISUALIZE_SCENE_Z_DELTA:
|
|
case NANITE_VISUALIZE_SCENE_Z_DECODED:
|
|
Output.bApplySobel = false;
|
|
Output.bApplyDebugShading = false;
|
|
break;
|
|
|
|
case NANITE_VISUALIZE_PICKING:
|
|
Output.bApplySobel = false;
|
|
break;
|
|
|
|
case NANITE_VISUALIZE_SHADOW_CASTERS:
|
|
Output.bApplySobel = false;
|
|
break;
|
|
}
|
|
|
|
// Let these features be externally disabled
|
|
Output.bApplySobel &= ShouldApplySobelFilter();
|
|
Output.bApplyDebugShading &= ShouldApplyDebugShading();
|
|
|
|
return Output;
|
|
}
|
|
|
|
float3 ApplySobelFilter(uint2 PixelPosXY, uint DepthInt, float3 Color, FVisualizeEffects Effects)
|
|
{
|
|
// Sobel edge detect depth
|
|
static const int SobelX[] =
|
|
{
|
|
1, 0, -1,
|
|
2, -2,
|
|
1, 0, -1
|
|
};
|
|
|
|
static const int SobelY[] =
|
|
{
|
|
1, 2, 1,
|
|
0, 0,
|
|
-1, -2, -1
|
|
};
|
|
|
|
static const uint2 UVSample[] =
|
|
{
|
|
{-1, 1}, {0, 1}, {1, 1},
|
|
{-1, 0}, {1, 0},
|
|
{-1, -1}, {0, -1}, {1, -1}
|
|
};
|
|
|
|
float3 DepthGradX = float3(0.0f, 0.0f, 0.0f);
|
|
float3 DepthGradY = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
uint DepthIntCurrent;
|
|
uint VisibleClusterIndexCurrent;
|
|
uint TriIndexCurrent;
|
|
|
|
// Get the center tap first
|
|
UlongType VisPixelCurrent = VisBuffer64[PixelPosXY];
|
|
UnpackVisPixel(VisPixelCurrent, DepthIntCurrent, VisibleClusterIndexCurrent, TriIndexCurrent);
|
|
const float CenterDepth = asfloat(DepthIntCurrent);
|
|
const bool bInnerBorder = GetCompositeWithSceneDepth() && !Effects.bDarkOutline;
|
|
|
|
for (uint Tap = 0; Tap < 8; ++Tap)
|
|
{
|
|
VisPixelCurrent = VisBuffer64[PixelPosXY + UVSample[Tap]];
|
|
UnpackVisPixel(VisPixelCurrent, DepthIntCurrent, VisibleClusterIndexCurrent, TriIndexCurrent);
|
|
|
|
float TapDepth = asfloat(DepthIntCurrent);
|
|
|
|
// We generally only want outer outlines, so don't consider further depth than center
|
|
// Exception is when not applying the filter outside of Nanite pixels and Nanite borders sky or non-Nanite
|
|
TapDepth = (TapDepth == 0.0f && bInnerBorder) ? 0.0f : max(TapDepth, CenterDepth);
|
|
|
|
float SampleDensityDepth = log2( ConvertFromDeviceZ(TapDepth) + 1.0 ) * 10.0;
|
|
|
|
DepthGradX += SobelX[Tap] * SampleDensityDepth;
|
|
DepthGradY += SobelY[Tap] * SampleDensityDepth;
|
|
}
|
|
|
|
// Build outline from depth
|
|
float3 DepthOutline = max(abs(DepthGradX), abs(DepthGradY));
|
|
|
|
float3 CombineColor;
|
|
if( Effects.bDarkOutline )
|
|
CombineColor = Color * ( 1.0 - DepthOutline * 0.25 );
|
|
else
|
|
CombineColor = Color + DepthOutline * 0.25 * Effects.OutlineColor;
|
|
|
|
return saturate(CombineColor);
|
|
}
|
|
|
|
|
|
[numthreads(8, 8, 1)]
|
|
void VisualizeCS(uint3 DTID : SV_DispatchThreadID, uint3 GID : SV_GroupID)
|
|
{
|
|
const uint VisualizeMode = GetVisualizeMode();
|
|
|
|
const uint2 PickingPos = uint2(PickingPixelPos);
|
|
const uint2 PixelPos = DTID.xy + uint2(View.ViewRectMin.xy);
|
|
const uint2 TilePos = GID.xy; // 8x8 tile 2D coord
|
|
|
|
const FNaniteView NaniteView = GetNaniteView( 0 );
|
|
|
|
if(!IsPixelInRect(PixelPos, NaniteView.ViewRect))
|
|
return;
|
|
|
|
const UlongType VisPixel = VisBuffer64[PixelPos];
|
|
|
|
uint DepthInt;
|
|
uint VisibleClusterIndex;
|
|
uint TriIndex;
|
|
bool bIsImposter;
|
|
UnpackVisPixel(VisPixel, DepthInt, VisibleClusterIndex, TriIndex, bIsImposter);
|
|
|
|
float4 SvPosition = float4( PixelPos + 0.5, asfloat( DepthInt ), 1 );
|
|
|
|
FShadingMask UnpackedMask = UnpackShadingMask(ShadingMask[PixelPos]);
|
|
|
|
float3 Result = float3(0, 0, 0);
|
|
float Opacity = 1.0f;
|
|
const FVisualizeEffects Effects = GetVisualizeModeEffects(VisualizeMode);
|
|
|
|
if (VisualizeMode == NANITE_VISUALIZE_OVERDRAW)
|
|
{
|
|
uint DebugValueAdd = DbgBuffer32[PixelPos];
|
|
|
|
const float OverdrawScale = GetOverdrawScale();
|
|
const float OverdrawCount = DebugValueAdd; // Num of evaluations per pixel
|
|
const float OverdrawColor = 1 - exp2(-OverdrawCount * OverdrawScale);
|
|
Result = ColorMapInferno(OverdrawColor);
|
|
}
|
|
else if (VisibleClusterIndex != 0xFFFFFFFF && (!GetCompositeWithSceneDepth() || UnpackedMask.bIsNanitePixel))
|
|
{
|
|
// Nanite Pixel
|
|
|
|
UlongType DbgPixel = DbgBuffer64[PixelPos];
|
|
uint DebugDepthInt;
|
|
uint DebugValueMax;
|
|
UnpackDbgPixel(DbgPixel, DebugDepthInt, DebugValueMax);
|
|
uint DebugValueAdd = DbgBuffer32[PixelPos];
|
|
|
|
const uint PackedShadingMaterialFlags = ShadingBinData.Load<FNaniteShadingBinMeta>(UnpackedMask.ShadingBin * NANITE_SHADING_BIN_META_BYTES).MaterialFlags;
|
|
FNaniteMaterialFlags ShadingMaterialFlags = UnpackNaniteMaterialFlags(PackedShadingMaterialFlags);
|
|
|
|
FVisibleCluster VisibleCluster = GetVisibleCluster(VisibleClusterIndex);
|
|
FInstanceSceneData InstanceData = GetInstanceSceneDataUnchecked(VisibleCluster);
|
|
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
|
|
FInstanceViewData InstanceViewData = GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId);
|
|
FCluster Cluster = GetCluster(VisibleCluster.PageIndex, VisibleCluster.ClusterIndex);
|
|
|
|
const bool bEvaluateWPO = (VisibleCluster.Flags & NANITE_CULLING_FLAG_ENABLE_WPO) != 0 && !bIsImposter;
|
|
const bool bFallbackRaster = (VisibleCluster.Flags & NANITE_CULLING_FLAG_FALLBACK_RASTER) != 0;
|
|
|
|
uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
|
|
|
|
FReconstructedVoxelData VoxelData;
|
|
|
|
BRANCH
|
|
if (Cluster.bVoxel)
|
|
{
|
|
VoxelData = ReconstructVoxel(NaniteView, PrimitiveData, InstanceData, InstanceViewData, VisibleCluster, Cluster, SvPosition, TriIndex);
|
|
TriIndices = VoxelData.VertIndex;
|
|
}
|
|
|
|
FNanitePostDeformVertex Verts[3];
|
|
FetchAndDeformLocalNaniteVerts<3>(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, TriIndices, NANITE_MAX_UVS, Verts);
|
|
|
|
const float3 PointTranslatedWorld0 = mul( float4( Verts[0].Position, 1 ), InstanceDynamicData.LocalToTranslatedWorld ).xyz;
|
|
const float3 PointTranslatedWorld1 = mul( float4( Verts[1].Position, 1 ), InstanceDynamicData.LocalToTranslatedWorld ).xyz;
|
|
const float3 PointTranslatedWorld2 = mul( float4( Verts[2].Position, 1 ), InstanceDynamicData.LocalToTranslatedWorld ).xyz;
|
|
|
|
FBarycentrics Barycentrics;
|
|
if (!Cluster.bVoxel)
|
|
{
|
|
const float4 PointClip0 = mul( float4( PointTranslatedWorld0, 1 ), NaniteView.TranslatedWorldToClip );
|
|
const float4 PointClip1 = mul( float4( PointTranslatedWorld1, 1 ), NaniteView.TranslatedWorldToClip );
|
|
const float4 PointClip2 = mul( float4( PointTranslatedWorld2, 1 ), NaniteView.TranslatedWorldToClip );
|
|
|
|
const float2 PixelClip = (SvPosition.xy - NaniteView.ViewRect.xy) * NaniteView.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
|
|
Barycentrics = CalculateTriangleBarycentrics(PixelClip, PointClip0, PointClip1, PointClip2, NaniteView.ViewSizeAndInvSize.zw);
|
|
}
|
|
else
|
|
{
|
|
Barycentrics.Value = float3(1,0,0);
|
|
Barycentrics.Value_dx = 0;
|
|
Barycentrics.Value_dy = 0;
|
|
}
|
|
|
|
float4 VertexColor = Lerp(Verts[0].Color, Verts[1].Color, Verts[2].Color, Barycentrics).Value;
|
|
|
|
TDual<float2> TexCoords[NANITE_MAX_UVS];
|
|
UNROLL
|
|
for (uint i = 0; i < NANITE_MAX_UVS; i++)
|
|
{
|
|
TexCoords[i] = Lerp(Verts[0].TexCoords[i], Verts[1].TexCoords[i], Verts[2].TexCoords[i], Barycentrics);
|
|
}
|
|
|
|
|
|
uint RasterBin = GetMaterialRasterBin(Cluster, InstanceData.PrimitiveId, MeshPassIndex, TriIndex, RegularMaterialRasterBinCount, bFallbackRaster);
|
|
const FNaniteMaterialFlags RasterMaterialFlags = UnpackNaniteMaterialFlags(RasterBinMeta[RasterBin].MaterialFlags_DepthBlock & 0xFFFFu);
|
|
RasterBin = RemapRasterBin(RasterBin, RenderFlags, RasterMaterialFlags, Cluster.bVoxel, InstanceViewData.bIsDeforming);
|
|
|
|
const int HierarchyOffset = InstanceData.NaniteHierarchyOffset;
|
|
|
|
// Note: The mode is no longer a bitmask at this point, just a single visualization mode.
|
|
|
|
if (VisualizeMode == NANITE_VISUALIZE_TESSELLATION)
|
|
{
|
|
const uint SubPatch = BitFieldExtractU32(DebugValueMax, 8, 8);
|
|
const uint MicroTri = BitFieldExtractU32(DebugValueMax, 8, 16);
|
|
|
|
const float3 PatchColor = IntToColor(TriIndex).x * 0.5 + 0.5;
|
|
|
|
float3 SubPatchColor = IntToColor(SubPatch << 8u) * 0.6 + 0.4;
|
|
float3 MicroTriColor = IntToColor(MicroTri) * 0.6 + 0.4;
|
|
|
|
MicroTriColor = lerp(MicroTriColor, dot(MicroTriColor, float3(0.3, 0.6, 0.1)), 0.5);
|
|
SubPatchColor /= max3(SubPatchColor.x, SubPatchColor.y, SubPatchColor.z);
|
|
Result = MicroTriColor * SubPatchColor * PatchColor;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_TRIANGLES)
|
|
{
|
|
const uint SubPatch = BitFieldExtractU32(DebugValueMax, 8, 8);
|
|
const uint MicroTri = BitFieldExtractU32(DebugValueMax, 8, 16);
|
|
if( MicroTri != 0u )
|
|
{
|
|
TriIndex = MurmurAdd( TriIndex, SubPatch );
|
|
TriIndex = MurmurAdd( TriIndex, MicroTri );
|
|
}
|
|
|
|
Result = IntToColor( TriIndex );
|
|
Result = Result * 0.8 + 0.2;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PATCHES)
|
|
{
|
|
Result = IntToColor(TriIndex);
|
|
Result = Result * 0.8 + 0.2;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_VOXELS)
|
|
{
|
|
Result = IntToColor(TriIndex);
|
|
Result = lerp(Result, Cluster.bVoxel ? float3(0, 1, 0) : float3(1, 0, 0), 0.5f);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_CLUSTERS)
|
|
{
|
|
Result = IntToColor( MurmurAdd( VisibleCluster.PageIndex, VisibleCluster.ClusterIndex ) );
|
|
Result = Result * 0.8 + 0.2;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_GROUPS)
|
|
{
|
|
Result = IntToColor(Cluster.GroupIndex);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PAGES)
|
|
{
|
|
Result = IntToColor(VisibleCluster.PageIndex);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_ASSEMBLIES)
|
|
{
|
|
if (IsAssemblyPartCluster(VisibleCluster))
|
|
{
|
|
// Get the "local" assembly transform index from the assembly metadata
|
|
const uint HierarchyTransformIndex = AssemblyMeta.Load(VisibleCluster.AssemblyTransformIndex * 4u);
|
|
Result = IntToColor(HierarchyTransformIndex) * 0.8 + 0.2;
|
|
}
|
|
else if (PrimitiveData.NaniteAssemblyTransformOffset != 0xFFFFFFFFu)
|
|
{
|
|
Result = float3(0, 0, 0.2f);
|
|
}
|
|
else
|
|
{
|
|
Result = float3(0.15f, 0.15f, 0.15f);
|
|
}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SKINNING)
|
|
{
|
|
if (Cluster.bSkinning && RasterMaterialFlags.bSkinnedMesh)
|
|
{
|
|
// Green = active, Red = disabled
|
|
Result = InstanceViewData.bIsDeforming ?
|
|
float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
// Grey = unskinned
|
|
Result = float3(0.15f, 0.15f, 0.15f);
|
|
}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PRIMITIVES)
|
|
{
|
|
Result = IntToColor(InstanceData.PrimitiveId) * 0.8;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_INSTANCES)
|
|
{
|
|
Result = IntToColor(VisibleCluster.InstanceId) * 0.8;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_RASTER_MODE)
|
|
{
|
|
const uint RasterMode = BitFieldExtractU32(DebugValueMax, 8, 0);
|
|
uint VisValue = 0u;
|
|
if (RasterMode == 1u)
|
|
{
|
|
// Hardware Raster
|
|
VisValue = 1u;
|
|
}
|
|
else if (RasterMode == 2u)
|
|
{
|
|
// Software Raster
|
|
VisValue = 2u;
|
|
}
|
|
else if (RasterMode == 3u)
|
|
{
|
|
// Software Tessellation
|
|
VisValue = 6000u;
|
|
}
|
|
|
|
Result = (IntToColor(VisValue) * 0.75 + 0.25) * (IntToColor(0).x * 0.5 + 0.5);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_RASTER_BINS)
|
|
{
|
|
Result = IntToColor(RasterBin) * 0.8 + 0.2;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SHADING_BINS)
|
|
{
|
|
const uint ShadingBin = GetMaterialShadingBin(Cluster, InstanceData.PrimitiveId, MeshPassIndex, TriIndex);
|
|
Result = IntToColor(ShadingBin) * 0.8 + 0.2;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_HIERARCHY_OFFSET)
|
|
{
|
|
Result = IntToColor(HierarchyOffset);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_MATERIAL_COUNT)
|
|
{
|
|
Result = IntToColor(GetMaterialCount(Cluster));
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_MATERIAL_MODE)
|
|
{
|
|
Result = IsMaterialFastPath(Cluster) ? float3(0, 1, 0) : float3(1, 0, 0);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_MATERIAL_INDEX)
|
|
{
|
|
Result = IntToColor(GetRelativeMaterialIndex(Cluster, TriIndex));
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SHADING_WRITE_MASK)
|
|
{
|
|
const uint WriteMask = BitFieldExtractU32(PackedShadingMaterialFlags, 8u, 24u);
|
|
Result = IntToColor(WriteMask);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MIN || VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MAX || VisualizeMode == NANITE_VISUALIZE_SCENE_Z_DELTA)// || VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MASK)
|
|
{
|
|
const uint4 ZLayout = SceneZLayout[PixelPos];
|
|
float2 MinMax = float2(f16tof32(ZLayout.y), f16tof32(ZLayout.y >> 16u));
|
|
|
|
if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MIN)
|
|
{
|
|
Result = float3(pow(MinMax.x, 0.11f), 0.0f, 0.0f);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MAX)
|
|
{
|
|
Result = float3(pow(MinMax.y, 0.11f), 0.0f, 0.0f);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_DELTA)
|
|
{
|
|
Result = float3(pow(MinMax.y - MinMax.x, 0.11f), 0.0f, 0.0f);
|
|
}
|
|
//else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_MASK)
|
|
//{
|
|
// Result = IntToColor((uint)ZLayout.z);
|
|
//}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_DECODED)
|
|
{
|
|
const float ZDecoded = SceneZDecoded[PixelPos];
|
|
Result = float3(pow(ZDecoded, 0.11f), 0.0f, 0.0f);
|
|
}
|
|
#if USE_EDITOR_SHADERS
|
|
else if (VisualizeMode == NANITE_VISUALIZE_HIT_PROXY_DEPTH)
|
|
{
|
|
if ((InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_EDITOR_DATA) != 0u)
|
|
{
|
|
Result = IntToColor(InstanceData.EditorData.HitProxyPacked);
|
|
}
|
|
else
|
|
{
|
|
Result = IntToColor(GetMaterialHitProxyId(Cluster, InstanceData.PrimitiveId, TriIndex, MaterialHitProxyTable));
|
|
}
|
|
}
|
|
#endif
|
|
else if (VisualizeMode == NANITE_VISUALIZE_NO_DERIVATIVE_OPS)
|
|
{
|
|
const uint ModeValue = select(ShadingMaterialFlags.bNoDerivativeOps, 2, 1);
|
|
Result = (IntToColor(ModeValue) * 0.75 + 0.25) * (IntToColor(TriIndex).x * 0.5 + 0.5);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_FAST_CLEAR_TILES)
|
|
{
|
|
const uint TileData = FastClearTileVis[PixelPos];
|
|
// Tile data has a counter for each MRT that is still marked as "clear" in
|
|
// the meta data, indicating that the next fast clear eliminate will fully
|
|
// write through this memory (slow tile).
|
|
Result = ColorMapInferno(float(TileData) / float(GetShadingExportCount()));
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_NANITE_MASK)
|
|
{
|
|
Result = float3(0, 1, 0);
|
|
Opacity = 0.5f;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_EVALUATE_WORLD_POSITION_OFFSET)
|
|
{
|
|
const bool bAlwaysEvaluateWPO = (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_HAS_ALWAYS_EVALUATE_WPO_MATERIALS) != 0u;
|
|
if (bAlwaysEvaluateWPO)
|
|
{
|
|
Result = float3(1, 1, 0);
|
|
}
|
|
else if (bEvaluateWPO)
|
|
{
|
|
Result = float3(0, 1, 0);
|
|
}
|
|
else
|
|
{
|
|
Result = float3(1, 0, 0);
|
|
}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PIXEL_PROGRAMMABLE_RASTER)
|
|
{
|
|
bool bHighlight = false;
|
|
switch (GetPixelProgrammableVisMode())
|
|
{
|
|
case NANITE_PIXEL_PROG_VIS_MODE_DEFAULT:
|
|
bHighlight = RasterMaterialFlags.bPixelDiscard || RasterMaterialFlags.bPixelDepthOffset;
|
|
break;
|
|
case NANITE_PIXEL_PROG_VIS_MODE_MASKED_ONLY:
|
|
bHighlight = RasterMaterialFlags.bPixelDiscard;
|
|
break;
|
|
case NANITE_PIXEL_PROG_VIS_MODE_PDO_ONLY:
|
|
bHighlight = RasterMaterialFlags.bPixelDepthOffset;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (bFallbackRaster &&
|
|
(PrimitiveData.PixelProgrammableDistanceSquared > 0.0f ||
|
|
PrimitiveData.MaterialDisplacementFadeOutSize > 0.0f))
|
|
{
|
|
// We've disabled pixel programmable for this cluster
|
|
bHighlight = false;
|
|
}
|
|
|
|
Result = select(bHighlight, float3(1, 0, 0), float3(0.1, 0, 0.3));
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_LIGHTMAP_UVS)
|
|
{
|
|
const float2 LightmapUVs = TexCoords[PrimitiveData.LightmapUVIndex].Value;
|
|
Result = float3(LightmapUVs.x, LightmapUVs.y, 0);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_LIGHTMAP_UV_INDEX)
|
|
{
|
|
Result = IntToColor(PrimitiveData.LightmapUVIndex);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_LIGHTMAP_DATA_INDEX)
|
|
{
|
|
Result = IntToColor(PrimitiveData.LightmapDataIndex);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_POSITION_BITS)
|
|
{
|
|
const uint NumBits = Cluster.PosBits.x + Cluster.PosBits.y + Cluster.PosBits.z;
|
|
|
|
if (NumBits <= 30)
|
|
{
|
|
Result = lerp(float3(0.0f, 1.0f, 0.0f), float3(1.0f, 1.0f, 1.0f), NumBits / 30.0f);
|
|
}
|
|
else
|
|
{
|
|
Result = lerp(float3(1.0f, 1.0f, 1.0f), float3(1.0f, 0.0f, 0.0f), (NumBits - 30) / (float)(3 * 16 - 30));
|
|
}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SHADOW_CASTERS)
|
|
{
|
|
FShadowCasterDrawParameters ShadowCasterDrawParameters = GetShadowCasterDrawParameters(NaniteView, InstanceData, InstanceViewData, InstanceDynamicData, PrimitiveData);
|
|
Result = ShadowCasterDrawParameters.Color;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_DISPLACEMENT_SCALE)
|
|
{
|
|
const float DisplacementCenter = RasterBinMeta[RasterBin].MaterialDisplacementParams.Center;
|
|
const float DisplacementMagnitude = RasterBinMeta[RasterBin].MaterialDisplacementParams.Magnitude;
|
|
|
|
const float MinDisplacement = (0.0f - DisplacementCenter) * DisplacementMagnitude;
|
|
const float MaxDisplacement = (1.0f - DisplacementCenter) * DisplacementMagnitude;
|
|
|
|
const float AbsDelta = abs(MaxDisplacement - MinDisplacement);
|
|
|
|
Result = GreenToRedTurbo(AbsDelta / 30.0f);
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_VERTEX_COLOR)
|
|
{
|
|
#if USE_EDITOR_SHADERS
|
|
if (GetMeshPaintingShowMode() == NANITE_MESH_PAINTING_SHOW_SELECTED && !IsInstanceSelected(InstanceData, VisibleCluster, TriIndex))
|
|
{
|
|
Opacity = 0;
|
|
}
|
|
#endif
|
|
Result = LinearToPostTonemapSpace(ApplyMeshPaintingChannelMode(GetMeshPaintingChannelMode(), VertexColor));
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_MESH_PAINT_TEXTURE)
|
|
{
|
|
#if USE_EDITOR_SHADERS
|
|
if (GetMeshPaintingShowMode() == NANITE_MESH_PAINTING_SHOW_SELECTED && !IsInstanceSelected(InstanceData, VisibleCluster, TriIndex))
|
|
{
|
|
Opacity = 0;
|
|
}
|
|
#endif
|
|
if (GetMeshPaintingShowMode() == NANITE_MESH_PAINTING_SHOW_ALL && GetMeshPaintingTextureMode() == NANITE_MESH_PAINTING_TEXTURE_DEFAULT && !GetMeshPaintTextureDescriptorIsValid(PrimitiveData))
|
|
{
|
|
Opacity = 0;
|
|
}
|
|
|
|
if (Opacity > 0)
|
|
{
|
|
if (GetMeshPaintingTextureMode() == NANITE_MESH_PAINTING_TEXTURE_DEFAULT)
|
|
{
|
|
// Sample the shared mesh paint virtual texture atlas.
|
|
TDual<float2> UV = TexCoords[GetMeshPaintTextureCoordinateIndex(PrimitiveData)];
|
|
float2 SVPositionXY = (float2)PixelPos + 0.5f;
|
|
FVirtualTextureFeedbackParams VirtualTextureFeedback;
|
|
InitializeVirtualTextureFeedback(VirtualTextureFeedback);
|
|
VTPageTableResult PageTableResult = TextureLoadVirtualPageTableGrad(Scene.MeshPaint.PageTableTexture, VTPageTableUniform_Unpack(GetMeshPaintTextureDescriptor(PrimitiveData)), UV.Value, VTADDRESSMODE_CLAMP, VTADDRESSMODE_CLAMP, UV.Value_dx, UV.Value_dy, SVPositionXY, VirtualTextureFeedback);
|
|
float4 Color = TextureVirtualSample(Scene.MeshPaint.PhysicalTexture, View.SharedBilinearClampedSampler, PageTableResult, 0, VTUniform_Unpack(Scene.MeshPaint.PackedUniform));
|
|
FinalizeVirtualTextureFeedback(VirtualTextureFeedback, float4(SVPositionXY, 0, 0), View.VTFeedbackBuffer);
|
|
Result = LinearToPostTonemapSpace(ApplyMeshPaintingChannelMode(GetMeshPaintingChannelMode(), Color));
|
|
}
|
|
else
|
|
{
|
|
// Sample a single selected mesh paint texture.
|
|
TDual<float2> UV = TexCoords[MeshPaintTextureCoordinate];
|
|
float4 Color = MeshPaintTexture.SampleGrad(View.SharedBilinearClampedSampler, UV.Value, UV.Value_dx, UV.Value_dy);
|
|
Result = LinearToPostTonemapSpace(ApplyMeshPaintingChannelMode(GetMeshPaintingChannelMode(), Color));
|
|
}
|
|
}
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PICKING)
|
|
{
|
|
const UlongType PickedVisPixel = VisBuffer64[PickingPos];
|
|
|
|
uint PickedDepthInt;
|
|
uint PickedVisibleClusterIndex;
|
|
uint PickedTriIndex;
|
|
UnpackVisPixel(PickedVisPixel, PickedDepthInt, PickedVisibleClusterIndex, PickedTriIndex);
|
|
|
|
FVisibleCluster PickedVisibleCluster = GetVisibleCluster(PickedVisibleClusterIndex);
|
|
FInstanceSceneData PickedInstanceData = GetInstanceSceneDataUnchecked(PickedVisibleCluster);
|
|
|
|
bool bPicked = false;
|
|
switch (GetPickingDomain())
|
|
{
|
|
case NANITE_PICKING_DOMAIN_TRIANGLE:
|
|
bPicked = (VisibleClusterIndex == PickedVisibleClusterIndex && PickedTriIndex == TriIndex);
|
|
Result = IntToColor(TriIndex) * 0.8 + 0.2;
|
|
break;
|
|
|
|
case NANITE_PICKING_DOMAIN_CLUSTER:
|
|
bPicked = (VisibleClusterIndex == PickedVisibleClusterIndex);
|
|
Result = IntToColor(VisibleCluster.ClusterIndex) * 0.8;
|
|
break;
|
|
|
|
case NANITE_PICKING_DOMAIN_INSTANCE:
|
|
bPicked = (VisibleCluster.InstanceId == PickedVisibleCluster.InstanceId);
|
|
Result = IntToColor(VisibleCluster.InstanceId) * 0.8;
|
|
break;
|
|
|
|
case NANITE_PICKING_DOMAIN_PRIMITIVE:
|
|
bPicked = (InstanceData.PrimitiveId == PickedInstanceData.PrimitiveId);
|
|
Result = IntToColor(InstanceData.PrimitiveId) * 0.8;
|
|
break;
|
|
|
|
default:
|
|
// Invalid picking domain
|
|
break;
|
|
}
|
|
|
|
if (bPicked)
|
|
{
|
|
Result = float3(1.0f, 0.0f, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
Result *= 0.3f;
|
|
}
|
|
}
|
|
|
|
if (Effects.bApplyDebugShading && !Cluster.bVoxel)
|
|
{
|
|
// Apply N dot V shading using face normal
|
|
// TODO: Debug shade voxels? N will need to be done differently here
|
|
const float3 N = normalize( cross( PointTranslatedWorld2 - PointTranslatedWorld0, PointTranslatedWorld1 - PointTranslatedWorld0 ) );
|
|
|
|
float3 PixelTranslatedWorld =
|
|
PointTranslatedWorld0 * Barycentrics.Value.x +
|
|
PointTranslatedWorld1 * Barycentrics.Value.y +
|
|
PointTranslatedWorld2 * Barycentrics.Value.z;
|
|
|
|
float3 V = normalize( PixelTranslatedWorld - View.TranslatedWorldCameraOrigin );
|
|
|
|
Result *= abs( dot( N, V ) ) * 0.7 + 0.3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non-Nanite Pixel
|
|
|
|
if (GetVisualizeMode() == NANITE_VISUALIZE_NANITE_MASK)
|
|
{
|
|
if (SceneDepth[PixelPos] > 0.0f) // only visualize written fragments
|
|
{
|
|
Result = float3(1, 0, 0);
|
|
Opacity = 0.5f;
|
|
}
|
|
}
|
|
else if (GetVisualizeMode() == NANITE_VISUALIZE_VERTEX_COLOR
|
|
|| GetVisualizeMode() == NANITE_VISUALIZE_MESH_PAINT_TEXTURE
|
|
|| GetVisualizeMode() == NANITE_VISUALIZE_SHADOW_CASTERS)
|
|
{
|
|
Opacity = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (Effects.bApplySobel && (!GetCompositeWithSceneDepth() || UnpackedMask.bIsNanitePixel))
|
|
{
|
|
Result = ApplySobelFilter(PixelPos, DepthInt, Result, Effects);
|
|
}
|
|
|
|
DebugOutput[PixelPos] = float4(Result, Opacity);
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void PickingCS(uint3 DTID : SV_DispatchThreadID, uint3 GID : SV_GroupID)
|
|
{
|
|
FNanitePickingFeedback FeedbackResults = (FNanitePickingFeedback)0;
|
|
|
|
const uint2 PickingPos = uint2(PickingPixelPos);
|
|
FeedbackResults.PixelX = PickingPos.x;
|
|
FeedbackResults.PixelY = PickingPos.y;
|
|
|
|
const UlongType PickedPixel = VisBuffer64[PickingPos];
|
|
|
|
uint DepthInt;
|
|
uint VisibleClusterIndex;
|
|
uint TriIndex;
|
|
UnpackVisPixel(PickedPixel, DepthInt, VisibleClusterIndex, TriIndex);
|
|
|
|
FNaniteView NaniteView = GetNaniteView(0);
|
|
|
|
FShadingMask UnpackedMask = UnpackShadingMask(ShadingMask[PickingPos]);
|
|
|
|
if (VisibleClusterIndex != 0xFFFFFFFFu && UnpackedMask.bIsNanitePixel)
|
|
{
|
|
UlongType DbgPixel = DbgBuffer64[PickingPos];
|
|
uint DebugDepthInt;
|
|
uint DebugValueMax;
|
|
UnpackDbgPixel(DbgPixel, DebugDepthInt, DebugValueMax);
|
|
uint DebugValueAdd = DbgBuffer32[PickingPos];
|
|
|
|
FVisibleCluster VisibleCluster = GetVisibleCluster(VisibleClusterIndex);
|
|
FInstanceSceneData InstanceData = GetInstanceSceneDataUnchecked(VisibleCluster);
|
|
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
|
|
FInstanceViewData InstanceViewData = GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId);
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
|
|
FCluster Cluster = GetCluster(VisibleCluster.PageIndex, VisibleCluster.ClusterIndex);
|
|
|
|
const bool bWPOEnabled = (VisibleCluster.Flags & NANITE_CULLING_FLAG_ENABLE_WPO) != 0;
|
|
const bool bFallbackRaster = (VisibleCluster.Flags & NANITE_CULLING_FLAG_FALLBACK_RASTER) != 0;
|
|
|
|
uint RasterBin = GetMaterialRasterBin(Cluster, InstanceData.PrimitiveId, MeshPassIndex, TriIndex, RegularMaterialRasterBinCount, bFallbackRaster);
|
|
const FNaniteMaterialFlags RasterMaterialFlags = UnpackNaniteMaterialFlags(RasterBinMeta[RasterBin].MaterialFlags_DepthBlock & 0xFFFFu);
|
|
RasterBin = RemapRasterBin(RasterBin, RenderFlags, RasterMaterialFlags, Cluster.bVoxel, InstanceViewData.bIsDeforming);
|
|
|
|
const uint AssemblyTransformIndex = IsAssemblyPartCluster(VisibleCluster) ? AssemblyMeta.Load(VisibleCluster.AssemblyTransformIndex * 4u) : (uint)-1;
|
|
|
|
FeedbackResults.PrimitiveId = InstanceData.PrimitiveId;
|
|
FeedbackResults.InstanceId = VisibleCluster.InstanceId;
|
|
FeedbackResults.PersistentIndex = PrimitiveData.PersistentPrimitiveIndex;
|
|
FeedbackResults.ClusterIndex = VisibleCluster.ClusterIndex;
|
|
FeedbackResults.GroupIndex = Cluster.GroupIndex;
|
|
FeedbackResults.PageIndex = VisibleCluster.PageIndex;
|
|
FeedbackResults.TriangleIndex = TriIndex;
|
|
FeedbackResults.DepthInt = DepthInt;
|
|
FeedbackResults.RasterMode = DebugValueMax;
|
|
FeedbackResults.RasterBin = RasterBin;
|
|
FeedbackResults.ShadingBin = GetMaterialShadingBin(Cluster, InstanceData.PrimitiveId, MeshPassIndex, TriIndex);
|
|
FeedbackResults.MaterialIndex = GetRelativeMaterialIndex(Cluster, TriIndex);
|
|
FeedbackResults.MaterialCount = GetMaterialCount(Cluster);
|
|
FeedbackResults.MaterialMode = IsMaterialFastPath(Cluster) ? 0u : 1u;
|
|
FeedbackResults.HierarchyOffset = InstanceData.NaniteHierarchyOffset;
|
|
FeedbackResults.RuntimeResourceID = InstanceData.NaniteRuntimeResourceID;
|
|
FeedbackResults.AssemblyTransformOffset = PrimitiveData.NaniteAssemblyTransformOffset;
|
|
FeedbackResults.AssemblyTransformIndex = AssemblyTransformIndex;
|
|
|
|
const float4 ApproximateClusterBoundsColor = ColorCyan;
|
|
const float4 SkinnedClusterBoundsColor = ColorLightGreen;
|
|
const float4 ClusterBoundsColor = ColorLightRed;
|
|
const float4 InstanceBoundsColor = ColorOrange;
|
|
const float4 ClusterLODBoundsColor = ColorYellow;
|
|
|
|
FShaderPrintContext ShaderPrint = InitShaderPrintContext(true, float2(0.15, 0.1));
|
|
|
|
Print( ShaderPrint, TEXT("LODError "), FontYellow );
|
|
Print( ShaderPrint, Cluster.LODError );
|
|
Newline( ShaderPrint );
|
|
|
|
Print( ShaderPrint, TEXT("EdgeLength "), FontYellow );
|
|
Print( ShaderPrint, Cluster.EdgeLength );
|
|
Newline( ShaderPrint );
|
|
|
|
const float4x4 LocalToTranslatedWorld = DFFastToTranslatedWorld(InstanceData.LocalToWorld, NaniteView.PreViewTranslation);
|
|
|
|
// If WPO and/or material displacement is enabled, the cluster bounds will have been dilated to account for max displacement
|
|
const float3 WPOExtent = GetLocalMaxWPOExtent(PrimitiveData, InstanceData, bWPOEnabled);
|
|
const float DisplacementExtent = GetMaxMaterialDisplacementExtent(PrimitiveData, bFallbackRaster, false /* bIsShadowPass */);
|
|
|
|
FNodeCullingBounds ClusterCullingBounds = InitNodeCullingBounds(InstanceData, InstanceViewData, VisibleCluster, Cluster);
|
|
FNodeCullingBounds OrgClusterCullingBounds = ClusterCullingBounds;
|
|
|
|
// Pre-Skinned Bounds
|
|
AddOBBTWS(
|
|
ShaderPrint,
|
|
ClusterCullingBounds.BoxCenter - ClusterCullingBounds.BoxExtent,
|
|
ClusterCullingBounds.BoxCenter + ClusterCullingBounds.BoxExtent,
|
|
ClusterBoundsColor,
|
|
LocalToTranslatedWorld
|
|
);
|
|
|
|
const bool bIsSkinned = (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && Cluster.bSkinning;
|
|
|
|
FSkinningHeader SkinningHeader = (FSkinningHeader)0;
|
|
BRANCH
|
|
if (bIsSkinned)
|
|
{
|
|
SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId);
|
|
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
|
|
|
|
float3 MinBounds = float( 999999999.0f).xxx;
|
|
float3 MaxBounds = float(-999999999.0f).xxx;
|
|
|
|
for (uint VertexID = 0; VertexID < Cluster.NumVerts; ++VertexID)
|
|
{
|
|
float3 PointLocal = FetchLocalNaniteVertexPosition(InstanceData, Cluster, VisibleCluster, VertexID);
|
|
float3 PointSkinned = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex = 0;
|
|
float BoneWeight = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, VertexID, InfluenceIndex, BoneIndex, BoneWeight);
|
|
|
|
float4x3 CurrentBoneTransform = LoadSkinningBoneTransform(SkinningHeader.TransformBufferOffset + (InstanceData.SkinningData * SkinningHeader.MaxTransformCount) + BoneIndex);
|
|
PointSkinned += mul(float4(PointLocal, 1.0f), CurrentBoneTransform) * BoneWeight;
|
|
}
|
|
|
|
MinBounds = min(MinBounds, PointSkinned);
|
|
MaxBounds = max(MaxBounds, PointSkinned);
|
|
}
|
|
|
|
ClusterCullingBounds.BoxCenter = (MaxBounds + MinBounds) * 0.5f;
|
|
ClusterCullingBounds.BoxExtent = (MaxBounds - MinBounds) * 0.5f;
|
|
}
|
|
|
|
BRANCH
|
|
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SPLINE_MESH) != 0 &&
|
|
(InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0)
|
|
{
|
|
// Only show the spline mesh debug stuff when picking clusters
|
|
ShaderPrint.bIsActive = GetPickingDomain() == NANITE_PICKING_DOMAIN_CLUSTER;
|
|
|
|
const FSplineMeshShaderParams SplineMeshParams = SplineMeshLoadParamsFromInstancePayload(InstanceData);
|
|
const FSplineMeshDeformedLocalBounds NewBounds = SplineMeshDeformLocalBoundsDebug(
|
|
SplineMeshParams,
|
|
ShaderPrint,
|
|
LocalToTranslatedWorld,
|
|
ClusterCullingBounds.BoxCenter,
|
|
ClusterCullingBounds.BoxExtent
|
|
);
|
|
ClusterCullingBounds.BoxCenter = NewBounds.BoundsCenter;
|
|
ClusterCullingBounds.BoxExtent = NewBounds.BoundsExtent;
|
|
|
|
ClusterCullingBounds.Sphere = SplineMeshDeformLODSphereBounds(SplineMeshParams, ClusterCullingBounds.Sphere);
|
|
|
|
ShaderPrint.bIsActive = true;
|
|
}
|
|
ClusterCullingBounds.BoxExtent += WPOExtent + DisplacementExtent.xxx;
|
|
|
|
// Skinned Bounds
|
|
AddOBBTWS(
|
|
ShaderPrint,
|
|
ClusterCullingBounds.BoxCenter - ClusterCullingBounds.BoxExtent,
|
|
ClusterCullingBounds.BoxCenter + ClusterCullingBounds.BoxExtent,
|
|
SkinnedClusterBoundsColor,
|
|
LocalToTranslatedWorld
|
|
);
|
|
|
|
// Estimated Skinned Bounds
|
|
BRANCH
|
|
if (bIsSkinned)
|
|
{
|
|
SkinningHeader = LoadSkinningHeader(InstanceData.PrimitiveId);
|
|
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
|
|
|
|
ClusterCullingBounds = OrgClusterCullingBounds;
|
|
SkinClusterBounds(Cluster, InstanceData, SkinningHeader, ClusterCullingBounds.BoxCenter, ClusterCullingBounds.BoxExtent);
|
|
|
|
AddOBBTWS(
|
|
ShaderPrint,
|
|
ClusterCullingBounds.BoxCenter - ClusterCullingBounds.BoxExtent,
|
|
ClusterCullingBounds.BoxCenter + ClusterCullingBounds.BoxExtent,
|
|
ApproximateClusterBoundsColor,
|
|
LocalToTranslatedWorld
|
|
);
|
|
}
|
|
|
|
AddReferentialTWS(ShaderPrint, LocalToTranslatedWorld, 50.f);
|
|
|
|
if (GetPickingDomain() == NANITE_PICKING_DOMAIN_INSTANCE)
|
|
{
|
|
const float3 InstanceBoxBoundsCenter = InstanceData.LocalBoundsCenter;
|
|
const float3 InstanceBoxBoundsExtent = InstanceData.LocalBoundsExtent;
|
|
|
|
AddOBBTWS(
|
|
ShaderPrint,
|
|
InstanceBoxBoundsCenter - InstanceBoxBoundsExtent,
|
|
InstanceBoxBoundsCenter + InstanceBoxBoundsExtent,
|
|
InstanceBoundsColor,
|
|
LocalToTranslatedWorld
|
|
);
|
|
}
|
|
|
|
// Only show the cluster LOD bounds with cluster picking to reduce visual noise
|
|
if (GetPickingDomain() == NANITE_PICKING_DOMAIN_CLUSTER)
|
|
{
|
|
AddSphereTWS(
|
|
ShaderPrint,
|
|
mul(float4(ClusterCullingBounds.Sphere.xyz, 1), LocalToTranslatedWorld).xyz,
|
|
ClusterCullingBounds.Sphere.w * InstanceData.NonUniformScale.w,
|
|
ClusterLODBoundsColor,
|
|
32
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FeedbackResults.PrimitiveId = INVALID_PRIMITIVE_ID;
|
|
}
|
|
|
|
FeedbackBuffer[0] = FeedbackResults;
|
|
} |