Files
UnrealEngine/Engine/Shaders/Private/Nanite/NaniteVertexFactory.ush
2025-05-18 13:04:45 +08:00

1169 lines
49 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "../VertexFactoryCommon.ush"
#include "../LightmapData.ush"
#include "../SplineMeshCommon.ush"
#if RAYHITGROUPSHADER
#include "../RayTracing/RayTracingCommon.ush"
#endif
#include "../MaterialCache/MaterialCacheCommon.ush"
#include "NaniteDataDecode.ush"
#include "NaniteAttributeDecode.ush"
#include "NaniteSceneCommon.ush"
#include "NaniteVertexDeformation.ush"
// Some platforms may configure custom shader compiler options when compiling Nanite material shaders
#ifdef OVERRIDE_NANITE_VERTEX_FACTORY_CONFIG_USH
#include "/Platform/Private/NaniteVertexFactoryConfig.ush"
#endif
#ifndef VIRTUAL_TEXTURE_TARGET
#define VIRTUAL_TEXTURE_TARGET 0
#endif
#ifndef NANITE_USE_HW_BARYCENTRICS
#define NANITE_USE_HW_BARYCENTRICS 0
#endif
// Make sure we decode enough texture coordinates to satisfy programmable features
#if NUM_MATERIAL_TEXCOORDS_VERTEX > NUM_MATERIAL_TEXCOORDS
#define NANITE_NUM_TEXCOORDS_TO_DECODE NUM_MATERIAL_TEXCOORDS_VERTEX
#else
#define NANITE_NUM_TEXCOORDS_TO_DECODE NUM_MATERIAL_TEXCOORDS
#endif
#if NANITE_PIXEL_PROGRAMMABLE && NANITE_NUM_TEXCOORDS_TO_DECODE < 2
// Ensure we decode at least 2 texture coordinates in the MS/VS to properly populate the interpolator with valid data.
#define NANITE_NUM_TEXCOORDS_TO_DECODE_HW_VS 2
#else
#define NANITE_NUM_TEXCOORDS_TO_DECODE_HW_VS NANITE_NUM_TEXCOORDS_TO_DECODE
#endif
// Nanite material evaluation is deferred to a screenspace pass sampling the visibility buffer,
// so the 'interpolants' used in the GBuffer pass are almost all generated in the PixelShader, instead of exported from VS.
// FNaniteFullscreenVSToPS is the struct containing what actually needs to be passed between VS and PS in the Nanite GBuffer pass.
struct FVertexFactoryInterpolantsVSToPS
{
#if NEEDS_LIGHTMAP_COORDINATE
nointerpolation float4 LightMapCoordinate : TEXCOORD3;
nointerpolation float4 LightMapCoordinateDDX: TEXCOORD4;
nointerpolation float4 LightMapCoordinateDDY: TEXCOORD5;
#endif
#if VF_USE_PRIMITIVE_SCENE_DATA && NEEDS_LIGHTMAP_COORDINATE
nointerpolation uint LightmapDataIndex : LIGHTMAP_ID;
#endif
#if INSTANCED_STEREO
nointerpolation uint EyeIndex : PACKED_EYE_INDEX;
#endif
nointerpolation uint ViewIndex : PACKED_VIEW_INDEX;
nointerpolation uint2 PixelPos : PIXEL_POS;
};
void GetNaniteMaterialSceneData(FVisibleCluster VisibleCluster, inout FPrimitiveSceneData PrimitiveData, inout FInstanceSceneData InstanceData)
{
InstanceData = GetInstanceSceneDataUnchecked(VisibleCluster);
PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
#if USE_SPLINEDEFORM
BRANCH
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SPLINE_MESH) != 0 &&
(InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0)
{
// Flip the determinant sign if the spline mesh params cause the mesh to mirror
FSplineMeshShaderParams SplineMeshParams = SplineMeshLoadParamsFromInstancePayload(InstanceData);
if (SplineMeshParams.StartScale.x < 0.0f != SplineMeshParams.StartScale.y < 0.0f)
{
InstanceData.DeterminantSign = -InstanceData.DeterminantSign;
}
}
#endif
}
#if IS_NANITE_RASTER_PASS
struct FVertexFactoryInput
{
VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK()
};
struct FVertexFactoryIntermediates
{
float3x3 TangentToLocal;
};
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0;
return Intermediates;
};
float3x3 VertexFactoryGetTangentToLocal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return Intermediates.TangentToLocal;
}
FMaterialVertexParameters GetMaterialVertexParameters(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, float3x3 TangentToLocal, bool bIsPreviousFrame = false)
{
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
return Result;
}
#endif // IS_NANITE_RASTER_PASS
#if NEEDS_LIGHTMAP_COORDINATE
void GetLightMapCoordinates(FVertexFactoryInterpolantsVSToPS Interpolants, out float2 LightmapUV0, out float2 LightmapUV1, out uint LightmapDataIndex)
{
LightmapUV0 = Interpolants.LightMapCoordinate.xy * float2(1.0, 0.5);
LightmapUV1 = LightmapUV0 + float2(0.0, 0.5);
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = Interpolants.LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
}
void GetLightMapCoordinates(FVertexFactoryInterpolantsVSToPS Interpolants, out FloatDeriv2 LightmapUV0, out FloatDeriv2 LightmapUV1, out uint LightmapDataIndex)
{
LightmapUV0 = ConstructFloatDeriv2( Interpolants.LightMapCoordinate.xy * float2(1.0, 0.5),
Interpolants.LightMapCoordinateDDX.xy * float2(1.0, 0.5),
Interpolants.LightMapCoordinateDDY.xy * float2(1.0, 0.5));
LightmapUV1 = LightmapUV0;
LightmapUV1.Value += float2(0.0, 0.5);
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = Interpolants.LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
}
void GetShadowMapCoordinate(FVertexFactoryInterpolantsVSToPS Interpolants, out float2 ShadowMapCoordinate, out uint LightmapDataIndex)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = Interpolants.LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
ShadowMapCoordinate = Interpolants.LightMapCoordinate.zw;
}
void GetShadowMapCoordinate(FVertexFactoryInterpolantsVSToPS Interpolants, out FloatDeriv2 ShadowMapCoordinate, out uint LightmapDataIndex)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = Interpolants.LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
ShadowMapCoordinate = ConstructFloatDeriv2( Interpolants.LightMapCoordinate.zw,
Interpolants.LightMapCoordinateDDX.zw,
Interpolants.LightMapCoordinateDDY.zw);
}
void SetLightMapCoordinate(inout FVertexFactoryInterpolantsVSToPS Interpolants, float2 InLightMapCoordinate, float2 InShadowMapCoordinate)
{
Interpolants.LightMapCoordinate.xy = InLightMapCoordinate;
Interpolants.LightMapCoordinate.zw = InShadowMapCoordinate;
Interpolants.LightMapCoordinateDDX = 0;
Interpolants.LightMapCoordinateDDY = 0;
}
void SetLightMapCoordinate(inout FVertexFactoryInterpolantsVSToPS Interpolants, TDual< float2 > InLightMapCoordinate, TDual< float2 > InShadowMapCoordinate)
{
Interpolants.LightMapCoordinate = float4(InLightMapCoordinate.Value, InShadowMapCoordinate.Value);
Interpolants.LightMapCoordinateDDX = float4(InLightMapCoordinate.Value_dx, InShadowMapCoordinate.Value_dy);
Interpolants.LightMapCoordinateDDY = float4(InLightMapCoordinate.Value_dy, InShadowMapCoordinate.Value_dy);
}
void SetLightMapDataIndex(inout FVertexFactoryInterpolantsVSToPS Interpolants, uint LightmapDataIndex)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
Interpolants.LightmapDataIndex = LightmapDataIndex;
#endif
}
#endif // NEEDS_LIGHTMAP_COORDINATE
// Determines the tangent to local frame of the specified vertex
half3x3 CalcVertexTangentToLocal(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FNanitePostDeformVertex Vert)
{
half3x3 TangentToLocal;
BRANCH
if(Vert.TangentBasis.TangentXAndSign.w != 0.0f)
{
TangentToLocal = NaniteTangentToLocal(Vert.TangentBasis.TangentXAndSign, Vert.TangentBasis.TangentZ);
}
else
{
TangentToLocal = GetTangentBasis(Vert.TangentBasis.TangentZ);
}
#if USE_SPLINEDEFORM
BRANCH
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SPLINE_MESH) != 0 &&
(InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_PAYLOAD_EXTENSION) != 0)
{
FSplineMeshShaderParams SplineMeshParams = SplineMeshLoadParamsFromInstancePayload(InstanceData);
TangentToLocal = mul(TangentToLocal, SplineMeshCalcSliceRot(SplineMeshParams, Vert.SplineDist));
}
#endif
return TangentToLocal;
}
void SetVertexParameterInstanceData(inout FMaterialVertexParameters VertexParameters, FInstanceSceneData InstanceData, FPrimitiveSceneData PrimitiveData, bool bEvaluateWorldPositionOffset)
{
VertexParameters.PrimitiveId = InstanceData.PrimitiveId;
VertexParameters.InstanceLocalToWorld = InstanceData.LocalToWorld;
VertexParameters.InstanceWorldToLocal = InstanceData.WorldToLocal;
VertexParameters.PrevFrameLocalToWorld = InstanceData.PrevLocalToWorld;
#if USES_PER_INSTANCE_CUSTOM_DATA
VertexParameters.CustomDataOffset = InstanceData.CustomDataOffset;
VertexParameters.CustomDataCount = InstanceData.CustomDataCount;
#endif
VertexParameters.PerInstanceRandom = InstanceData.RandomID;
VertexParameters.SceneData.PrimitiveId = InstanceData.PrimitiveId;
VertexParameters.SceneData.InstanceId = InstanceData.RelativeId;
VertexParameters.SceneData.InstanceData = InstanceData;
VertexParameters.SceneData.Primitive = PrimitiveData;
VertexParameters.bEvaluateWorldPositionOffset = bEvaluateWorldPositionOffset;
}
void SetVertexParameterAttributeData(inout FMaterialVertexParameters VertexParameters, FNanitePostDeformVertex Vert, float4x4 LocalToTranslatedWorld, float3x3 LocalToWorldNoScale)
{
half3x3 TangentToLocal = CalcVertexTangentToLocal(VertexParameters.SceneData.Primitive, VertexParameters.SceneData.InstanceData, Vert);
float4x4 TranslatedWorldToPrimitive = DFFastToTranslatedWorld(VertexParameters.SceneData.Primitive.WorldToLocal, ResolvedView.PreViewTranslation);
VertexParameters.WorldPosition = mul(float4(Vert.Position, 1), LocalToTranslatedWorld).xyz;
VertexParameters.PositionInstanceSpace = Vert.Position;
VertexParameters.PositionPrimitiveSpace = mul(float4(VertexParameters.WorldPosition, 1), TranslatedWorldToPrimitive).xyz;
VertexParameters.TangentToWorld = mul(TangentToLocal, LocalToWorldNoScale);
VertexParameters.VertexColor = Vert.RawAttributeData.Color;
VertexParameters.PreSkinnedPosition = Vert.PointLocal;
VertexParameters.PreSkinnedNormal = Vert.PreSkinnedNormal;
#if NUM_MATERIAL_TEXCOORDS_VERTEX
UNROLL
for (uint TexCoordIndex = 0; TexCoordIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; ++TexCoordIndex)
{
// Protect against case where Nanite max UV count is lower than what the material may define.
VertexParameters.TexCoords[TexCoordIndex] = Vert.RawAttributeData.TexCoords[min(TexCoordIndex, NANITE_MAX_UVS - 1)];
}
#endif
VertexParameters.LWCData = MakeMaterialLWCData(VertexParameters);
}
// Group of transforms needed to transform a Nanite vertex
struct FNaniteVertTransforms
{
float4x4 LocalToTranslatedWorld;
float4x4 PrevLocalToTranslatedWorld;
float4x4 TranslatedWorldToClip;
float3x3 LocalToWorldNoScale;
float3x3 PrevLocalToWorldNoScale;
float3x3 WorldToLocalVector;
float3x3 PrevWorldToLocalVector;
};
struct FNaniteTransformedVert
{
uint VertIndex;
float3 PointLocal;
float3 PointPostDeform;
float3 PrevPointPostDeform;
float3 PointWorld;
float3 PointWorld_NoOffset;
float4 PointClip;
float4 NormalClip;
FNaniteTangentBasis TangentBasis;
FNaniteRawAttributeData RawAttributeData;
float SplineDist;
#if NUM_TEX_COORD_INTERPOLATORS
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS];
#endif
};
// Post-transformed Nanite triangle data
struct FNaniteTransformedTri
{
FNaniteTransformedVert Verts[3];
};
FNaniteVertTransforms CalculateNaniteVertexTransforms(FInstanceSceneData InstanceData, FInstanceDynamicData InstanceDynamicData, FNaniteView NaniteView)
{
const float4x4 LocalToTranslatedWorld = InstanceDynamicData.LocalToTranslatedWorld;
const float4x4 PrevLocalToTranslatedWorld = InstanceDynamicData.PrevLocalToTranslatedWorld;
const float3 InvNonUniformScale = InstanceData.InvNonUniformScale;
// Should be Pow2(InvScale) but that requires renormalization
float3x3 LocalToWorldNoScale = (float3x3)LocalToTranslatedWorld;
LocalToWorldNoScale[0] *= InvNonUniformScale.x;
LocalToWorldNoScale[1] *= InvNonUniformScale.y;
LocalToWorldNoScale[2] *= InvNonUniformScale.z;
float3x3 PrevLocalToWorldNoScale = (float3x3)PrevLocalToTranslatedWorld;
PrevLocalToWorldNoScale[0] *= InvNonUniformScale.x;
PrevLocalToWorldNoScale[1] *= InvNonUniformScale.y;
PrevLocalToWorldNoScale[2] *= InvNonUniformScale.z;
float3x3 WorldToLocalVector = DFToFloat3x3(InstanceData.WorldToLocal);
// TODO: We need PrevWorldToLocal here, but we don't have it
const float3 SqInvNonUniformScale = Pow2(InvNonUniformScale);
float3x3 PrevWorldToLocalVector = DFToFloat3x3(InstanceData.PrevLocalToWorld);
PrevWorldToLocalVector[0] *= SqInvNonUniformScale.x;
PrevWorldToLocalVector[1] *= SqInvNonUniformScale.y;
PrevWorldToLocalVector[2] *= SqInvNonUniformScale.z;
PrevWorldToLocalVector = transpose(PrevWorldToLocalVector);
FNaniteVertTransforms Transforms;
Transforms.LocalToTranslatedWorld = LocalToTranslatedWorld;
Transforms.PrevLocalToTranslatedWorld = PrevLocalToTranslatedWorld;
Transforms.TranslatedWorldToClip = NaniteView.TranslatedWorldToClip;
Transforms.LocalToWorldNoScale = LocalToWorldNoScale;
Transforms.PrevLocalToWorldNoScale = PrevLocalToWorldNoScale;
Transforms.WorldToLocalVector = WorldToLocalVector;
Transforms.PrevWorldToLocalVector = PrevWorldToLocalVector;
return Transforms;
}
HLSL_STATIC_ASSERT(sizeof(FNaniteVertTransforms) == 336, "Unexpected size of FNaniteVertTransforms. Update WaveReadLaneAt to reflect changes.");
FNaniteVertTransforms WaveReadLaneAt(FNaniteVertTransforms In, uint SrcIndex)
{
FNaniteVertTransforms Result;
Result.LocalToTranslatedWorld = WaveReadLaneAtMatrix(In.LocalToTranslatedWorld, SrcIndex);
Result.PrevLocalToTranslatedWorld = WaveReadLaneAtMatrix(In.PrevLocalToTranslatedWorld, SrcIndex);
Result.TranslatedWorldToClip = WaveReadLaneAtMatrix(In.TranslatedWorldToClip, SrcIndex);
Result.LocalToWorldNoScale = WaveReadLaneAtMatrix(In.LocalToWorldNoScale, SrcIndex);
Result.PrevLocalToWorldNoScale = WaveReadLaneAtMatrix(In.PrevLocalToWorldNoScale, SrcIndex);
Result.WorldToLocalVector = WaveReadLaneAtMatrix(In.WorldToLocalVector, SrcIndex);
Result.PrevWorldToLocalVector = WaveReadLaneAtMatrix(In.PrevWorldToLocalVector, SrcIndex);
return Result;
}
float4 GetNaniteClipPosition(in FNaniteView NaniteView, in FMaterialVertexParameters MaterialParameters, FNaniteVertTransforms Transforms, FNanitePostDeformVertex Vertex, float3 PointWorld)
{
#if MATERIAL_CACHE
#if NUM_MATERIAL_OUTPUTS_GETMATERIALCACHE > 0
float2 MaterialCacheUV = GetMaterialCache1(MaterialParameters);
#else // NUM_MATERIAL_OUTPUTS_GETMATERIALCACHE > 0
float2 MaterialCacheUV = Vertex.RawAttributeData.TexCoords[0].xy;
#endif // NUM_MATERIAL_OUTPUTS_GETMATERIALCACHE > 0
return GetMaterialCacheUnwrapClipPosition(MaterialCacheUV, NaniteView.MaterialCacheUnwrapMinAndInvSize);
#else // MATERIAL_CACHE
return mul(float4(PointWorld, 1), Transforms.TranslatedWorldToClip);
#endif // MATERIAL_CACHE
}
FNaniteTransformedTri TransformNaniteTriangle(in FNaniteView NaniteView, FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FNaniteVertTransforms Transforms, FNanitePostDeformVertex InVerts[3], bool bEvaluateWPO)
{
FNaniteTransformedTri Tri = (FNaniteTransformedTri)0;
#if USES_WORLD_POSITION_OFFSET
#if ALWAYS_EVALUATE_WORLD_POSITION_OFFSET
bEvaluateWPO = true;
#else
bEvaluateWPO &= (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_EVALUATE_WORLD_POSITION_OFFSET) != 0u;
#endif
BRANCH
if (bEvaluateWPO)
{
UNROLL_N(3)
for (int i = 0; i < 3; ++i)
{
FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, true /* WPO */);
SetVertexParameterAttributeData(VertexParameters, InVerts[i], Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale);
FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, true /* WPO */);
SetVertexParameterAttributeData(PrevVertexParameters, InVerts[i], Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale);
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(VertexParameters);
EvaluateVertexMaterialAttributes(PrevVertexParameters);
#endif
const float3 WorldPositionOffset = GetMaterialWorldPositionOffset(VertexParameters);
const float3 PrevWorldPositionOffset = GetMaterialPreviousWorldPositionOffset(PrevVertexParameters);
const float3 LocalOffset = mul(WorldPositionOffset, Transforms.WorldToLocalVector);
const float3 PrevLocalOffset = mul(PrevWorldPositionOffset, Transforms.PrevWorldToLocalVector);
const float3 NormalWorld = mul(float4(InVerts[i].TangentBasis.TangentZ, 0), Transforms.LocalToTranslatedWorld).xyz;
Tri.Verts[i].VertIndex = InVerts[i].VertIndex;
Tri.Verts[i].RawAttributeData = InVerts[i].RawAttributeData;
Tri.Verts[i].SplineDist = InVerts[i].SplineDist;
Tri.Verts[i].NormalClip = mul(float4(NormalWorld, 0), Transforms.TranslatedWorldToClip);
Tri.Verts[i].TangentBasis = InVerts[i].TangentBasis;
Tri.Verts[i].PointLocal = InVerts[i].PointLocal;
Tri.Verts[i].PointPostDeform = InVerts[i].Position + LocalOffset;
Tri.Verts[i].PrevPointPostDeform = InVerts[i].Position + PrevLocalOffset;
Tri.Verts[i].PointWorld = VertexParameters.WorldPosition + WorldPositionOffset;
Tri.Verts[i].PointWorld_NoOffset = VertexParameters.WorldPosition;
Tri.Verts[i].PointClip = GetNaniteClipPosition(NaniteView, VertexParameters, Transforms, InVerts[i], Tri.Verts[i].PointWorld);
#if NUM_TEX_COORD_INTERPOLATORS
GetMaterialCustomizedUVs(VertexParameters, Tri.Verts[i].CustomizedUVs);
GetCustomInterpolators(VertexParameters, Tri.Verts[i].CustomizedUVs);
#endif
}
}
else
#endif // USES WORLD_POSITION_OFFSET
{
UNROLL_N(3)
for (int i = 0; i < 3; ++i)
{
const float3 NormalWorld = mul(float4(InVerts[i].TangentBasis.TangentZ, 0), Transforms.LocalToTranslatedWorld).xyz;
FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, false /* WPO */);
SetVertexParameterAttributeData(VertexParameters, InVerts[i], Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale);
Tri.Verts[i].VertIndex = InVerts[i].VertIndex;
Tri.Verts[i].RawAttributeData = InVerts[i].RawAttributeData;
Tri.Verts[i].SplineDist = InVerts[i].SplineDist;
Tri.Verts[i].NormalClip = mul(float4(NormalWorld, 0), Transforms.TranslatedWorldToClip);
Tri.Verts[i].TangentBasis = InVerts[i].TangentBasis;
Tri.Verts[i].PointLocal = InVerts[i].PointLocal;
Tri.Verts[i].PointPostDeform = InVerts[i].Position;
Tri.Verts[i].PrevPointPostDeform = InVerts[i].Position;
Tri.Verts[i].PointWorld = mul(float4(Tri.Verts[i].PointPostDeform, 1), Transforms.LocalToTranslatedWorld).xyz;
Tri.Verts[i].PointWorld_NoOffset = Tri.Verts[i].PointWorld;
Tri.Verts[i].PointClip = GetNaniteClipPosition(NaniteView, VertexParameters, Transforms, InVerts[i], Tri.Verts[i].PointWorld);
#if NUM_TEX_COORD_INTERPOLATORS
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(VertexParameters);
#endif
GetMaterialCustomizedUVs(VertexParameters, Tri.Verts[i].CustomizedUVs);
GetCustomInterpolators(VertexParameters, Tri.Verts[i].CustomizedUVs);
#endif
}
}
return Tri;
}
FNaniteTransformedTri FetchTransformedNaniteTriangle(
in FNaniteView NaniteView,
FPrimitiveSceneData PrimitiveData,
FInstanceSceneData InstanceData,
FInstanceViewData InstanceViewData,
FNaniteVertTransforms Transforms,
FCluster Cluster,
FVisibleCluster VisibleCluster,
uint3 VertIndexes,
bool bEvaluateWPO)
{
FNanitePostDeformVertex Verts[3];
FetchAndDeformLocalNaniteTriangle(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, VertIndexes, NANITE_NUM_TEXCOORDS_TO_DECODE, Verts);
return TransformNaniteTriangle(NaniteView, PrimitiveData, InstanceData, Transforms, Verts, bEvaluateWPO);
}
FNaniteTransformedVert TransformNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FNaniteVertTransforms Transforms, FNanitePostDeformVertex InVert, bool bEvaluateWPO)
{
FNaniteTransformedVert Vert = (FNaniteTransformedVert)0;
Vert.VertIndex = InVert.VertIndex;
Vert.RawAttributeData = InVert.RawAttributeData;
Vert.TangentBasis = InVert.TangentBasis;
Vert.PointLocal = InVert.PointLocal;
Vert.SplineDist = InVert.SplineDist;
const float3 NormalWorld = mul(float4(InVert.TangentBasis.TangentZ, 0), Transforms.LocalToTranslatedWorld).xyz;
Vert.NormalClip = mul(float4(NormalWorld, 0), Transforms.TranslatedWorldToClip);
#if USES_WORLD_POSITION_OFFSET
#if ALWAYS_EVALUATE_WORLD_POSITION_OFFSET
bEvaluateWPO = true;
#else
bEvaluateWPO &= (PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_EVALUATE_WORLD_POSITION_OFFSET) != 0u;
#endif
BRANCH
if (bEvaluateWPO)
{
FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, true /* WPO */);
SetVertexParameterAttributeData(VertexParameters, InVert, Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale);
FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, true /* WPO */);
SetVertexParameterAttributeData(PrevVertexParameters, InVert, Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale);
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(VertexParameters);
EvaluateVertexMaterialAttributes(PrevVertexParameters);
#endif
const float3 WorldPositionOffset = GetMaterialWorldPositionOffset(VertexParameters);
const float3 PrevWorldPositionOffset = GetMaterialPreviousWorldPositionOffset(PrevVertexParameters);
const float3 LocalOffset = mul(WorldPositionOffset, Transforms.WorldToLocalVector);
const float3 PrevLocalOffset = mul(PrevWorldPositionOffset, Transforms.PrevWorldToLocalVector);
Vert.PointPostDeform = InVert.Position + LocalOffset;
Vert.PrevPointPostDeform = InVert.Position + PrevLocalOffset;
Vert.PointWorld = VertexParameters.WorldPosition + WorldPositionOffset;
Vert.PointWorld_NoOffset = VertexParameters.WorldPosition;
Vert.PointClip = mul(float4(Vert.PointWorld, 1), Transforms.TranslatedWorldToClip);
#if NUM_TEX_COORD_INTERPOLATORS
GetMaterialCustomizedUVs(VertexParameters, Vert.CustomizedUVs);
GetCustomInterpolators(VertexParameters, Vert.CustomizedUVs);
#endif
}
else
#endif // USES WORLD_POSITION_OFFSET
{
Vert.PointPostDeform = InVert.Position;
Vert.PrevPointPostDeform = InVert.Position;
Vert.PointWorld = mul(float4(Vert.PointPostDeform, 1), Transforms.LocalToTranslatedWorld).xyz;
Vert.PointWorld_NoOffset = Vert.PointWorld;
Vert.PointClip = mul(float4(Vert.PointWorld, 1), Transforms.TranslatedWorldToClip);
#if NUM_TEX_COORD_INTERPOLATORS
FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters();
SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, false /* WPO */);
SetVertexParameterAttributeData(VertexParameters, InVert, Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale);
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(VertexParameters);
#endif
GetMaterialCustomizedUVs(VertexParameters, Vert.CustomizedUVs);
GetCustomInterpolators(VertexParameters, Vert.CustomizedUVs);
#endif
}
return Vert;
}
FNaniteTransformedVert FetchTransformedNaniteVertex(
FPrimitiveSceneData PrimitiveData,
FInstanceSceneData InstanceData,
FInstanceViewData InstanceViewData,
FNaniteVertTransforms Transforms,
FCluster Cluster,
FVisibleCluster VisibleCluster,
uint VertIndex,
bool bEvaluateWPO)
{
FNanitePostDeformVertex Vert = FetchAndDeformLocalNaniteVertex(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, VertIndex, NANITE_NUM_TEXCOORDS_TO_DECODE);
return TransformNaniteVertex(PrimitiveData, InstanceData, Transforms, Vert, bEvaluateWPO);
}
HLSL_STATIC_ASSERT(sizeof(FNaniteTransformedVert) == (128 + sizeof(FNaniteRawAttributeData) + 8 * NUM_TEX_COORD_INTERPOLATORS), "Unexpected size of FNaniteTransformedVert. WaveReadLaneAt implementation needs to be updated.");
FNaniteTransformedVert WaveReadLaneAt(FNaniteTransformedVert Vert, uint SrcIndex)
{
FNaniteTransformedVert Result;
Result.VertIndex = WaveReadLaneAt( Vert.VertIndex, SrcIndex );
Result.RawAttributeData = WaveReadLaneAt( Vert.RawAttributeData, SrcIndex );
Result.PointLocal = WaveReadLaneAt( Vert.PointLocal, SrcIndex );
Result.PointPostDeform = WaveReadLaneAt( Vert.PointPostDeform, SrcIndex );
Result.PrevPointPostDeform = WaveReadLaneAt( Vert.PrevPointPostDeform, SrcIndex );
Result.PointWorld = WaveReadLaneAt( Vert.PointWorld, SrcIndex );
Result.PointWorld_NoOffset = WaveReadLaneAt( Vert.PointWorld_NoOffset, SrcIndex );
Result.PointClip = WaveReadLaneAt( Vert.PointClip, SrcIndex );
Result.SplineDist = WaveReadLaneAt( Vert.SplineDist, SrcIndex );
Result.TangentBasis = WaveReadLaneAt( Vert.TangentBasis, SrcIndex );
Result.NormalClip = WaveReadLaneAt( Vert.NormalClip, SrcIndex );
#if NUM_TEX_COORD_INTERPOLATORS
UNROLL
for (uint i = 0; i < NUM_TEX_COORD_INTERPOLATORS; ++i)
{
Result.CustomizedUVs[i] = WaveReadLaneAt(Vert.CustomizedUVs[i], SrcIndex);
}
#endif
return Result;
}
FNaniteTransformedTri MakeTransformedNaniteTriangle(FNaniteTransformedVert Vert, uint3 SrcLaneIndices)
{
FNaniteTransformedTri Tri;
UNROLL
for (uint Corner = 0; Corner < 3; ++Corner)
{
Tri.Verts[Corner] = WaveReadLaneAt(Vert, SrcLaneIndices[Corner]);
}
return Tri;
}
FMaterialPixelParameters FetchNaniteMaterialPixelParameters(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceDynamicData InstanceDynamicData, FNaniteView NaniteView, FNaniteTransformedTri Tri, FCluster Cluster, FBarycentrics Barycentrics, inout FVertexFactoryInterpolantsVSToPS Interpolants, inout float4 SvPosition)
{
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
// TODO Couldn't this be derived from the barycentric derivatives?
const float SignTest = dot(cross(Tri.Verts[1].PointClip.xyw - Tri.Verts[0].PointClip.xyw, Tri.Verts[2].PointClip.xyw - Tri.Verts[0].PointClip.xyw), Tri.Verts[0].PointClip.xyw);
Result.TwoSidedSign = CondMask(SignTest < 0.0f, -1.0f, 1.0f);
// VOXELTODO
// Hack to determine TwoSidedSign from normals for voxel. Doesn't work well with trees that bend normals for shading
if (Cluster.bVoxel)
{
const float3 WorldNormal = mul(Tri.Verts[0].RawAttributeData.TangentZ, DFToFloat3x3(InstanceData.LocalToWorld));
float Dot = dot(WorldNormal, NaniteView.ViewForward);
Result.TwoSidedSign = CondMask(Dot < 0.0f, -1.0f, 1.0f);
Result.TwoSidedSign = 1.0;
}
// Only need the first UV from cluster data (used to solve the tangent frame in GetAttributeData).
// But we don't use any for the pixel parameters because we interpolate the UVs manually below, in case they were customized.
const uint NumAttributeDataCoords = 1;
const FNaniteAttributeData AttributeData = GetAttributeData(
Cluster,
Tri.Verts[0].PointLocal,
Tri.Verts[1].PointLocal,
Tri.Verts[2].PointLocal,
Tri.Verts[0].RawAttributeData,
Tri.Verts[1].RawAttributeData,
Tri.Verts[2].RawAttributeData,
Tri.Verts[0].TangentBasis,
Tri.Verts[1].TangentBasis,
Tri.Verts[2].TangentBasis,
Barycentrics,
InstanceData,
NumAttributeDataCoords
);
#if INTERPOLATE_VERTEX_COLOR
Result.VertexColor = AttributeData.VertexColor.Value;
Result.VertexColor_DDX = AttributeData.VertexColor.Value_dx;
Result.VertexColor_DDY = AttributeData.VertexColor.Value_dy;
#else
// Coerce compiler into DCE as much code as possible.
Result.VertexColor = float4(1, 1, 1, 1);
Result.VertexColor_DDX = 0.0f;
Result.VertexColor_DDY = 0.0f;
#endif
Result.TangentToWorld = AttributeData.TangentToWorld;
Result.UnMirrored = AttributeData.UnMirrored;
#if NUM_TEX_COORD_INTERPOLATORS > 0
UNROLL
for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; TexCoordIndex++)
{
TDual< float2 > TexCoord = Lerp( Tri.Verts[0].CustomizedUVs[TexCoordIndex], Tri.Verts[1].CustomizedUVs[TexCoordIndex], Tri.Verts[2].CustomizedUVs[TexCoordIndex], Barycentrics );
Result.TexCoords[TexCoordIndex] = TexCoord.Value;
Result.TexCoords_DDX[TexCoordIndex] = TexCoord.Value_dx;
Result.TexCoords_DDY[TexCoordIndex] = TexCoord.Value_dy;
}
#endif
const TDual< float3 > PointWorld = Lerp( Tri.Verts[0].PointWorld, Tri.Verts[1].PointWorld, Tri.Verts[2].PointWorld, Barycentrics );
Result.WorldPosition_CamRelative = PointWorld.Value;
Result.WorldPosition_DDX = PointWorld.Value_dx;
Result.WorldPosition_DDY = PointWorld.Value_dy;
// Should be Pow2(InvScale) but that requires renormalization
float3x3 LocalToWorldNoScale = DFToFloat3x3(InstanceData.LocalToWorld);
float3 InvScale = InstanceData.InvNonUniformScale;
LocalToWorldNoScale[0] *= InvScale.x;
LocalToWorldNoScale[1] *= InvScale.y;
LocalToWorldNoScale[2] *= InvScale.z;
const TDual< float3 > WorldGeoNormal = Lerp(mul(Tri.Verts[0].TangentBasis.TangentZ, LocalToWorldNoScale), mul(Tri.Verts[1].TangentBasis.TangentZ, LocalToWorldNoScale), mul(Tri.Verts[2].TangentBasis.TangentZ, LocalToWorldNoScale), Barycentrics );
Result.WorldGeoNormal_DDX = WorldGeoNormal.Value_dx;
Result.WorldGeoNormal_DDY = WorldGeoNormal.Value_dy;
Result.WorldPosition_NoOffsets_CamRelative = Lerp( Tri.Verts[0].PointWorld_NoOffset, Tri.Verts[1].PointWorld_NoOffset, Tri.Verts[2].PointWorld_NoOffset, Barycentrics ).Value;
const float3 PrevPointPostDeform = Lerp( Tri.Verts[0].PrevPointPostDeform, Tri.Verts[1].PrevPointPostDeform, Tri.Verts[2].PrevPointPostDeform, Barycentrics ).Value;
float3 PrevPointWorld = mul(float4(PrevPointPostDeform.xyz, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz;
#if USES_DISPLACEMENT
{
// TODO PixelTranslatedWorld is already calculated outside this function.
// TODO GetMaterialPreviousDisplacementScaled so that dynamic displacement gets velocities.
const float4 PixelTranslatedWorld = mul( SvPosition, NaniteView.SVPositionToTranslatedWorld );
const float3 Displacement = PixelTranslatedWorld.xyz / PixelTranslatedWorld.w - PointWorld.Value;
PrevPointWorld += Displacement;
}
#endif
Result.PrevScreenPosition = mul(float4(PrevPointWorld, 1), NaniteView.PrevTranslatedWorldToClip);
if( Cluster.bVoxel )
{
float4 ThisClip = SvPositionToScreenPosition( SvPosition );
float4 PrevClip = mul( ThisClip, View.ClipToPrevClipWithAA );
Result.PrevScreenPosition = PrevClip;
const float3 V = -normalize( Result.WorldPosition_CamRelative );
#if 1
//const float Alpha = View.GeneralPurposeTweak;
//const float Alpha = tan( 0.5 * PI * min( AttributeData.VertexColor.Value.a, 0.999 ) );
float3 Alpha = 1;
if( AttributeData.VertexColor.Value.a > 0.5 )
Alpha.z = 2 - 2 * AttributeData.VertexColor.Value.a;
else
Alpha.xy = 2 * AttributeData.VertexColor.Value.a;
const float2 Noise = Rand3DPCG16( int3( SvPosition.xy, View.StateFrameIndexMod8 ) ).xy / 65535.0;
// world to sphere
float3 TangentV = mul( AttributeData.TangentToWorld, V );
TangentV *= Alpha;
TangentV = normalize( TangentV );
// visible sample on sphere
float3 VisibleNormal = CosineSampleHemisphere( Noise, TangentV ).xyz;
// sphere to world
VisibleNormal *= Alpha;
VisibleNormal = normalize( VisibleNormal );
float3 WorldNormal = mul( VisibleNormal, AttributeData.TangentToWorld );
Result.TangentToWorld = GetTangentBasis( WorldNormal );
#else
Result.TangentToWorld[2] *= dot( Result.TangentToWorld[2], V ) > 0 ? 1 : -1;
#endif
}
// Update screen W and all screen derivatives. This is rarely used and will be dead code eliminated most of the time.
{
const TDual< float4 > PointClip = Lerp( Tri.Verts[0].PointClip, Tri.Verts[1].PointClip, Tri.Verts[2].PointClip, Barycentrics );
SvPosition.w = PointClip.Value.w;
float2 Z_DDX_DDY = float2( PointClip.Value_dx.z, PointClip.Value_dy.z );
float2 W_DDX_DDY = float2( PointClip.Value_dx.w, PointClip.Value_dy.w );
// PPZ = Z / W
// PPZ' = (Z'W - ZW')/W^2
float2 PPZ_DDX_DDY = (Z_DDX_DDY * PointClip.Value.w - PointClip.Value.z * W_DDX_DDY) / (PointClip.Value.w * PointClip.Value.w);
SvPositionToResolvedScreenPositionDeriv(SvPosition, PPZ_DDX_DDY, W_DDX_DDY, Result.ScreenPosition, Result.ScreenPosition_DDX, Result.ScreenPosition_DDY);
}
#if USE_PARTICLE_SUBUVS && NUM_TEX_COORD_INTERPOLATORS > 0
// Output TexCoord0 for when previewing materials that use ParticleSubUV.
Result.Particle.SubUVCoords[0] = Result.TexCoords[0];
Result.Particle.SubUVCoords[1] = Result.TexCoords[0];
#endif
// Required for previewing materials that use ParticleColor
Result.Particle.Color = half4(1, 1, 1, 1);
Result.PerInstanceRandom = InstanceData.RandomID;
#if NEEDS_LIGHTMAP_COORDINATE
const uint LightMapDataIndex = PrimitiveData.LightmapDataIndex;
const uint LightMapUVIndex = PrimitiveData.LightmapUVIndex;
TDual< float2 > LightMapCoordinateInput;
if (LightMapUVIndex < NumAttributeDataCoords)
{
LightMapCoordinateInput = AttributeData.TexCoords[LightMapUVIndex];
}
else
{
// We don't already have the UV in the attribute data, so retrieve it
LightMapCoordinateInput = GetTexCoord(Cluster, uint3(Tri.Verts[0].VertIndex, Tri.Verts[1].VertIndex, Tri.Verts[2].VertIndex), Barycentrics, LightMapUVIndex);
}
const bool bHasPerInstanceCoordinateScaleBias = (InstanceData.Flags & INSTANCE_SCENE_DATA_FLAG_HAS_LIGHTSHADOW_UV_BIAS);
const float4 LightMapCoordinateScaleBias = GetLightmapData(LightMapDataIndex).LightMapCoordinateScaleBias;
const float2 InstanceLightMapScaleBias = CondMask(bHasPerInstanceCoordinateScaleBias, InstanceData.LightMapAndShadowMapUVBias.xy, LightMapCoordinateScaleBias.zw);
// TODO: Figure out why LightMapCoordinateInput * LightMapCoordinateScaleBias.xy + InstanceLightMapScaleBias fails to compile
TDual< float2 > LightMapCoordinate = LightMapCoordinateInput;
LightMapCoordinate.Value *= LightMapCoordinateScaleBias.xy;
LightMapCoordinate.Value_dx *= LightMapCoordinateScaleBias.xy;
LightMapCoordinate.Value_dy *= LightMapCoordinateScaleBias.xy;
LightMapCoordinate.Value += InstanceLightMapScaleBias;
TDual< float2 > ShadowMapCoordinate = (TDual< float2 >)0;
#if STATICLIGHTING_TEXTUREMASK
const float4 ShadowMapCoordinateScaleBias = GetLightmapData(LightMapDataIndex).ShadowMapCoordinateScaleBias;
const float2 InstanceShadowMapScaleBias = CondMask(bHasPerInstanceCoordinateScaleBias, InstanceData.LightMapAndShadowMapUVBias.zw, ShadowMapCoordinateScaleBias.zw);
// TODO: Figure out why LightMapCoordinateInput * ShadowMapCoordinateScaleBias.xy + InstanceShadowMapScaleBias fails to compile
ShadowMapCoordinate = LightMapCoordinateInput;
ShadowMapCoordinate.Value *= ShadowMapCoordinateScaleBias.xy;
ShadowMapCoordinate.Value_dx *= ShadowMapCoordinateScaleBias.xy;
ShadowMapCoordinate.Value_dy *= ShadowMapCoordinateScaleBias.xy;
ShadowMapCoordinate.Value += InstanceShadowMapScaleBias;
#endif
#if LIGHTMAP_UV_ACCESS
// Store unscaled/unbiased lightmap UVs
Result.LightmapUVs = LightMapCoordinateInput.Value;
Result.LightmapUVs_DDX = LightMapCoordinateInput.Value_dx;
Result.LightmapUVs_DDY = LightMapCoordinateInput.Value_dy;
#endif
SetLightMapCoordinate(Interpolants, LightMapCoordinate, ShadowMapCoordinate);
SetLightMapDataIndex(Interpolants, LightMapDataIndex);
#endif // NEEDS_LIGHTMAP_COORDINATE
#if USES_PER_INSTANCE_CUSTOM_DATA
Result.CustomDataOffset = InstanceData.CustomDataOffset;
Result.CustomDataCount = InstanceData.CustomDataCount;
#endif
#if HAS_INSTANCE_LOCAL_TO_WORLD_PS
Result.InstanceLocalToWorld = InstanceData.LocalToWorld;
#endif
#if HAS_INSTANCE_WORLD_TO_LOCAL_PS
Result.InstanceWorldToLocal = InstanceData.WorldToLocal;
#endif
Result.PrimitiveId = InstanceData.PrimitiveId;
Result.InstanceId = InstanceData.RelativeId;
return Result;
}
// Shared function (for Nanite raster and shading passes) to fetch a valid FMaterialPixelParameters struct, which is used by material inputs.
FMaterialPixelParameters FetchNaniteMaterialPixelParameters(FNaniteView NaniteView, UlongType PackedPixel, bool bHasPageData, FBarycentrics Barycentrics, bool bCalcBarycentrics, uint3 TriIndices, bool bCalcTriIndices, inout FVertexFactoryInterpolantsVSToPS Interpolants, inout float4 SvPosition)
{
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
uint DepthInt = 0;
uint VisibleClusterIndex = 0;
uint TriIndex = 0;
bool bIsImposter = false;
UnpackVisPixel(PackedPixel, DepthInt, VisibleClusterIndex, TriIndex, bIsImposter);
// Update to real depth from VisBuffer
SvPosition.z = asfloat(DepthInt);
if (VisibleClusterIndex != 0xFFFFFFFF)
{
#if VIRTUAL_TEXTURE_TARGET
FVisibleCluster VisibleCluster = GetVisibleCluster( VisibleClusterIndex, VIRTUAL_TEXTURE_TARGET );
#else
FVisibleCluster VisibleCluster = GetVisibleCluster( VisibleClusterIndex );
#endif
FPrimitiveSceneData PrimitiveData;
FInstanceSceneData InstanceData;
GetNaniteMaterialSceneData(VisibleCluster, PrimitiveData, InstanceData);
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
FCluster Cluster = GetCluster(VisibleCluster.PageIndex, VisibleCluster.ClusterIndex);
if (bCalcTriIndices)
{
TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
BRANCH
if( Cluster.bVoxel )
{
// TODO Share with USES_DISPLACEMENT
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld( InstanceData.WorldToLocal, NaniteView.PreViewTranslation );
float4 PixelTranslatedWorld = mul( SvPosition, NaniteView.SVPositionToTranslatedWorld );
float4 PixelLocal = mul( PixelTranslatedWorld, TranslatedWorldToLocal );
float3 LocalPos = PixelLocal.xyz / PixelLocal.w;
FBrick Brick = DecodeBrick(Cluster, TriIndex);
BRANCH
if (Cluster.bSkinning)
{
const FNaniteSkinningHeader SkinningHeader = LoadNaniteSkinningHeader(InstanceData.PrimitiveId);
const FBoneInfluenceHeader BoneInfluenceHeader = GetBoneInfluenceHeader(Cluster);
#if NANITE_PER_VOXEL_BRICK_SKINNING
const float4x3 SkinningTransform4x3 = SampleSkinningTransform(InstanceData, SkinningHeader, BoneInfluenceHeader, Brick.VertOffset);
#else
const float4x3 SkinningTransform4x3 = SampleVoxelSkinningTransform(InstanceData, Cluster, SkinningHeader);
#endif
const float3x3 InvSkinningTransform3x3 = Inverse(float3x3(SkinningTransform4x3[0], SkinningTransform4x3[1], SkinningTransform4x3[2]));
LocalPos = mul(LocalPos - SkinningTransform4x3[3], InvSkinningTransform3x3);
}
float3 VoxelPos = LocalPos.xyz / ( Cluster.LODError );
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;
}
}
// Don't evaluate WPO for imposter pixels or for clusters that don't have WPO enabled
bool bEvaluateWPO = !bIsImposter;
#if !ALWAYS_EVALUATE_WORLD_POSITION_OFFSET
bEvaluateWPO &= (VisibleCluster.Flags & NANITE_CULLING_FLAG_ENABLE_WPO) != 0;
#endif
const FNaniteVertTransforms Transforms = CalculateNaniteVertexTransforms(InstanceData, InstanceDynamicData, NaniteView);
const FNaniteTransformedTri Tri = FetchTransformedNaniteTriangle(NaniteView, PrimitiveData, InstanceData, GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId), Transforms, Cluster, VisibleCluster, TriIndices, bEvaluateWPO);
if (bCalcBarycentrics)
{
// Calculate perspective correct barycentric coordinates with screen derivatives
const float2 PixelClip = (SvPosition.xy - NaniteView.ViewRect.xy) * NaniteView.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
#if USES_DISPLACEMENT
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld( InstanceData.WorldToLocal, NaniteView.PreViewTranslation );
float3 CameraLocal = TranslatedWorldToLocal[3].xyz;
float4 PixelTranslatedWorld = mul( SvPosition, NaniteView.SVPositionToTranslatedWorld );
float4 PixelTranslatedWorld_dx = mul( float4(1,0,0,0), NaniteView.SVPositionToTranslatedWorld );
float4 PixelTranslatedWorld_dy = mul( float4(0,1,0,0), NaniteView.SVPositionToTranslatedWorld );
float4 PixelLocal = mul( PixelTranslatedWorld, TranslatedWorldToLocal );
float4 PixelLocal_dx = mul( PixelTranslatedWorld_dx, TranslatedWorldToLocal );
float4 PixelLocal_dy = mul( PixelTranslatedWorld_dy, TranslatedWorldToLocal );
Barycentrics = CalculateTriangleBarycentrics(
CameraLocal,
PixelLocal.xyz / PixelLocal.w,
( PixelLocal.xyz + PixelLocal_dx.xyz ) / PixelLocal.w,
( PixelLocal.xyz + PixelLocal_dy.xyz ) / PixelLocal.w,
Tri.Verts[0].PointPostDeform,
Tri.Verts[1].PointPostDeform,
Tri.Verts[2].PointPostDeform,
Tri.Verts[0].TangentBasis.TangentZ,
Tri.Verts[1].TangentBasis.TangentZ,
Tri.Verts[2].TangentBasis.TangentZ );
#else
Barycentrics = CalculateTriangleBarycentrics(PixelClip, Tri.Verts[0].PointClip, Tri.Verts[1].PointClip, Tri.Verts[2].PointClip, NaniteView.ViewSizeAndInvSize.zw);
#endif
if( Cluster.bVoxel )
{
Barycentrics.Value = float3(1,0,0);
Barycentrics.Value_dx = 0;
Barycentrics.Value_dy = 0;
}
}
Result = FetchNaniteMaterialPixelParameters(PrimitiveData, InstanceData, InstanceDynamicData, NaniteView, Tri, Cluster, Barycentrics, Interpolants, SvPosition);
#if NUM_TEX_COORD_INTERPOLATORS > 0
if( Cluster.bVoxel )
{
float Depth = NaniteView.ViewToClip[3][2] / ( SvPosition.z - NaniteView.ViewToClip[2][2] );
float4 UVDensities = GetMaterialUVDensities(Cluster, InstanceData.PrimitiveId, TriIndex);
UNROLL
for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; TexCoordIndex++)
{
// TODO Don't use NaniteView.LODScale
float dUV_dXY = rcp( GetProjectedEdgeLengthAtDepth( UVDensities[TexCoordIndex] * InstanceData.NonUniformScale.w, Depth, NaniteView ) );
Result.TexCoords_DDX[TexCoordIndex] = dUV_dXY;
Result.TexCoords_DDY[TexCoordIndex] = dUV_dXY;
}
}
#endif
}
return Result;
}
#if IS_NANITE_SHADING_PASS
#if NANITE_USE_HW_BARYCENTRICS
#error NOT_SUPPORTED
#endif
/** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */
FMaterialPixelParameters GetMaterialPixelParameters(FNaniteView NaniteView, inout FVertexFactoryInterpolantsVSToPS Interpolants, inout float4 SvPosition)
{
UlongType PackedPixel = (UlongType)0;
// Note: because of quad shading the CS shading can produce OOB pixels that need to return a safe value (0 translates into an invalid visible cluster ID which will skip most of the work)
if(all(Interpolants.PixelPos >= NaniteView.ViewRect.xy) && all(Interpolants.PixelPos < NaniteView.ViewRect.zw))
{
PackedPixel = NaniteShading.VisBuffer64[Interpolants.PixelPos];
}
const FBarycentrics Barycentrics = (FBarycentrics)0; // Unused for shading pass (barycentrics are invalid here for full screen tile grid)
return FetchNaniteMaterialPixelParameters(NaniteView, PackedPixel, VIRTUAL_TEXTURE_TARGET, Barycentrics, true, uint3(0,0,0), true, Interpolants, SvPosition);
}
FMaterialPixelParameters GetMaterialPixelParameters(inout FVertexFactoryInterpolantsVSToPS Interpolants, inout float4 SvPosition)
{
#if INSTANCED_STEREO
const FNaniteView NaniteView = GetNaniteView(Interpolants.EyeIndex);
#else
const FNaniteView NaniteView = GetNaniteView(0);
#endif
return GetMaterialPixelParameters(NaniteView, Interpolants, SvPosition);
}
#endif // IS_NANITE_SHADING_PASS
#if RAYHITGROUPSHADER
struct FVertexFactoryInput
{
// Dynamic instancing related attributes with InstanceIdOffset : ATTRIBUTE13
VF_GPUSCENE_DECLARE_INPUT_BLOCK(13)
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Material evaluation for raytracing
// define this to let shaders know this alternative method is available
#define VF_SUPPORTS_RAYTRACING_PREPARE_MATERIAL_PIXEL_PARAMETERS 1
FMaterialPixelParameters GetMaterialPixelParameters(float3 RayOrigin, float3 RayDirection, float HitT, uint HitPrimitiveIndex, FRayTracingIntersectionAttributes HitAttributes, uint HitKind, float4 SvPosition, out float3 WorldGeoNormal)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
const uint GPUSceneInstanceId = GetInstanceUserData();
const FInstanceSceneData InstanceSceneData = GetInstanceSceneData(GPUSceneInstanceId);
FVertexFactoryInput Input = (FVertexFactoryInput)0;
VF_GPUSCENE_SET_INPUT_FOR_RT(Input, GPUSceneInstanceId, InstanceSceneData.RelativeId);
#else
#error "HGS requires GPU Scene support"
#endif // VF_USE_PRIMITIVE_SCENE_DATA
FSceneDataIntermediates SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input); // NOTE: Input is not used when VF_USE_PRIMITIVE_SCENE_DATA == 0
FInstanceSceneData InstanceData = SceneData.InstanceData;
FPrimitiveSceneData PrimitiveData = SceneData.Primitive;
const uint InstanceId = SceneData.InstanceId;
const uint FirstTriangle = HitGroupSystemRootConstants.FirstPrimitive;
const uint PackedTriangleData = RayTracingDataBuffer[PrimitiveData.NaniteRayTracingDataOffset + FirstTriangle + HitPrimitiveIndex];
const uint PageIndex = PackedTriangleData & NANITE_MAX_GPU_PAGES_MASK;
const uint ClusterIndex = (PackedTriangleData >> NANITE_MAX_GPU_PAGES_BITS) & NANITE_MAX_CLUSTERS_PER_PAGE_MASK;
const uint TriIndex = (PackedTriangleData >> (NANITE_MAX_GPU_PAGES_BITS + NANITE_MAX_CLUSTERS_PER_PAGE_BITS)) & NANITE_MAX_CLUSTER_TRIANGLES_MASK;
const FNaniteView NaniteView = GetNaniteView(0);
FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData);
FCluster Cluster = GetCluster(PageIndex, ClusterIndex);
// TODO: Nanite-Assemblies: No assembly support here for RT
FVisibleCluster VisibleCluster = (FVisibleCluster)0;
VisibleCluster.AssemblyTransformIndex = 0xFFFFFFFFu;
const uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex);
const FNaniteVertTransforms Transforms = CalculateNaniteVertexTransforms(InstanceData, InstanceDynamicData, NaniteView);
const bool bEvaluateWPO = false;
const FNaniteTransformedTri Tri = FetchTransformedNaniteTriangle(NaniteView, PrimitiveData, InstanceData, GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId), Transforms, Cluster, VisibleCluster, TriIndices, bEvaluateWPO);
const float2 HitBarycentrics = HitAttributes.GetBarycentrics();
#if USE_ANALYTIC_DERIVATIVES
const float2 PixelClip = (SvPosition.xy - View.ViewRectMin.xy) * View.ViewSizeAndInvSize.zw * float2(2, -2) + float2(-1, 1);
FBarycentrics Barycentrics = CalculateTriangleBarycentrics(PixelClip, Tri.Verts[0].PointClip, Tri.Verts[1].PointClip, Tri.Verts[2].PointClip, View.ViewSizeAndInvSize.zw);
#else
FBarycentrics Barycentrics = (FBarycentrics)0;
#endif
// Replace value with barycentrics from the raytracer, the function above is to make sure derivatives are populated
Barycentrics.Value = float3(1 - HitBarycentrics.x - HitBarycentrics.y, HitBarycentrics.x, HitBarycentrics.y);
FVertexFactoryInterpolantsVSToPS Interpolants;
FMaterialPixelParameters Result = FetchNaniteMaterialPixelParameters(PrimitiveData, InstanceData, InstanceDynamicData, NaniteView, Tri, Cluster, Barycentrics, Interpolants, SvPosition);
WorldGeoNormal = Result.TangentToWorld[2];
Result.TwoSidedSign = -sign(dot(RayDirection, WorldGeoNormal));
return Result;
}
#endif // RAYHITGROUPSHADER
struct FVertexFactoryRayTracingInterpolants
{
FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS;
};
float2 VertexFactoryGetRayTracingTextureCoordinate( FVertexFactoryRayTracingInterpolants Interpolants )
{
return float2(0,0);
}
FVertexFactoryInterpolantsVSToPS VertexFactoryAssignInterpolants(FVertexFactoryRayTracingInterpolants Input)
{
return Input.InterpolantsVSToPS;
}
FVertexFactoryRayTracingInterpolants VertexFactoryInterpolate(FVertexFactoryRayTracingInterpolants a, float aInterp, FVertexFactoryRayTracingInterpolants b, float bInterp)
{
return a;
}
#if RAYHITGROUPSHADER
// Fake structs / functions required to compile RayTracingHitShaders.usf
struct FVertexFactoryIntermediates
{
half3x3 TangentToLocal;
};
FVertexFactoryInput LoadVertexFactoryInputForHGS(uint TriangleIndex, int VertexIndex)
{
FVertexFactoryInput Input = (FVertexFactoryInput)0;
return Input;
}
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0;
return Intermediates;
}
half3x3 VertexFactoryGetTangentToLocal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return Intermediates.TangentToLocal;
}
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return 0.0f;
}
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return 0.0f;
}
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return 0.0f;
}
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return 0.0f;
}
FMaterialVertexParameters GetMaterialVertexParameters(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, half3x3 TangentToLocal, bool bIsPreviousFrame = false)
{
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
return Result;
}
FVertexFactoryRayTracingInterpolants VertexFactoryGetRayTracingInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
FVertexFactoryRayTracingInterpolants Interpolants = (FVertexFactoryRayTracingInterpolants)0;
return Interpolants;
}
#endif