// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "../VertexFactoryCommon.ush" #include "../LightmapData.ush" #include "../SplineMeshCommon.ush" #include "../SGGX.ush" #if RAYHITGROUPSHADER #include "../RayTracing/RayTracingCommon.ush" #endif #if MATERIAL_CACHE #include "../MaterialCache/MaterialCacheCommon.ush" #endif // MATERIAL_CACHE #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, bool bUsePrevPosition) { half3x3 TangentToLocal = CalcVertexTangentToLocal(VertexParameters.SceneData.Primitive, VertexParameters.SceneData.InstanceData, Vert); float4x4 TranslatedWorldToPrimitive = DFFastToTranslatedWorld(VertexParameters.SceneData.Primitive.WorldToLocal, ResolvedView.PreViewTranslation); VertexParameters.WorldPosition = mul(float4(bUsePrevPosition ? Vert.PrevPosition : 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.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.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; float3x3 TranslatedWorldToFirstPerson; float3x3 PrevTranslatedWorldToFirstPerson; }; struct FNaniteTransformedVert { uint VertIndex; float3 PointLocal; float3 PointPostDeform; float3 PrevPointPostDeform; float3 PointWorld; float3 PointWorld_NoOffset; float4 PointClip; float4 NormalClip; float4 Color; FNaniteTangentBasis TangentBasis; float SplineDist; #if NUM_TEX_COORD_INTERPOLATORS float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS]; #endif }; 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; Transforms.TranslatedWorldToFirstPerson = NaniteView.FirstPersonTransform; Transforms.PrevTranslatedWorldToFirstPerson = NaniteView.PrevFirstPersonTransform; return Transforms; } HLSL_STATIC_ASSERT(sizeof(FNaniteVertTransforms) == 408, "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); Result.TranslatedWorldToFirstPerson = WaveReadLaneAtMatrix(In.TranslatedWorldToFirstPerson, SrcIndex); Result.PrevTranslatedWorldToFirstPerson = WaveReadLaneAtMatrix(In.PrevTranslatedWorldToFirstPerson, SrcIndex); return Result; } float4 GetNaniteClipPosition(in FNaniteView NaniteView, in FMaterialVertexParameters MaterialParameters, FPrimitiveSceneData PrimitiveData, FNaniteVertTransforms Transforms, FNanitePostDeformVertex Vertex, float3 PointWorld) { #if MATERIAL_CACHE const uint TexCoordIndex = min(NANITE_MAX_UVS - 1, GetMaterialCacheUVCoordinateIndex(PrimitiveData)); const float2 MaterialCacheUV = Vertex.TexCoords[TexCoordIndex].xy; return GetMaterialCacheUnwrapClipPosition(MaterialCacheUV, NaniteView.MaterialCacheUnwrapMinAndInvSize); #else // MATERIAL_CACHE return mul(float4(PointWorld, 1), Transforms.TranslatedWorldToClip); #endif // MATERIAL_CACHE } struct FNaniteFirstPersonOffsets { float3 WorldOffset; float3 PrevWorldOffset; float3 LocalOffset; float3 PrevLocalOffset; }; FNaniteFirstPersonOffsets GetNaniteFirstPersonOffsets(FNaniteVertTransforms Transforms, FMaterialVertexParameters VertexParameters, float3 WorldPositionOffset, FMaterialVertexParameters PrevVertexParameters, float3 PrevWorldPositionOffset) { FNaniteFirstPersonOffsets Result = (FNaniteFirstPersonOffsets)0; const float3 TranslatedWorldPosition = VertexParameters.WorldPosition + WorldPositionOffset; const float3 PrevTranslatedWorldPosition = PrevVertexParameters.WorldPosition + PrevWorldPositionOffset; const float InterpolationAlpha = GetMaterialFirstPersonInterpolationAlpha(VertexParameters); const float PrevInterpolationAlpha = GetMaterialPreviousFirstPersonInterpolationAlpha(PrevVertexParameters); Result.WorldOffset = mul(TranslatedWorldPosition, Transforms.TranslatedWorldToFirstPerson) - TranslatedWorldPosition; Result.PrevWorldOffset = mul(PrevTranslatedWorldPosition, Transforms.PrevTranslatedWorldToFirstPerson) - PrevTranslatedWorldPosition; Result.WorldOffset *= InterpolationAlpha; Result.PrevWorldOffset *= PrevInterpolationAlpha; Result.LocalOffset = mul(Result.WorldOffset, Transforms.WorldToLocalVector); Result.PrevLocalOffset = mul(Result.PrevWorldOffset, Transforms.PrevWorldToLocalVector); return Result; } template void TransformNaniteVerts(in FNaniteView NaniteView, FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FNaniteVertTransforms Transforms, bool bEvaluateWPO, FNanitePostDeformVertex InVerts[N], inout FNaniteTransformedVert OutVerts[N]) { const bool bIsFirstPerson = IsFirstPerson_FromFlags(PrimitiveData.Flags); #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 for (int i = 0; i < N; ++i) { FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, true /* WPO */); SetVertexParameterAttributeData(VertexParameters, InVerts[i], Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale, false /*bUsePrevPosition*/); FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, true /* WPO */); SetVertexParameterAttributeData(PrevVertexParameters, InVerts[i], Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale, true /*bUsePrevPosition*/); #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; OutVerts[i].VertIndex = InVerts[i].VertIndex; OutVerts[i].Color = InVerts[i].Color; OutVerts[i].SplineDist = InVerts[i].SplineDist; OutVerts[i].NormalClip = mul(float4(NormalWorld, 0), Transforms.TranslatedWorldToClip); OutVerts[i].TangentBasis = InVerts[i].TangentBasis; OutVerts[i].PointLocal = InVerts[i].PointLocal; OutVerts[i].PointPostDeform = InVerts[i].Position + LocalOffset; OutVerts[i].PrevPointPostDeform = InVerts[i].PrevPosition + PrevLocalOffset; OutVerts[i].PointWorld = VertexParameters.WorldPosition + WorldPositionOffset; OutVerts[i].PointWorld_NoOffset = VertexParameters.WorldPosition; #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (bIsFirstPerson) { const FNaniteFirstPersonOffsets FPOffsets = GetNaniteFirstPersonOffsets(Transforms, VertexParameters, WorldPositionOffset, PrevVertexParameters, PrevWorldPositionOffset); OutVerts[i].PointPostDeform += FPOffsets.LocalOffset; OutVerts[i].PrevPointPostDeform += FPOffsets.PrevLocalOffset; OutVerts[i].PointWorld += FPOffsets.WorldOffset; } #endif // SUPPORT_FIRST_PERSON_RENDERING OutVerts[i].PointClip = GetNaniteClipPosition(NaniteView, VertexParameters, PrimitiveData, Transforms, InVerts[i], OutVerts[i].PointWorld); #if NUM_TEX_COORD_INTERPOLATORS GetMaterialCustomizedUVs(VertexParameters, OutVerts[i].CustomizedUVs); GetCustomInterpolators(VertexParameters, OutVerts[i].CustomizedUVs); #endif } } else #endif // USES WORLD_POSITION_OFFSET { UNROLL for (int i = 0; i < N; ++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, false /*bUsePrevPosition*/); #if ENABLE_NEW_HLSL_GENERATOR && ((NANITE_VERTEX_PROGRAMMABLE && SUPPORT_FIRST_PERSON_RENDERING) || NUM_TEX_COORD_INTERPOLATORS) EvaluateVertexMaterialAttributes(VertexParameters); #endif OutVerts[i].VertIndex = InVerts[i].VertIndex; OutVerts[i].Color = InVerts[i].Color; OutVerts[i].SplineDist = InVerts[i].SplineDist; OutVerts[i].NormalClip = mul(float4(NormalWorld, 0), Transforms.TranslatedWorldToClip); OutVerts[i].TangentBasis = InVerts[i].TangentBasis; OutVerts[i].PointLocal = InVerts[i].PointLocal; OutVerts[i].PointPostDeform = InVerts[i].Position; OutVerts[i].PrevPointPostDeform = InVerts[i].PrevPosition; OutVerts[i].PointWorld = VertexParameters.WorldPosition; OutVerts[i].PointWorld_NoOffset = VertexParameters.WorldPosition; #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (bIsFirstPerson) { FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, false /* WPO */); SetVertexParameterAttributeData(PrevVertexParameters, InVerts[i], Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale, true /*bUsePrevPosition*/); #if ENABLE_NEW_HLSL_GENERATOR && NANITE_VERTEX_PROGRAMMABLE EvaluateVertexMaterialAttributes(PrevVertexParameters); #endif const FNaniteFirstPersonOffsets FPOffsets = GetNaniteFirstPersonOffsets(Transforms, VertexParameters, /*WorldPositionOffset*/ 0.0f, PrevVertexParameters, /*PrevWorldPositionOffset*/ 0.0f); OutVerts[i].PointPostDeform += FPOffsets.LocalOffset; OutVerts[i].PrevPointPostDeform += FPOffsets.PrevLocalOffset; OutVerts[i].PointWorld += FPOffsets.WorldOffset; } #endif // SUPPORT_FIRST_PERSON_RENDERING OutVerts[i].PointClip = GetNaniteClipPosition(NaniteView, VertexParameters, PrimitiveData, Transforms, InVerts[i], OutVerts[i].PointWorld); #if NUM_TEX_COORD_INTERPOLATORS GetMaterialCustomizedUVs(VertexParameters, OutVerts[i].CustomizedUVs); GetCustomInterpolators(VertexParameters, OutVerts[i].CustomizedUVs); #endif } } } template void FetchTransformedNaniteVerts( FNaniteView NaniteView, FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceViewData InstanceViewData, FNaniteVertTransforms Transforms, FCluster Cluster, FVisibleCluster VisibleCluster, uint3 VertIndexes, FReconstructedVoxelData VoxelData, bool bEvaluateWPO, inout FNaniteTransformedVert OutVerts[N] ) { FNaniteLocalVertex LocalVerts[N]; FetchLocalNaniteVerts(PrimitiveData, InstanceData, InstanceViewData, Cluster, VisibleCluster, VertIndexes, NANITE_NUM_TEXCOORDS_TO_DECODE, LocalVerts); FNanitePostDeformVertex DeformedVerts[N]; BRANCH if (Cluster.bVoxel) { UNROLL for(uint i = 0; i < N; i++) { DeformedVerts[i] = DeformLocalNaniteVoxelVertex(VoxelData, LocalVerts[i]); } } else { UNROLL for(uint i = 0; i < N; i++) { DeformedVerts[i] = DeformLocalNaniteVertex(PrimitiveData, InstanceData, InstanceViewData, Cluster, LocalVerts[i]); } } TransformNaniteVerts(NaniteView, PrimitiveData, InstanceData, Transforms, bEvaluateWPO, DeformedVerts, OutVerts); } FNaniteTransformedVert TransformNaniteVertex(FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FNaniteVertTransforms Transforms, FNanitePostDeformVertex InVert, bool bEvaluateWPO) { FNaniteTransformedVert Vert = (FNaniteTransformedVert)0; Vert.VertIndex = InVert.VertIndex; Vert.Color = InVert.Color; 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); const bool bIsFirstPerson = IsFirstPerson_FromFlags(PrimitiveData.Flags); #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, false /*bUsePrevPosition*/); FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, true /* WPO */); SetVertexParameterAttributeData(PrevVertexParameters, InVert, Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale, true /*bUsePrevPosition*/); #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.PrevPosition + PrevLocalOffset; Vert.PointWorld = VertexParameters.WorldPosition + WorldPositionOffset; Vert.PointWorld_NoOffset = VertexParameters.WorldPosition; #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (bIsFirstPerson) { const FNaniteFirstPersonOffsets FPOffsets = GetNaniteFirstPersonOffsets(Transforms, VertexParameters, WorldPositionOffset, PrevVertexParameters, PrevWorldPositionOffset); Vert.PointPostDeform += FPOffsets.LocalOffset; Vert.PrevPointPostDeform += FPOffsets.PrevLocalOffset; Vert.PointWorld += FPOffsets.WorldOffset; } #endif // SUPPORT_FIRST_PERSON_RENDERING 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 { FMaterialVertexParameters VertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(VertexParameters, InstanceData, PrimitiveData, false /* WPO */); SetVertexParameterAttributeData(VertexParameters, InVert, Transforms.LocalToTranslatedWorld, Transforms.LocalToWorldNoScale, false /*bUsePrevPosition*/); #if ENABLE_NEW_HLSL_GENERATOR && ((NANITE_VERTEX_PROGRAMMABLE && SUPPORT_FIRST_PERSON_RENDERING) || NUM_TEX_COORD_INTERPOLATORS) EvaluateVertexMaterialAttributes(VertexParameters); #endif Vert.PointPostDeform = InVert.Position; Vert.PrevPointPostDeform = InVert.PrevPosition; Vert.PointWorld = VertexParameters.WorldPosition; Vert.PointWorld_NoOffset = VertexParameters.WorldPosition; #if SUPPORT_FIRST_PERSON_RENDERING BRANCH if (bIsFirstPerson) { FMaterialVertexParameters PrevVertexParameters = MakeInitializedMaterialVertexParameters(); SetVertexParameterInstanceData(PrevVertexParameters, InstanceData, PrimitiveData, false /* WPO */); SetVertexParameterAttributeData(PrevVertexParameters, InVert, Transforms.PrevLocalToTranslatedWorld, Transforms.PrevLocalToWorldNoScale, true /*bUsePrevPosition*/); #if ENABLE_NEW_HLSL_GENERATOR && NANITE_VERTEX_PROGRAMMABLE EvaluateVertexMaterialAttributes(PrevVertexParameters); #endif const FNaniteFirstPersonOffsets FPOffsets = GetNaniteFirstPersonOffsets(Transforms, VertexParameters, /*WorldPositionOffset*/ 0.0f, PrevVertexParameters, /*PrevWorldPositionOffset*/ 0.0f); Vert.PointPostDeform += FPOffsets.LocalOffset; Vert.PrevPointPostDeform += FPOffsets.PrevLocalOffset; Vert.PointWorld += FPOffsets.WorldOffset; } #endif // SUPPORT_FIRST_PERSON_RENDERING Vert.PointClip = mul(float4(Vert.PointWorld, 1), Transforms.TranslatedWorldToClip); #if NUM_TEX_COORD_INTERPOLATORS 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) == (144 + 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.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 ); Result.Color = WaveReadLaneAt( Vert.Color, 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; } struct FNanitePixelAttributes { TDual< float3 > PointWorld; TDual< float3 > PointWorld_NoOffset; TDual< float3 > PrevPointPostDeform; TDual< float4 > PointClip; TDual< half3 > TangentZ; /** Interpolated vertex color, in linear color space. */ TDual< float4 > Color; #if NUM_TEX_COORD_INTERPOLATORS TDual< float2 > TexCoords[NUM_TEX_COORD_INTERPOLATORS]; #endif /** Orthonormal rotation-only transform from tangent space to world space. */ half3x3 TangentToWorld; half UnMirrored; }; FNanitePixelAttributes GetPixelAttributes( FInstanceSceneData InstanceData, FCluster Cluster, FNaniteVertTransforms Transforms, FNaniteTransformedVert Verts[1], FBarycentrics Barycentrics ) { FNanitePixelAttributes PixelAttributes = (FNanitePixelAttributes)0; // Decode vertex color PixelAttributes.Color.Value = Verts[0].Color; #if NUM_TEX_COORD_INTERPOLATORS > 0 UNROLL for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; ++TexCoordIndex) { PixelAttributes.TexCoords[TexCoordIndex].Value = Verts[0].CustomizedUVs[TexCoordIndex]; } #endif PixelAttributes.PointWorld.Value = Verts[0].PointWorld; PixelAttributes.PointWorld_NoOffset.Value = Verts[0].PointWorld_NoOffset; PixelAttributes.PrevPointPostDeform.Value = Verts[0].PrevPointPostDeform; PixelAttributes.PointClip.Value = Verts[0].PointClip; PixelAttributes.TangentZ.Value = Verts[0].TangentBasis.TangentZ; const float4 TangentXAndSign = Verts[0].TangentBasis.TangentXAndSign; float3x3 TangentToLocal = NaniteTangentToLocal( TangentXAndSign, PixelAttributes.TangentZ.Value ); PixelAttributes.UnMirrored = TangentXAndSign.w; PixelAttributes.TangentToWorld = mul( TangentToLocal, Transforms.LocalToWorldNoScale ); return PixelAttributes; } FNanitePixelAttributes GetPixelAttributes( FInstanceSceneData InstanceData, FCluster Cluster, FNaniteVertTransforms Transforms, FNaniteTransformedVert Verts[3], FBarycentrics Barycentrics ) { FNanitePixelAttributes PixelAttributes = (FNanitePixelAttributes)0; // Decode vertex color if (Cluster.ColorMode == NANITE_VERTEX_COLOR_MODE_VARIABLE) { PixelAttributes.Color = Lerp( Verts[0].Color, Verts[1].Color, Verts[2].Color, Barycentrics ); } else { PixelAttributes.Color.Value = Verts[0].Color; } #if NUM_TEX_COORD_INTERPOLATORS > 0 UNROLL for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; ++TexCoordIndex) { PixelAttributes.TexCoords[TexCoordIndex] = Lerp( Verts[0].CustomizedUVs[TexCoordIndex], Verts[1].CustomizedUVs[TexCoordIndex], Verts[2].CustomizedUVs[TexCoordIndex], Barycentrics ); } #endif PixelAttributes.PointWorld = Lerp( Verts[0].PointWorld, Verts[1].PointWorld, Verts[2].PointWorld, Barycentrics ); PixelAttributes.PointWorld_NoOffset = Lerp( Verts[0].PointWorld_NoOffset, Verts[1].PointWorld_NoOffset, Verts[2].PointWorld_NoOffset, Barycentrics ); PixelAttributes.PrevPointPostDeform = Lerp( Verts[0].PrevPointPostDeform, Verts[1].PrevPointPostDeform, Verts[2].PrevPointPostDeform, Barycentrics ); PixelAttributes.PointClip = Lerp( Verts[0].PointClip, Verts[1].PointClip, Verts[2].PointClip, Barycentrics ); PixelAttributes.TangentZ = Lerp( Verts[0].TangentBasis.TangentZ, Verts[1].TangentBasis.TangentZ, Verts[2].TangentBasis.TangentZ, Barycentrics ); const float4 TangentXAndSign = Lerp( Verts[0].TangentBasis.TangentXAndSign, Verts[1].TangentBasis.TangentXAndSign, Verts[2].TangentBasis.TangentXAndSign, Barycentrics ).Value; float3x3 TangentToLocal = NaniteTangentToLocal( TangentXAndSign, PixelAttributes.TangentZ.Value ); PixelAttributes.UnMirrored = TangentXAndSign.w; PixelAttributes.TangentToWorld = mul( TangentToLocal, Transforms.LocalToWorldNoScale ); return PixelAttributes; } float GetNaniteVoxelTwoSidedSign(FInstanceSceneData InstanceData, FNaniteView NaniteView, FNaniteTransformedVert Vert) { // VOXELTODO // Hack to determine TwoSidedSign from normals for voxel. Doesn't work well with trees that bend normals for shading const float3 WorldNormal = mul(Vert.TangentBasis.TangentZ, DFToFloat3x3(InstanceData.LocalToWorld)); float Dot = dot(WorldNormal, NaniteView.ViewForward); //return Dot < 0.0 ? -1.0f : 1.0f; return 1.0f; } float GetNaniteTwoSidedSign(FInstanceSceneData InstanceData, FNaniteView NaniteView, FCluster Cluster, FBarycentrics Barycentrics, FNaniteTransformedVert Verts[1]) { // Assume voxel return GetNaniteVoxelTwoSidedSign(InstanceData, NaniteView, Verts[0]); } float GetNaniteTwoSidedSign(FInstanceSceneData InstanceData, FNaniteView NaniteView, FCluster Cluster, FBarycentrics Barycentrics, FNaniteTransformedVert Verts[3]) { if (Cluster.bVoxel) { return GetNaniteVoxelTwoSidedSign(InstanceData, NaniteView, Verts[0]); } else { // TODO: FIXME: using barycentric derivs here causes issues with tessellation #if USES_DISPLACEMENT const float SignTest = dot(cross(Verts[1].PointClip.xyw - Verts[0].PointClip.xyw, Verts[2].PointClip.xyw - Verts[0].PointClip.xyw), Verts[0].PointClip.xyw); #else const float SignTest = Barycentrics.Value_dx.y * Barycentrics.Value_dy.x - Barycentrics.Value_dx.x * Barycentrics.Value_dy.y; #endif return SignTest < 0.0f ? -1.0f : 1.0f; } } template FMaterialPixelParameters FetchNaniteMaterialPixelParameters( FPrimitiveSceneData PrimitiveData, FInstanceSceneData InstanceData, FInstanceDynamicData InstanceDynamicData, FNaniteVertTransforms Transforms, FNaniteView NaniteView, FNaniteTransformedVert Verts[N], FCluster Cluster, FBarycentrics Barycentrics, inout FVertexFactoryInterpolantsVSToPS Interpolants, inout float4 SvPosition) { FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters(); Result.TwoSidedSign = GetNaniteTwoSidedSign(InstanceData, NaniteView, Cluster, Barycentrics, Verts); FNanitePixelAttributes PixelAttributes = GetPixelAttributes(InstanceData, Cluster, Transforms, Verts, Barycentrics); #if INTERPOLATE_VERTEX_COLOR Result.VertexColor = PixelAttributes.Color.Value; Result.VertexColor_DDX = PixelAttributes.Color.Value_dx; Result.VertexColor_DDY = PixelAttributes.Color.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 = PixelAttributes.TangentToWorld; Result.UnMirrored = PixelAttributes.UnMirrored; #if NUM_TEX_COORD_INTERPOLATORS > 0 UNROLL for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; TexCoordIndex++) { TDual< float2 > TexCoord = PixelAttributes.TexCoords[TexCoordIndex]; Result.TexCoords[TexCoordIndex] = TexCoord.Value; Result.TexCoords_DDX[TexCoordIndex] = TexCoord.Value_dx; Result.TexCoords_DDY[TexCoordIndex] = TexCoord.Value_dy; } #endif TDual PointWorld = PixelAttributes.PointWorld; Result.WorldPosition_CamRelative = PointWorld.Value; Result.WorldPosition_DDX = PointWorld.Value_dx; Result.WorldPosition_DDY = PointWorld.Value_dy; #if !MATERIAL_CACHE // VOXELTODO Fix PointWorld properly. // TODO PixelTranslatedWorld is already calculated outside this function. const float4 PixelTranslatedWorld = mul( float4( SvPosition.xyz, 1.0f ), NaniteView.SVPositionToTranslatedWorld ); Result.WorldPosition_CamRelative = PixelTranslatedWorld.xyz / PixelTranslatedWorld.w; #endif // MATERIAL_CACHE Result.WorldGeoNormal_DDX = mul(PixelAttributes.TangentZ.Value_dx, Transforms.LocalToWorldNoScale); Result.WorldGeoNormal_DDY = mul(PixelAttributes.TangentZ.Value_dy, Transforms.LocalToWorldNoScale); Result.WorldPosition_NoOffsets_CamRelative = PixelAttributes.PointWorld_NoOffset.Value; float3 PrevPointPostDeform = PixelAttributes.PrevPointPostDeform.Value; float3 PrevPointWorld = mul(float4(PrevPointPostDeform.xyz, 1), InstanceDynamicData.PrevLocalToTranslatedWorld).xyz; #if USES_DISPLACEMENT && !MATERIAL_CACHE { // TODO GetMaterialPreviousDisplacementScaled so that dynamic displacement gets velocities. const float3 Displacement = PixelTranslatedWorld.xyz / PixelTranslatedWorld.w - PointWorld.Value; PrevPointWorld += Displacement; } #endif Result.PrevScreenPosition = mul(float4(PrevPointWorld, 1), NaniteView.PrevTranslatedWorldToClip); Result.NormalDistribution = PixelAttributes.TangentToWorld[2]; if( Cluster.bVoxel ) { const float3 V = -normalize( Result.WorldPosition_CamRelative ); if( NANITE_USE_ANALYTIC_SGGX ) { half NoV = dot( Result.NormalDistribution, V ); Result.NormalDistribution *= ( NoV >= 0 ? 1 : -1 ) * ( 1 - 2 * PixelAttributes.Color.Value.a ); } float3 Alpha = 1; if( PixelAttributes.Color.Value.a > 0.5 ) Alpha.z = 2 - 2 * PixelAttributes.Color.Value.a; else Alpha.xy = 2 * PixelAttributes.Color.Value.a; const float2 Noise = Rand3DPCG16( int3( SvPosition.xy, View.StateFrameIndex ) ).xy / 65535.0; float3 TangentV = mul( PixelAttributes.TangentToWorld, V ); float3 TangentN = SGGX_DvisSample( Noise, Alpha, TangentV ).xyz; float3 WorldNormal = mul( TangentN, PixelAttributes.TangentToWorld ); Result.TangentToWorld = GetTangentBasis( WorldNormal ); } // Update screen W and all screen derivatives. This is rarely used and will be dead code eliminated most of the time. { TDual< float4 > PointClip = PixelAttributes.PointClip; 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 NUM_TEX_COORD_INTERPOLATORS > 0 if (LightMapUVIndex < NUM_TEX_COORD_INTERPOLATORS) { LightMapCoordinateInput = PixelAttributes.TexCoords[LightMapUVIndex]; } else #endif { // We don't already have the UV in the attribute data, so retrieve it LightMapCoordinateInput = GetTexCoord(Cluster, uint3(Verts[0].VertIndex, Verts[1].VertIndex, 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; } FBarycentrics CalculateBarycentrics( FNaniteView NaniteView, FInstanceSceneData InstanceData, FCluster Cluster, FNaniteTransformedVert Verts[1], float4 SvPosition) { FBarycentrics Barycentrics; Barycentrics.Value = float3(1, 0, 0); Barycentrics.Value_dx = 0.0f; Barycentrics.Value_dy = 0.0f; return Barycentrics; } FBarycentrics CalculateBarycentrics( FNaniteView NaniteView, FInstanceSceneData InstanceData, FCluster Cluster, FNaniteTransformedVert Verts[3], float4 SvPosition) { FBarycentrics Barycentrics; if (!Cluster.bVoxel) { // 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 && !MATERIAL_CACHE float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(InstanceData.WorldToLocal, NaniteView.PreViewTranslation); float3 CameraLocal = TranslatedWorldToLocal[3].xyz; float4 PixelTranslatedWorld = mul(float4(SvPosition.xyz, 1.0f), 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, Verts[0].PointPostDeform, Verts[1].PointPostDeform, Verts[2].PointPostDeform, Verts[0].TangentBasis.TangentZ, Verts[1].TangentBasis.TangentZ, Verts[2].TangentBasis.TangentZ); #else Barycentrics = CalculateTriangleBarycentrics(PixelClip, Verts[0].PointClip, Verts[1].PointClip, Verts[2].PointClip, NaniteView.ViewSizeAndInvSize.zw); #endif return Barycentrics; } else { Barycentrics.Value = float3(1,0,0); Barycentrics.Value_dx = 0.0f; Barycentrics.Value_dy = 0.0f; return Barycentrics; } } template FMaterialPixelParameters FetchNaniteMaterialPixelParameters_Inner( FNaniteView NaniteView, FCluster Cluster, FVisibleCluster VisibleCluster, FBarycentrics Barycentrics, uint TriIndex, bool bCalcBarycentrics, uint3 TriIndices, bool bCalcTriIndices, bool bIsImposter, inout FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition) { FPrimitiveSceneData PrimitiveData; FInstanceSceneData InstanceData; GetNaniteMaterialSceneData(VisibleCluster, PrimitiveData, InstanceData); const FInstanceViewData InstanceViewData = GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId); const FInstanceDynamicData InstanceDynamicData = CalculateInstanceDynamicData(NaniteView, InstanceData); FMaterialPixelParameters Result; FReconstructedVoxelData VoxelData = (FReconstructedVoxelData)0; BRANCH if( Cluster.bVoxel ) { VoxelData = ReconstructVoxel(NaniteView, PrimitiveData, InstanceData, InstanceViewData, VisibleCluster, Cluster, SvPosition, TriIndex); TriIndices = VoxelData.VertIndex; } else if( bCalcTriIndices ) { TriIndices = DecodeTriangleIndices(Cluster, TriIndex); } // 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); FNaniteTransformedVert Verts[N]; FetchTransformedNaniteVerts(NaniteView, PrimitiveData, InstanceData, InstanceViewData, Transforms, Cluster, VisibleCluster, TriIndices, VoxelData, bEvaluateWPO, Verts); if (bCalcBarycentrics) { Barycentrics = CalculateBarycentrics(NaniteView, InstanceData, Cluster, Verts, SvPosition); } Result = FetchNaniteMaterialPixelParameters(PrimitiveData, InstanceData, InstanceDynamicData, Transforms, NaniteView, Verts, Cluster, Barycentrics, Interpolants, SvPosition); if( Cluster.bVoxel ) { float Depth = NaniteView.ViewToClip[3][2] / ( SvPosition.z - NaniteView.ViewToClip[2][2] ); // TODO: Should we explicitly take voxel size / sampling rate into account ? const bool bOrtho = NaniteView.ViewToClip[3][3] >= 1; const float ViewToPixels = NaniteView.ViewToClip[1][1] * NaniteView.ViewSizeAndInvSize.y * 0.5f; const float dViewPos_dXY = ( bOrtho ? 1.0f : Depth ) / ViewToPixels; // DDX and DDY should be orthogonal to behave nicely with anisotropic filtering. // We have no direction information, so for now just use some orthogonal basis that is unlikely to be aligned with any projection the material shader might do. Result.WorldPosition_DDX = float3(0.766585, -0.240119, -0.595559) * dViewPos_dXY; Result.WorldPosition_DDY = float3(0.230514, -0.762736, 0.604233) * dViewPos_dXY; #if NUM_TEX_COORD_INTERPOLATORS > 0 const float4 UVDensities = GetMaterialUVDensities(Cluster, InstanceData.PrimitiveId, TriIndex); UNROLL for (uint TexCoordIndex = 0; TexCoordIndex < NUM_TEX_COORD_INTERPOLATORS; TexCoordIndex++) { const float UVDensity = UVDensities[TexCoordIndex] != 0.0f ? UVDensities[TexCoordIndex] : 20.0f; //VOXELTODO: UVDensity can be 0, because of broken fallback meshes. Just assume some semi-reasonable number until that gets fixed. float dUV_dXY = dViewPos_dXY / ( UVDensity * InstanceData.NonUniformScale.w ) * ( 1.0f / sqrt( 2.0f ) ); dUV_dXY = min( dUV_dXY, 0.125f ); // VOXELTODO: Clamp to avoid some VT Aniso artifacts as a temporary workaround Result.TexCoords_DDX[TexCoordIndex] = float2( dUV_dXY, dUV_dXY ); Result.TexCoords_DDY[TexCoordIndex] = float2(-dUV_dXY, dUV_dXY ); } #endif } 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 != 0xFFFFFFFFu) { #if VIRTUAL_TEXTURE_TARGET FVisibleCluster VisibleCluster = GetVisibleCluster( VisibleClusterIndex, VIRTUAL_TEXTURE_TARGET ); #else FVisibleCluster VisibleCluster = GetVisibleCluster( VisibleClusterIndex ); #endif FCluster Cluster = GetCluster( VisibleCluster.PageIndex, VisibleCluster.ClusterIndex ); #if NANITE_SHADING_PASS_FORCE_VOXELS Cluster.bVoxel = true; Result = FetchNaniteMaterialPixelParameters_Inner<1>(NaniteView, Cluster, VisibleCluster, Barycentrics, TriIndex, bCalcBarycentrics, TriIndices, bCalcTriIndices, bIsImposter, Interpolants, SvPosition); #elif NANITE_SHADING_PASS_FORCE_TRIANGLES Cluster.bVoxel = false; Result = FetchNaniteMaterialPixelParameters_Inner<3>(NaniteView, Cluster, VisibleCluster, Barycentrics, TriIndex, bCalcBarycentrics, TriIndices, bCalcTriIndices, bIsImposter, Interpolants, SvPosition); #else // Voxels and Triangles in same permutation. Used by Lumen. Result = FetchNaniteMaterialPixelParameters_Inner<3>(NaniteView, Cluster, VisibleCluster, Barycentrics, TriIndex, bCalcBarycentrics, TriIndices, bCalcTriIndices, bIsImposter, Interpolants, SvPosition); #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; #if NANITE_ASSEMBLY_DATA const uint2 PackedNaniteData = RayTracingDataBuffer.Load2(8u * (PrimitiveData.NaniteRayTracingDataOffset + FirstTriangle + HitPrimitiveIndex)); const uint PackedTriangleData = PackedNaniteData.x; const uint AssemblyTransformIndex = PackedNaniteData.y; #else const uint PackedTriangleData = RayTracingDataBuffer.Load(4u * (PrimitiveData.NaniteRayTracingDataOffset + FirstTriangle + HitPrimitiveIndex)); const uint AssemblyTransformIndex = NANITE_MAX_ASSEMBLY_TRANSFORMS; #endif 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 = AssemblyTransformIndex; const uint3 TriIndices = DecodeTriangleIndices(Cluster, TriIndex); const FNaniteVertTransforms Transforms = CalculateNaniteVertexTransforms(InstanceData, InstanceDynamicData, NaniteView); const bool bEvaluateWPO = false; // VOXELTODO: No voxel support for RT FNaniteTransformedVert Verts[3]; FetchTransformedNaniteVerts<3>(NaniteView, PrimitiveData, InstanceData, GetInstanceViewData(InstanceData.InstanceId, NaniteView.SceneRendererPrimaryViewId), Transforms, Cluster, VisibleCluster, TriIndices, (FReconstructedVoxelData)0, bEvaluateWPO, Verts); 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, Verts[0].PointClip, Verts[1].PointClip, 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, Transforms, NaniteView, Verts, Cluster, Barycentrics, Interpolants, SvPosition); WorldGeoNormal = normalize(cross(Verts[1].PointWorld - Verts[0].PointWorld, Verts[2].PointWorld - Verts[0].PointWorld)); Result.TwoSidedSign = -sign(dot(RayDirection, WorldGeoNormal)); Result.TangentToWorld[2] *= Result.TwoSidedSign; 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