1030 lines
36 KiB
HLSL
1030 lines
36 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 "../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;
|
|
|
|
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;
|
|
|
|
// TODO: Try N.V to improve shading look
|
|
float3 ApplySobelFilter(uint2 PixelPosXY, uint DepthInt, float3 Color, float3 OutlineColor, bool bDarkOutline)
|
|
{
|
|
// Sobel edge detect depth
|
|
static int SobelX[] =
|
|
{
|
|
1, 0, -1,
|
|
2, 0, -2,
|
|
1, 0, -1
|
|
};
|
|
|
|
static int SobelY[] =
|
|
{
|
|
1, 2, 1,
|
|
0, 0, 0,
|
|
-1, -2, -1
|
|
};
|
|
|
|
static uint2 UVSample[] =
|
|
{
|
|
{-1, 1}, {0, 1}, {1, 1},
|
|
{-1, 0}, {0, 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;
|
|
|
|
for (uint Tap = 0; Tap < 9; ++Tap)
|
|
{
|
|
const UlongType VisPixelCurrent = VisBuffer64[PixelPosXY + UVSample[Tap]];
|
|
UnpackVisPixel(VisPixelCurrent, DepthIntCurrent, VisibleClusterIndexCurrent, TriIndexCurrent);
|
|
|
|
float SampleDensityDepth = log2( ConvertFromDeviceZ(asfloat(DepthIntCurrent)) + 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( bDarkOutline )
|
|
CombineColor = Color * ( 1.0 - DepthOutline * 0.25 );
|
|
else
|
|
CombineColor = Color + DepthOutline * 0.25 * OutlineColor;
|
|
|
|
return saturate(CombineColor);
|
|
}
|
|
|
|
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 != 0;
|
|
}
|
|
|
|
[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 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 );
|
|
|
|
FNaniteView NaniteView = GetNaniteView( 0 );
|
|
|
|
FShadingMask UnpackedMask = UnpackShadingMask(ShadingMask[PixelPos]);
|
|
|
|
float3 Result = float3(0, 0, 0);
|
|
float3 OutlineColor = 1;
|
|
float Opacity = 1.0f;
|
|
|
|
bool bDarkOutline = false;
|
|
bool bApplySobel = true;
|
|
|
|
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 PackedMaterialFlags = ShadingBinData.Load<FNaniteShadingBinMeta>(UnpackedMask.ShadingBin * NANITE_SHADING_BIN_META_BYTES).MaterialFlags;
|
|
FNaniteMaterialFlags MaterialFlags = UnpackNaniteMaterialFlags(PackedMaterialFlags);
|
|
|
|
FVisibleCluster VisibleCluster = GetVisibleCluster(VisibleClusterIndex);
|
|
FInstanceSceneData InstanceData = GetInstanceSceneDataUnchecked(VisibleCluster);
|
|
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
|
|
FCluster Cluster = GetCluster(VisibleCluster.PageIndex, VisibleCluster.ClusterIndex);
|
|
|
|
const bool bIsSkinned = (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) != 0 && Cluster.bSkinning;
|
|
|
|
FNaniteSkinningHeader SkinningHeader = (FNaniteSkinningHeader)0;
|
|
FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
|
|
BRANCH
|
|
if (bIsSkinned)
|
|
{
|
|
SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
|
|
}
|
|
|
|
uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
|
|
|
|
if( Cluster.bVoxel )
|
|
{
|
|
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld( InstanceData.WorldToLocal, NaniteView.PreViewTranslation );
|
|
|
|
float4 PixelTranslatedWorld = mul( SvPosition, NaniteView.SVPositionToTranslatedWorld );
|
|
float4 PixelLocal = mul( PixelTranslatedWorld, TranslatedWorldToLocal );
|
|
float3 VoxelPos = PixelLocal.xyz / ( PixelLocal.w * Cluster.LODError );
|
|
|
|
FBrick Brick = DecodeBrick( Cluster, TriIndex );
|
|
|
|
VoxelPos -= Brick.StartPos.xyz;
|
|
|
|
uint3 Voxel = (uint3)floor( VoxelPos );
|
|
uint VoxelIndex = Voxel.x + Voxel.y * 4 + Voxel.z * 16;
|
|
uint Mask = 1u << ( VoxelIndex & 31 );
|
|
Mask -= 1;
|
|
|
|
const uint2 BrickBits = reversebits( Brick.ReverseBrickBits.yx ); //TODO: Fix up logic to work on reversed bits directly
|
|
|
|
uint VertIndex = Brick.VertOffset;
|
|
VertIndex += countbits( BrickBits.x & ( VoxelIndex < 32 ? Mask : ~0u ) );
|
|
VertIndex += countbits( BrickBits.y & ( VoxelIndex < 32 ? 0 : Mask ) );
|
|
|
|
TriIndices = VertIndex;
|
|
}
|
|
|
|
FNaniteLocalVertex LocalVerts[3];
|
|
FetchLocalNaniteTriangle(InstanceData, Cluster, VisibleCluster, TriIndices, NANITE_MAX_UVS, LocalVerts);
|
|
|
|
FNaniteTangentBasis TangentBasis0 = MakeTangentBasis(LocalVerts[0].RawAttributeData);
|
|
FNaniteTangentBasis TangentBasis1 = MakeTangentBasis(LocalVerts[1].RawAttributeData);
|
|
FNaniteTangentBasis TangentBasis2 = MakeTangentBasis(LocalVerts[2].RawAttributeData);
|
|
|
|
BRANCH
|
|
if (bIsSkinned)
|
|
{
|
|
float3 SkinnedPosition[3];
|
|
SkinnedPosition[0] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedPosition[1] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedPosition[2] = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
float3 SkinnedNormal[3];
|
|
SkinnedNormal[0] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedNormal[1] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedNormal[2] = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
float3 SkinnedTangent[3];
|
|
SkinnedTangent[0] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedTangent[1] = float3(0.0f, 0.0f, 0.0f);
|
|
SkinnedTangent[2] = float3(0.0f, 0.0f, 0.0f);
|
|
|
|
LOOP
|
|
for (uint InfluenceIndex = 0; InfluenceIndex < BoneInfluenceHeader.NumVertexBoneInfluences; ++InfluenceIndex)
|
|
{
|
|
uint BoneIndex0 = 0;
|
|
float BoneWeight0 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.x, InfluenceIndex, BoneIndex0, BoneWeight0);
|
|
|
|
uint BoneIndex1 = 0;
|
|
float BoneWeight1 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.y, InfluenceIndex, BoneIndex1, BoneWeight1);
|
|
|
|
uint BoneIndex2 = 0;
|
|
float BoneWeight2 = 0.0f;
|
|
DecodeVertexBoneInfluence(BoneInfluenceHeader, TriIndices.z, InfluenceIndex, BoneIndex2, BoneWeight2);
|
|
|
|
float4x3 CurrentBoneTransform[3];
|
|
CurrentBoneTransform[0] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex0);
|
|
CurrentBoneTransform[1] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex1);
|
|
CurrentBoneTransform[2] = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + BoneIndex2);
|
|
|
|
SkinnedPosition[0] += mul(float4(LocalVerts[0].Position, 1.0f), CurrentBoneTransform[0]) * BoneWeight0;
|
|
SkinnedPosition[1] += mul(float4(LocalVerts[1].Position, 1.0f), CurrentBoneTransform[1]) * BoneWeight1;
|
|
SkinnedPosition[2] += mul(float4(LocalVerts[2].Position, 1.0f), CurrentBoneTransform[2]) * BoneWeight2;
|
|
|
|
SkinnedNormal[0] += mul((float3x3)CurrentBoneTransform[0], TangentBasis0.TangentZ) * BoneWeight0;
|
|
SkinnedNormal[1] += mul((float3x3)CurrentBoneTransform[1], TangentBasis1.TangentZ) * BoneWeight1;
|
|
SkinnedNormal[2] += mul((float3x3)CurrentBoneTransform[2], TangentBasis2.TangentZ) * BoneWeight2;
|
|
|
|
SkinnedTangent[0] += mul((float3x3)CurrentBoneTransform[0], TangentBasis0.TangentXAndSign.xyz) * BoneWeight0;
|
|
SkinnedTangent[1] += mul((float3x3)CurrentBoneTransform[1], TangentBasis1.TangentXAndSign.xyz) * BoneWeight1;
|
|
SkinnedTangent[2] += mul((float3x3)CurrentBoneTransform[2], TangentBasis2.TangentXAndSign.xyz) * BoneWeight2;
|
|
}
|
|
|
|
TangentBasis0.TangentZ = SkinnedNormal[0];
|
|
TangentBasis1.TangentZ = SkinnedNormal[1];
|
|
TangentBasis2.TangentZ = SkinnedNormal[2];
|
|
|
|
TangentBasis0.TangentXAndSign.xyz = SkinnedTangent[0];
|
|
TangentBasis1.TangentXAndSign.xyz = SkinnedTangent[1];
|
|
TangentBasis2.TangentXAndSign.xyz = SkinnedTangent[2];
|
|
|
|
LocalVerts[0].Position = SkinnedPosition[0];
|
|
LocalVerts[1].Position = SkinnedPosition[1];
|
|
LocalVerts[2].Position = SkinnedPosition[2];
|
|
}
|
|
|
|
const float3 PointWorld0 = mul(float4(LocalVerts[0].Position, 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
const float3 PointWorld1 = mul(float4(LocalVerts[1].Position, 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
const float3 PointWorld2 = mul(float4(LocalVerts[2].Position, 1), InstanceDynamicData.LocalToTranslatedWorld).xyz;
|
|
|
|
const float4 PointClip0 = mul(float4(PointWorld0, 1), NaniteView.TranslatedWorldToClip);
|
|
const float4 PointClip1 = mul(float4(PointWorld1, 1), NaniteView.TranslatedWorldToClip);
|
|
const float4 PointClip2 = mul(float4(PointWorld2, 1), NaniteView.TranslatedWorldToClip);
|
|
|
|
const float2 PixelClip = (PixelPos + 0.5 - View.ViewRectMin.xy) * View.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
|
|
|
|
// Calculate perspective correct barycentric coordinates with screen derivatives
|
|
FBarycentrics Barycentrics = CalculateTriangleBarycentrics(PixelClip, PointClip0, PointClip1, PointClip2, View.ViewSizeAndInvSize.zw);
|
|
if( Cluster.bVoxel )
|
|
{
|
|
Barycentrics.Value = float3(1,0,0);
|
|
Barycentrics.Value_dx = 0;
|
|
Barycentrics.Value_dy = 0;
|
|
}
|
|
|
|
const FNaniteAttributeData AttributeData = GetAttributeData(
|
|
Cluster,
|
|
LocalVerts[0].Position,
|
|
LocalVerts[1].Position,
|
|
LocalVerts[2].Position,
|
|
LocalVerts[0].RawAttributeData,
|
|
LocalVerts[1].RawAttributeData,
|
|
LocalVerts[2].RawAttributeData,
|
|
TangentBasis0,
|
|
TangentBasis1,
|
|
TangentBasis2,
|
|
Barycentrics,
|
|
InstanceData,
|
|
NANITE_MAX_UVS
|
|
);
|
|
|
|
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);
|
|
RasterBin = RemapRasterBin(RasterBin, RenderFlags, MaterialFlags, Cluster.bVoxel);
|
|
|
|
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;
|
|
bDarkOutline = true;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_PATCHES)
|
|
{
|
|
Result = IntToColor(TriIndex);
|
|
Result = Result * 0.8 + 0.2;
|
|
bDarkOutline = true;
|
|
}
|
|
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;
|
|
bDarkOutline = true;
|
|
}
|
|
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 (IsValidAssemblyTransformIndex(VisibleCluster.AssemblyTransformIndex))
|
|
{
|
|
Result = IntToColor(VisibleCluster.AssemblyTransformIndex) * 0.8 + 0.2;
|
|
}
|
|
else if (InstanceData.NaniteAssemblyTransformOffset != 0xFFFFFFFFu)
|
|
{
|
|
Result = float3(0.2f, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
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 PackedFlags = ShadingBinData.Load<FNaniteShadingBinMeta>(UnpackedMask.ShadingBin * NANITE_SHADING_BIN_META_BYTES).MaterialFlags;
|
|
const uint WriteMask = BitFieldExtractU32(PackedFlags, 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);
|
|
//}
|
|
|
|
bApplySobel = false;
|
|
}
|
|
else if (VisualizeMode == NANITE_VISUALIZE_SCENE_Z_DECODED)
|
|
{
|
|
const float ZDecoded = SceneZDecoded[PixelPos];
|
|
Result = float3(pow(ZDecoded, 0.11f), 0.0f, 0.0f);
|
|
bApplySobel = false;
|
|
}
|
|
#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(MaterialFlags.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;
|
|
const bool bEvaluateWPO = !bIsImposter && bWPOEnabled;
|
|
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 = MaterialFlags.bPixelDiscard || MaterialFlags.bPixelDepthOffset;
|
|
break;
|
|
case NANITE_PIXEL_PROG_VIS_MODE_MASKED_ONLY:
|
|
bHighlight = MaterialFlags.bPixelDiscard;
|
|
break;
|
|
case NANITE_PIXEL_PROG_VIS_MODE_PDO_ONLY:
|
|
bHighlight = MaterialFlags.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 = AttributeData.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_VSM_STATIC_CACHING)
|
|
{
|
|
// TODO: We need to figure out a way to try and better visualize shadow clipmap WPO distances
|
|
bool bAllowWPO = VirtualShadowMapIsWPOAllowed(PrimitiveData, -1);
|
|
if (ShouldCacheInstanceAsStatic(VisibleCluster.InstanceId, false, bAllowWPO, NaniteView.SceneRendererPrimaryViewId))
|
|
{
|
|
Result = float3(1, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
Result = float3(0, 0, 1);
|
|
}
|
|
}
|
|
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(), AttributeData.VertexColor.Value));
|
|
}
|
|
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 = AttributeData.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, 0, 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 = AttributeData.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)
|
|
{
|
|
bApplySobel = false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
Opacity = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (bApplySobel && ShouldApplySobelFilter() && (!GetCompositeWithSceneDepth() || UnpackedMask.bIsNanitePixel))
|
|
{
|
|
Result = ApplySobelFilter(PixelPos, DepthInt, Result, OutlineColor, bDarkOutline);
|
|
}
|
|
|
|
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];
|
|
|
|
const uint PackedMaterialFlags = ShadingBinData.Load<FNaniteShadingBinMeta>(UnpackedMask.ShadingBin * NANITE_SHADING_BIN_META_BYTES).MaterialFlags;
|
|
FNaniteMaterialFlags MaterialFlags = UnpackNaniteMaterialFlags(PackedMaterialFlags);
|
|
|
|
FVisibleCluster VisibleCluster = GetVisibleCluster(VisibleClusterIndex);
|
|
FInstanceSceneData InstanceData = GetInstanceSceneDataUnchecked(VisibleCluster);
|
|
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
|
|
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);
|
|
RasterBin = RemapRasterBin(RasterBin, RenderFlags, MaterialFlags, Cluster.bVoxel);
|
|
|
|
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 = InstanceData.NaniteAssemblyTransformOffset;
|
|
FeedbackResults.AssemblyTransformIndex = VisibleCluster.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));
|
|
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, 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;
|
|
|
|
FNaniteSkinningHeader SkinningHeader = (FNaniteSkinningHeader)0;
|
|
BRANCH
|
|
if (bIsSkinned)
|
|
{
|
|
SkinningHeader = LoadNaniteSkinningHeader(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 = LoadNaniteBoneTransform(SkinningHeader.TransformBufferOffset + InstanceData.SkinningData + 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 = LoadNaniteSkinningHeader(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;
|
|
} |