// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LightmapGBuffer.usf
=============================================================================*/
#include "/Engine/Private/Common.ush"
#include "/Engine/Generated/Material.ush"
#include "/Engine/Generated/VertexFactory.ush"
#include "/Engine/Private/PathTracing/Utilities/PathTracingRandomSequence.ush"
float4 VirtualTexturePhysicalTileCoordinateScaleAndBias;
int2 ScratchTilePoolOffset;
int RenderPassIndex;
int bMaterialTwoSided;
struct FLightmapGBufferVSToPS
{
FVertexFactoryInterpolantsVSToPS FactoryInterpolants;
float4 Position : SV_POSITION;
float4 SavedWorldPosition : POSITION1;
// Support WPO in the far future
// float4 SavedWorldPositionWithShaderOffsets : POSITION2;
};
#if VERTEXSHADER
void LightmapGBufferVS(
FVertexFactoryInput Input,
out FLightmapGBufferVSToPS Output
)
{
ResolvedView = ResolveView();
FVertexFactoryIntermediates VFIntermediates = GetVertexFactoryIntermediates(Input);
float3 WorldPosition = VertexFactoryGetWorldPosition(Input, VFIntermediates).xyz;
float3x3 TangentToLocal = VertexFactoryGetTangentToLocal(Input, VFIntermediates);
FMaterialVertexParameters VertexParameters = GetMaterialVertexParameters(Input, VFIntermediates, WorldPosition, TangentToLocal);
Output.FactoryInterpolants = VertexFactoryGetInterpolantsVSToPS(Input, VFIntermediates, VertexParameters);
float2 LightmapUV, UnusedLightmapUV1;
uint UnusedLightmapDataIndex;
GetLightMapCoordinates(Output.FactoryInterpolants, LightmapUV, UnusedLightmapUV1, UnusedLightmapDataIndex);
// Unscale with float2(1, 2)
LightmapUV *= float2(1, 2);
// Transform to tile [-1, 1] uv space
float2 LightmapUVYInverted = (LightmapUV * VirtualTexturePhysicalTileCoordinateScaleAndBias.xy + VirtualTexturePhysicalTileCoordinateScaleAndBias.zw) * 2.0f - 1.0f;
LightmapUVYInverted.y = -LightmapUVYInverted.y;
uint SampleIndex = 0;
float2 RandSample = float2(
Halton(RenderPassIndex, 2),
Halton(RenderPassIndex, 3)
);
const float FilterWidth = 2.0f;
float2 RandOffset = FilterWidth * (- 1.0f + 2 * RandSample) / GPreviewLightmapPhysicalTileSize;
Output.Position = float4(LightmapUVYInverted + RandOffset, 0.0f, 1.0f);
Output.SavedWorldPosition = VertexFactoryGetWorldPosition(Input, VFIntermediates);
// Support WPO in the far future
// Output.SavedWorldPositionWithShaderOffsets = Output.SavedWorldPosition + float4(GetMaterialWorldPositionOffset(VertexParameters), 0.0f);
}
#endif
#define WorldPositionScalar 0.00001f
void LightmapGBufferPS(
FLightmapGBufferVSToPS Input
OPTIONAL_IsFrontFace
)
{
ResolvedView = ResolveView();
FMaterialPixelParameters MaterialParameters = GetMaterialPixelParameters(Input.FactoryInterpolants, Input.SavedWorldPosition);
FPixelMaterialInputs PixelMaterialInputs;
// Support WPO in the far future
// CalcMaterialParametersEx(MaterialParameters, PixelMaterialInputs, float4(0, 0, 0, 0), float4(0, 0, 0, 0), bIsFrontFace, Input.SavedWorldPositionWithShaderOffsets, Input.SavedWorldPosition);
CalcMaterialParametersEx(MaterialParameters, PixelMaterialInputs, float4(0, 0, 0, 0), float4(0, 0, 0, 0), bIsFrontFace, Input.SavedWorldPosition, Input.SavedWorldPosition);
float4 OutWorldPosition = float4(LWCHackToFloat(MaterialParameters.AbsoluteWorldPosition) * WorldPositionScalar, 1.0f);
// Faceted triangle normal
float4 OutWorldNormal = float4(normalize(cross(ddx(OutWorldPosition.xyz), ddy(OutWorldPosition.xyz))), 1.0f);
OutWorldNormal *= CondMask(bIsFrontFace, 1.0f, -1.0f);
OutWorldNormal *= GetPrimitive_DeterminantSign(MaterialParameters.PrimitiveId);
// Interpolated vertex normal
float4 OutShadingNormal = float4(MaterialParameters.TangentToWorld[2], bMaterialTwoSided);
// GBuffer Outputs
LightmapGBufferParams.ScratchTilePoolLayer0[ScratchTilePoolOffset + Input.Position.xy] = OutWorldPosition;
LightmapGBufferParams.ScratchTilePoolLayer1[ScratchTilePoolOffset + Input.Position.xy] = OutWorldNormal;
LightmapGBufferParams.ScratchTilePoolLayer2[ScratchTilePoolOffset + Input.Position.xy] = OutShadingNormal;
}
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0;
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(Intermediates);
#if VF_USE_PRIMITIVE_SCENE_DATA
// Calculate the local (within the range of instance IDs belonging to a given primitive), as this can be used to load custom stuff
Intermediates.PrimitiveLocalInstanceIndex = Intermediates.SceneData.InstanceId - PrimitiveData.InstanceSceneDataOffset;
#if USE_INSTANCE_CULLING
Intermediates.InstanceLightmapAndShadowMapUVBias = Intermediates.SceneData.InstanceData.LightMapAndShadowMapUVBias;
#if USE_EDITOR_SHADERS
Intermediates.IsSelected = Intermediates.SceneData.InstanceData.EditorData.bIsSelected ? 1.0f : 0.0f;
Intermediates.HitProxyId = float4(Intermediates.SceneData.InstanceData.EditorData.HitProxyId, 0.0f);
#else // !USE_EDITOR_SHADERS
Intermediates.IsSelected = 0.0f;
Intermediates.HitProxyId = float4(0.0f, 0.0f, 0.0f, 0.0f);
#endif // USE_EDITOR_SHADERS
{
// This repackaging is stupid-seeming but these params are propably packed off to some interpolator or something
// PerInstanceParams.x: per-instance fade out amount
float3 InstanceTranslatedLocation = TransformLocalToTranslatedWorld(GetInstanceData(Intermediates).LocalBoundsCenter, GetInstanceData(Intermediates).LocalToWorld).xyz;
Intermediates.PerInstanceParams.x = 1.0 - saturate((length(InstanceTranslatedLocation) - InstancingFadeOutParams.x) * InstancingFadeOutParams.y);
// InstancingFadeOutParams.z,w are RenderSelected and RenderDeselected respectively.
// PerInstanceParams.y = hide / show flag,
Intermediates.IsVisible = lerp(InstancingFadeOutParams.w, InstancingFadeOutParams.z, Intermediates.IsSelected);
Intermediates.PerInstanceParams.y = Intermediates.IsVisible;
// PerInstanceParams.z dither fade cutoff
#if USE_DITHERED_LOD_TRANSITION
float RandomLOD = InstancingViewZCompareZero.w * Intermediates.SceneData.InstanceData.RandomID;
float ViewZZero = length(InstanceTranslatedLocation - InstancingTranslatedWorldViewOriginZero.xyz) + RandomLOD;
float ViewZOne = length(InstanceTranslatedLocation - InstancingTranslatedWorldViewOriginOne.xyz) + RandomLOD;
Intermediates.PerInstanceParams.z =
dot(float3(ViewZZero.xxx > InstancingViewZCompareZero.xyz), InstancingViewZConstant.xyz) * InstancingTranslatedWorldViewOriginZero.w +
dot(float3(ViewZOne.xxx > InstancingViewZCompareOne.xyz), InstancingViewZConstant.xyz) * InstancingTranslatedWorldViewOriginOne.w;
Intermediates.PerInstanceParams.y *= abs(Intermediates.PerInstanceParams.z) < .999;
Intermediates.IsVisible = Intermediates.PerInstanceParams.y;
#else
Intermediates.PerInstanceParams.z = 0;
#endif
}
#endif // USE_INSTANCE_CULLING
#endif // VF_USE_PRIMITIVE_SCENE_DATA
#if MANUAL_VERTEX_FETCH
Intermediates.Color = LocalVF.VertexFetch_ColorComponentsBuffer[(LocalVF.VertexFetch_Parameters[VF_VertexOffset] + Input.VertexId) & LocalVF.VertexFetch_Parameters[VF_ColorIndexMask_Index]] FMANUALFETCH_COLOR_COMPONENT_SWIZZLE; // Swizzle vertex color.
#else
Intermediates.Color = Input.Color FCOLOR_COMPONENT_SWIZZLE; // Swizzle vertex color.
#endif
#if USE_INSTANCING && MANUAL_VERTEX_FETCH
uint InstanceId = GetInstanceId(Input.InstanceId);
Intermediates.InstanceTransform1 = InstanceVF.VertexFetch_InstanceTransformBuffer[3 * (InstanceId + InstanceOffset) + 0];
Intermediates.InstanceTransform2 = InstanceVF.VertexFetch_InstanceTransformBuffer[3 * (InstanceId + InstanceOffset) + 1];
Intermediates.InstanceTransform3 = InstanceVF.VertexFetch_InstanceTransformBuffer[3 * (InstanceId + InstanceOffset) + 2];
Intermediates.InstanceOrigin = InstanceVF.VertexFetch_InstanceOriginBuffer[(InstanceId + InstanceOffset)];
Intermediates.InstanceLightmapAndShadowMapUVBias = InstanceVF.VertexFetch_InstanceLightmapBuffer[(InstanceId + InstanceOffset)];
#elif USE_INSTANCING
Intermediates.InstanceTransform1 = Input.InstanceTransform1;
Intermediates.InstanceTransform2 = Input.InstanceTransform2;
Intermediates.InstanceTransform3 = Input.InstanceTransform3;
Intermediates.InstanceOrigin = Input.InstanceOrigin;
Intermediates.InstanceLightmapAndShadowMapUVBias = Input.InstanceLightmapAndShadowMapUVBias;
#endif
float TangentSign = 1.0;
Intermediates.TangentToLocal = CalcTangentToLocal(Input, TangentSign);
Intermediates.TangentToWorld = CalcTangentToWorld(Intermediates, Intermediates.TangentToLocal);
Intermediates.TangentToWorldSign = TangentSign * GetInstanceData(Intermediates).DeterminantSign;
#if USE_INSTANCING
// x = per-instance fade out factor, y = zero or one depending of if it is shown at all, z is dither cutoff
// PerInstanceParams.y stores a hide/show flag for this instance
float SelectedValue = GetInstanceSelected(Intermediates);
// GPUCULL_TODO: This can't be right
float3 InstanceTranslatedLocation = TransformLocalToTranslatedWorld(GetInstanceOrigin(Intermediates), PrimitiveData.LocalToWorld).xyz;
Intermediates.PerInstanceParams.x = 1.0 - saturate((length(InstanceTranslatedLocation) - InstancingFadeOutParams.x) * InstancingFadeOutParams.y);
// InstancingFadeOutParams.z,w are RenderSelected and RenderDeselected respectively.
Intermediates.PerInstanceParams.y = InstancingFadeOutParams.z * SelectedValue + InstancingFadeOutParams.w * (1-SelectedValue);
#if USE_DITHERED_LOD_TRANSITION
float RandomLOD = InstancingViewZCompareZero.w * Intermediates.SceneData.InstanceData.RandomID;
float ViewZZero = length(InstanceTranslatedLocation - InstancingTranslatedWorldViewOriginZero.xyz) + RandomLOD;
float ViewZOne = length(InstanceTranslatedLocation - InstancingTranslatedWorldViewOriginOne.xyz) + RandomLOD;
Intermediates.PerInstanceParams.z =
dot(float3(ViewZZero.xxx > InstancingViewZCompareZero.xyz), InstancingViewZConstant.xyz) * InstancingTranslatedWorldViewOriginZero.w +
dot(float3(ViewZOne.xxx > InstancingViewZCompareOne.xyz), InstancingViewZConstant.xyz) * InstancingTranslatedWorldViewOriginOne.w;
Intermediates.PerInstanceParams.y *= abs(Intermediates.PerInstanceParams.z) < .999;
#else
Intermediates.PerInstanceParams.z = 0;
#endif
#if USES_PER_INSTANCE_CUSTOM_DATA
// index into instance CustomData
Intermediates.PerInstanceParams.w = asfloat(GetInstanceId(Input.InstanceId) + InstanceOffset);
#endif
#endif // USE_INSTANCING
if (IsGPUSkinPassThrough())
{
#if SUPPORT_GPUSKIN_PASSTHROUGH
#if MANUAL_VERTEX_FETCH
uint PreSkinVertexOffset = LocalVF.PreSkinBaseVertexIndex + Input.VertexId * 3;
Intermediates.PreSkinPosition.x = LocalVF.VertexFetch_PreSkinPositionBuffer[PreSkinVertexOffset + 0];
Intermediates.PreSkinPosition.y = LocalVF.VertexFetch_PreSkinPositionBuffer[PreSkinVertexOffset + 1];
Intermediates.PreSkinPosition.z = LocalVF.VertexFetch_PreSkinPositionBuffer[PreSkinVertexOffset + 2];
#else
Intermediates.PreSkinPosition = Input.PreSkinPosition.xyz;
#endif
#endif
}
else
{
Intermediates.PreSkinPosition = Input.Position.xyz;
}
return Intermediates;
}
#define VF_GPUSCENE_GET_INTERMEDIATES(VFInput) \
GetSceneDataIntermediates( \
VFInput.InstanceIdOffset, \
VFInput.DrawInstanceId)
FSceneDataIntermediates GetSceneDataIntermediates(uint InstanceIdOffset, uint DrawInstanceId)
{
FSceneDataIntermediates Intermediates = (FSceneDataIntermediates)0;
Intermediates.InstanceIdLoadIndex = InstanceIdOffset + DrawInstanceId;
// GPUCULL_TODO: workaround for the fact that DrawDynamicMeshPassPrivate et al. don't work with GPU-Scene instancing
// instead they mark the top bit in the primitive ID and disable auto instancing such that there is an 1:1:1
// drawcmd:primitive:instance. Then we can just look up the primitive and fetch the instance data index.
// GPUCULL_TODO: Workaround also used for ray tracing interfacing with the VFs, that also supply a DrawInstanceId.
// We mark the PrimitiveID with the top bit in dynamic draw passes
if ((InstanceIdOffset & VF_TREAT_INSTANCE_ID_OFFSET_AS_PRIMITIVE_ID_FLAG) != 0U)
{
// mask off the flag
uint PrimitiveID = InstanceIdOffset & (VF_TREAT_INSTANCE_ID_OFFSET_AS_PRIMITIVE_ID_FLAG - 1U);
Intermediates.InstanceId = GetPrimitiveData(PrimitiveID).InstanceSceneDataOffset + DrawInstanceId;
Intermediates.ViewIndex = 0;
}
// Workaround for Vulkan SPIRV issue when "else" branch is not removed when it should, which leads to higher level code expecting InstanceCulling buffer to be bound.
// See: https://github.com/KhronosGroup/SPIRV-Tools/issues/4902
#if USE_INSTANCE_CULLING_DATA
else
{
Intermediates.InstanceId = InstanceCulling.InstanceIdsBuffer[InstanceIdOffset + DrawInstanceId] & ((1U << 28U) - 1);
// We store the view index (which can be used for instanced stereo or other multi-view in the top four bits of the instance ID)
// Note: this is an index of views for this render pass, not the view ID in the culling manager.
Intermediates.ViewIndex = InstanceCulling.InstanceIdsBuffer[InstanceIdOffset + DrawInstanceId] >> 28U;
}
#endif
Intermediates.InstanceData = GetInstanceSceneData(Intermediates.InstanceId, View.InstanceSceneDataSOAStride);
Intermediates.PrimitiveId = Intermediates.InstanceData.PrimitiveId;
Intermediates.Primitive = GetPrimitiveData(Intermediates.PrimitiveId);
return Intermediates;
}
// Fetch from scene primitive buffer
FInstanceSceneData GetInstanceSceneData(uint InstanceId, uint SOAStride, bool bCheckValid = true)
{
FInstanceSceneData InstanceData = (FInstanceSceneData)0;
//
// NOTE: When changing the packed data layout, ensure that GPUScene/GPUSceneWriter.ush is kept in sync!
// Also, please update the GetInstanceSceneData function in GPUScene.cpp for validation purposes.
//
// Only process valid instances
LoadInstancePrimitiveIdAndFlags(InstanceId, SOAStride, InstanceData.PrimitiveId, InstanceData.Flags);
InstanceData.ValidInstance = InstanceData.PrimitiveId != INVALID_PRIMITIVE_ID;
// Payload Data Layout
// NOTE: Per-instance local bounds and hierarchy offset are always mutually inclusive, so pack together.
// Random ID <packed inline>
// Custom Data Count <packed inline>
// HierarchyOffset float0.x
// LocalBounds Center float0.yzw
// LocalBounds Extent float1.xyz
// __UNUSED float1.w
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
// Previous Transform[0] float2.xyzw
// Previous Transform[1] float3.xyzw
// LM/SM Scale Bias float4.xyzw
// Custom Data Float4s float5.xyzw ... floatN.xyzw
#else
// Previous Transform[0] float2.xyzw
// Previous Transform[1] float3.xyzw
// Previous Transform[2] float4.xyzw
// LM/SM Scale Bias float5.xyzw
// Custom Data Float4s float6.xyzw ... floatN.xyzw
#endif
BRANCH
if (!bCheckValid || InstanceData.ValidInstance)
{
uint CustomDataCount;
LoadInstanceRelativeIdAndCustomDataCount(InstanceId, SOAStride, InstanceData.RelativeId, CustomDataCount);
FInstancePayloadDataOffsets Offsets = GetInstancePayloadDataOffsets(InstanceData.PrimitiveId, InstanceData.Flags, InstanceData.RelativeId);
#if ENABLE_PER_INSTANCE_CUSTOM_DATA
InstanceData.CustomDataCount = CustomDataCount;
InstanceData.CustomDataOffset = Offsets.CustomData;
#endif
InstanceData.LastUpdateSceneFrameNumber = asuint(LoadInstanceSceneDataElement(0 * SOAStride + InstanceId).z);
#if USES_PER_INSTANCE_RANDOM || USE_DITHERED_LOD_TRANSITION
InstanceData.RandomID = LoadInstanceSceneDataElement(0 * SOAStride + InstanceId).w;
#endif
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceData.PrimitiveId);
float3 TilePosition = PrimitiveData.TilePosition;
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
uint4 RotationScale = asuint(LoadInstanceSceneDataElement(1 * SOAStride + InstanceId));
float3 Translation = LoadInstanceSceneDataElement(2 * SOAStride + InstanceId).xyz;
float3 Scale = 0;
float4x4 LocalToRelativeWorld = DecodeTransform( RotationScale, Translation, Scale );
uint4 PrevRotationScale = asuint(LoadInstanceSceneDataElement(3 * SOAStride + InstanceId));
float3 PrevTranslation = LoadInstanceSceneDataElement(4 * SOAStride + InstanceId).xyz;
float3 PrevScale = 0;
float4x4 PrevLocalToRelativeWorld = DecodeTransform( PrevRotationScale, PrevTranslation, PrevScale );
InstanceData.NonUniformScale.xyz = abs(Scale);
InstanceData.InvNonUniformScale = rcp(InstanceData.NonUniformScale.xyz);
#else
float4x4 LocalToRelativeWorld = transpose(float4x4(LoadInstanceSceneDataElement(1 * SOAStride + InstanceId),
LoadInstanceSceneDataElement(2 * SOAStride + InstanceId),
LoadInstanceSceneDataElement(3 * SOAStride + InstanceId),
float4(0.0f, 0.0f, 0.0f, 1.0f)));
float4x4 PrevLocalToRelativeWorld = transpose(float4x4(LoadInstanceSceneDataElement(4 * SOAStride + InstanceId),
LoadInstanceSceneDataElement(5 * SOAStride + InstanceId),
LoadInstanceSceneDataElement(6 * SOAStride + InstanceId),
float4(0.0f, 0.0f, 0.0f, 1.0f)));
#endif
InstanceData.LocalToWorld = MakeLWCMatrix(TilePosition, LocalToRelativeWorld);
InstanceData.PrevLocalToWorld = MakeLWCMatrix(TilePosition, PrevLocalToRelativeWorld);
ComputeInstanceDerivedData(InstanceData, TilePosition, LocalToRelativeWorld);
InstanceData.NaniteRuntimeResourceID = PrimitiveData.NaniteResourceID;
InstanceData.NaniteHierarchyOffset = PrimitiveData.NaniteHierarchyOffset;
BRANCH
if (Offsets.HierarchyOffset != INVALID_INSTANCE_PAYLOAD_OFFSET)
{
const uint HierarchyRootOffset = asuint(LoadInstancePayloadDataElement(Offsets.HierarchyOffset)).x;
// Combine this instance's hierarchy offset with the primitive's root hierarchy offset
InstanceData.NaniteHierarchyOffset += HierarchyRootOffset;
}
#if USE_EDITOR_SHADERS
BRANCH
if (Offsets.EditorData != INVALID_INSTANCE_PAYLOAD_OFFSET)
{
const uint PackedEditorData = asuint(LoadInstancePayloadDataElement(Offsets.EditorData)).y;
InstanceData.EditorData.bIsSelected = (PackedEditorData >> 24u) != 0;
InstanceData.EditorData.HitProxyPacked = PackedEditorData & 0x00FFFFFFu;
InstanceData.EditorData.HitProxyId = UnpackHitProxyId(InstanceData.EditorData.HitProxyPacked);
}
#endif
BRANCH
if (Offsets.LocalBounds != INVALID_INSTANCE_PAYLOAD_OFFSET)
{
InstanceData.LocalBoundsCenter = float3(LoadInstancePayloadDataElement(Offsets.LocalBounds + 0).zw, LoadInstancePayloadDataElement(Offsets.LocalBounds + 1).x);
InstanceData.LocalBoundsExtent = LoadInstancePayloadDataElement(Offsets.LocalBounds + 1).yzw;
}
else
{
InstanceData.LocalBoundsCenter = PrimitiveData.InstanceLocalBoundsCenter;
InstanceData.LocalBoundsExtent = PrimitiveData.InstanceLocalBoundsExtent;
}
BRANCH
if (Offsets.DynamicData != INVALID_INSTANCE_PAYLOAD_OFFSET)
{
#if PLATFORM_ALLOW_SCENE_DATA_COMPRESSED_TRANSFORMS
uint4 PrevRotationScale = asuint(LoadInstancePayloadDataElement(Offsets.DynamicData + 0));
float3 PrevTranslation = LoadInstancePayloadDataElement(Offsets.DynamicData + 1).xyz;
float3 PrevScale = 0;
float4x4 PrevLocalToRelativeWorld = DecodeTransform(PrevRotationScale, PrevTranslation, PrevScale);
#else
float4x4 PrevLocalToRelativeWorld = transpose(float4x4(LoadInstancePayloadDataElement(Offsets.DynamicData + 0),
LoadInstancePayloadDataElement(Offsets.DynamicData + 1),
LoadInstancePayloadDataElement(Offsets.DynamicData + 2),
float4(0.0f, 0.0f, 0.0f, 1.0f)));
#endif
float3 TilePosition = PrimitiveData.TilePosition;
InstanceData.PrevLocalToWorld = MakeLWCMatrix(TilePosition, PrevLocalToRelativeWorld);
}
#if 1 //NEEDS_LIGHTMAP_COORDINATE
BRANCH
if (Offsets.LightShadowUVBias != INVALID_INSTANCE_PAYLOAD_OFFSET)
{
InstanceData.LightMapAndShadowMapUVBias = LoadInstancePayloadDataElement(Offsets.LightShadowUVBias);
}
#endif
}
return InstanceData;
}
FLWCMatrix MakeLWCMatrix(float3 Tile, float4x4 InMatrix)
{
FLWCMatrix Result;
LWCSetTile(Result, Tile);
Result.M = InMatrix;
return Result;
}
// @return translated world position
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
FLWCMatrix LocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
#if USE_INSTANCING
return CalcWorldPosition(Input.Position, GetInstanceTransform(Intermediates), LocalToWorld) * Intermediates.PerInstanceParams.y;
#elif USE_INSTANCE_CULLING
// Scale to zero if not visible, seems a bit wild but whatever
return CalcWorldPosition(Input.Position, LocalToWorld) * Intermediates.IsVisible;
#else
return CalcWorldPosition(Input.Position, LocalToWorld);
#endif // USE_INSTANCING
}
#if USE_INSTANCING
float4 CalcWorldPosition(float4 Position, float4x4 InstanceTransform, FLWCMatrix LocalToWorld)
#else
float4 CalcWorldPosition(float4 Position, FLWCMatrix LocalToWorld)
#endif // USE_INSTANCING
{
#if USE_INSTANCING
return TransformLocalToTranslatedWorld(mul(Position, InstanceTransform).xyz, LocalToWorld);
#elif USE_SPLINEDEFORM
/*
// Make transform for this point along spline
float4x3 SliceTransform = CalcSliceTransform(dot(Position.xyz, SplineMeshDir));
// Transform into mesh space
float4 LocalPos = float4(mul(Position, SliceTransform), Position.w);
// Transform from mesh to world space
return TransformLocalToTranslatedWorld(LocalPos.xyz);
*/
return TransformLocalToTranslatedWorld(float3(mul(Position, CalcSliceTransform(dot(Position.xyz, SplineMeshDir))).xyz), LocalToWorld);
#else
return TransformLocalToTranslatedWorld(Position.xyz, LocalToWorld);
#endif
}
FInstanceSceneData GetInstanceData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.InstanceData;
}
/** Converts from vertex factory specific input to a FMaterialVertexParameters, which is used by vertex shader material inputs. */
FMaterialVertexParameters GetMaterialVertexParameters(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, half3x3 TangentToLocal)
{
FMaterialVertexParameters Result = (FMaterialVertexParameters)0;
Result.SceneData = Intermediates.SceneData;
// FIXME: just for compatibility with assets that use custom HLSL expressions, will be removed once we fix up all these assets
Result.PrimitiveId = Intermediates.SceneData.PrimitiveId;
Result.WorldPosition = WorldPosition;
Result.VertexColor = Intermediates.Color;
// does not handle instancing!
Result.TangentToWorld = Intermediates.TangentToWorld;
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(Intermediates);
#if USE_INSTANCING
float4x4 InstanceToLocal = GetInstanceTransform(Intermediates);
Result.InstanceLocalToWorld = LWCMultiply(InstanceToLocal, PrimitiveData.LocalToWorld);
Result.InstanceLocalPosition = Input.Position.xyz;
Result.PerInstanceParams = Intermediates.PerInstanceParams;
Result.InstanceId = GetInstanceId(Input.InstanceId);
Result.InstanceOffset = InstanceOffset;
Result.PrevFrameLocalToWorld = LWCMultiply(GetInstancePrevTransform(Intermediates), PrimitiveData.PreviousLocalToWorld);
// Calculate derived world to local
{
float3 Scale2;
Scale2.x = length2(InstanceToLocal[0].xyz);
Scale2.y = length2(InstanceToLocal[1].xyz);
Scale2.z = length2(InstanceToLocal[2].xyz);
float3 InvNonUniformScale = rsqrt(Scale2);
float4x4 LocalToInstance = InstanceToLocal;
LocalToInstance[0].xyz *= Pow2(InvNonUniformScale.x);
LocalToInstance[1].xyz *= Pow2(InvNonUniformScale.y);
LocalToInstance[2].xyz *= Pow2(InvNonUniformScale.z);
LocalToInstance[3].xyz = 0.0f;
LocalToInstance = transpose(LocalToInstance);
LocalToInstance[3].xyz = mul(float4(-InstanceToLocal[3].xyz, 0.0f), LocalToInstance).xyz;
Result.InstanceWorldToLocal = LWCMultiply(PrimitiveData.WorldToLocal, LocalToInstance);
}
#else
Result.PrevFrameLocalToWorld = GetInstanceData(Intermediates).PrevLocalToWorld;
#if USE_INSTANCE_CULLING
Result.InstanceLocalPosition = Input.Position.xyz;
Result.InstanceLocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
Result.InstanceWorldToLocal = GetInstanceData(Intermediates).WorldToLocal;
Result.PerInstanceParams = Intermediates.PerInstanceParams;
#endif
#if USES_PER_INSTANCE_CUSTOM_DATA && VF_USE_PRIMITIVE_SCENE_DATA
Result.CustomDataOffset = Intermediates.SceneData.InstanceData.CustomDataOffset;
Result.CustomDataCount = Intermediates.SceneData.InstanceData.CustomDataCount;
#endif
#if USES_PER_INSTANCE_RANDOM && VF_USE_PRIMITIVE_SCENE_DATA
Result.PerInstanceRandom = Intermediates.SceneData.InstanceData.RandomID;
#endif
#endif
Result.PreSkinnedPosition = Intermediates.PreSkinPosition.xyz;
Result.PreSkinnedNormal = TangentToLocal[2]; //TangentBias(Input.TangentZ.xyz);
#if MANUAL_VERTEX_FETCH && NUM_MATERIAL_TEXCOORDS_VERTEX
const uint NumFetchTexCoords = LocalVF.VertexFetch_Parameters[VF_NumTexcoords_Index];
UNROLL
for (uint CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++)
{
// Clamp coordinates to mesh's maximum as materials can request more than are available
uint ClampedCoordinateIndex = min(CoordinateIndex, NumFetchTexCoords-1);
Result.TexCoords[CoordinateIndex] = LocalVF.VertexFetch_TexCoordBuffer[NumFetchTexCoords * (LocalVF.VertexFetch_Parameters[VF_VertexOffset] + Input.VertexId) + ClampedCoordinateIndex];
}
#elif NUM_MATERIAL_TEXCOORDS_VERTEX
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 0
Result.TexCoords[0] = Input.TexCoords0.xy;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 1
Result.TexCoords[1] = Input.TexCoords0.zw;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 2
Result.TexCoords[2] = Input.TexCoords1.xy;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 3
Result.TexCoords[3] = Input.TexCoords1.zw;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 4
Result.TexCoords[4] = Input.TexCoords2.xy;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 5
Result.TexCoords[5] = Input.TexCoords2.zw;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 6
Result.TexCoords[6] = Input.TexCoords3.xy;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 7
Result.TexCoords[7] = Input.TexCoords3.zw;
#endif
#endif //MANUAL_VERTEX_FETCH && NUM_MATERIAL_TEXCOORDS_VERTEX
Result.Particle.Color = half4(1,1,1,1);
#if NEEDS_PARTICLE_LOCAL_TO_WORLD
Result.Particle.ParticleToWorld = PrimitiveData.LocalToWorld;
#endif
#if NEEDS_PARTICLE_WORLD_TO_LOCAL
Result.Particle.WorldToParticle = PrimitiveData.WorldToLocal;
#endif
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(Result);
#endif
return Result;
}
FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
FVertexFactoryInterpolantsVSToPS Interpolants;
// Initialize the whole struct to 0
// Really only the last two components of the packed UVs have the opportunity to be uninitialized
Interpolants = (FVertexFactoryInterpolantsVSToPS)0;
#if NUM_TEX_COORD_INTERPOLATORS
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS];
GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs);
GetCustomInterpolators(VertexParameters, CustomizedUVs);
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
SetUV(Interpolants, CoordinateIndex, CustomizedUVs[CoordinateIndex]);
}
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 0 && USE_PARTICLE_SUBUVS
#if MANUAL_VERTEX_FETCH
SetUV(Interpolants, 0, LocalVF.VertexFetch_TexCoordBuffer[LocalVF.VertexFetch_Parameters[VF_NumTexcoords_Index] * (LocalVF.VertexFetch_Parameters[VF_VertexOffset] + Input.VertexId)]);
#else
SetUV(Interpolants, 0, Input.TexCoords0);
#endif
#endif
#if NEEDS_LIGHTMAP_COORDINATE
float2 LightMapCoordinate = 0;
float2 ShadowMapCoordinate = 0;
#if MANUAL_VERTEX_FETCH
float2 LightMapCoordinateInput = LocalVF.VertexFetch_TexCoordBuffer[LocalVF.VertexFetch_Parameters[VF_NumTexcoords_Index] * (LocalVF.VertexFetch_Parameters[VF_VertexOffset] + Input.VertexId) + LocalVF.VertexFetch_Parameters[FV_LightMapIndex_Index]];
#else
float2 LightMapCoordinateInput = Input.LightMapCoordinate;
#endif
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(Intermediates);
uint LightmapDataIndex = PrimitiveData.LightmapDataIndex + LocalVF.LODLightmapDataIndex;
uint LightmapUVIndex = PrimitiveData.LightmapUVIndex;
float4 LightMapCoordinateScaleBias = VF_GPUSCENE_GET_LIGHTMAP_UV_SCALE_BIAS(Input, LightmapDataIndex);
#if USE_INSTANCING || USE_INSTANCE_CULLING
LightMapCoordinate = LightMapCoordinateInput * LightMapCoordinateScaleBias.xy + GetInstanceLightMapBias(Intermediates);
#else
LightMapCoordinate = LightMapCoordinateInput * LightMapCoordinateScaleBias.xy + LightMapCoordinateScaleBias.zw;
#endif
#if STATICLIGHTING_TEXTUREMASK
float4 ShadowMapCoordinateScaleBias = VF_GPUSCENE_GET_SHADOWMAP_UV_SCALE_BIAS(Input, LightmapDataIndex);
#if USE_INSTANCING || USE_INSTANCE_CULLING
ShadowMapCoordinate = LightMapCoordinateInput * ShadowMapCoordinateScaleBias.xy + GetInstanceShadowMapBias(Intermediates);
#else
ShadowMapCoordinate = LightMapCoordinateInput * ShadowMapCoordinateScaleBias.xy + ShadowMapCoordinateScaleBias.zw;
#endif
#endif // STATICLIGHTING_TEXTUREMASK
SetLightMapCoordinate(Interpolants, LightMapCoordinate, ShadowMapCoordinate);
SetLightmapDataIndex(Interpolants, LightmapDataIndex);
#endif // NEEDS_LIGHTMAP_COORDINATE
SetTangents(Interpolants, Intermediates.TangentToWorld[0], Intermediates.TangentToWorld[2], Intermediates.TangentToWorldSign);
SetColor(Interpolants, Intermediates.Color);
#if USE_INSTANCING || USE_INSTANCE_CULLING
Interpolants.PerInstanceParams = Intermediates.PerInstanceParams;
#endif
#if USES_PER_INSTANCE_CUSTOM_DATA && VF_USE_PRIMITIVE_SCENE_DATA
Interpolants.CustomDataOffset = Intermediates.SceneData.InstanceData.CustomDataOffset;
Interpolants.CustomDataCount = Intermediates.SceneData.InstanceData.CustomDataCount;
#endif
#if USES_PER_INSTANCE_RANDOM && VF_USE_PRIMITIVE_SCENE_DATA
Interpolants.PerInstanceRandom = Intermediates.SceneData.InstanceData.RandomID;
#endif
#if INSTANCED_STEREO
Interpolants.EyeIndex = 0;
#endif
SetPrimitiveId(Interpolants, Intermediates.SceneData.PrimitiveId);
#if HAS_INSTANCE_LOCAL_TO_WORLD_PS
SetInstanceLocalToWorld(Interpolants, VertexParameters.InstanceLocalToWorld);
#endif
#if HAS_INSTANCE_WORLD_TO_LOCAL_PS
SetInstanceWorldToLocal(Interpolants, VertexParameters.InstanceWorldToLocal);
#endif
return Interpolants;
}
/** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */
FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition)
{
// GetMaterialPixelParameters is responsible for fully initializing the result
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
#if NUM_TEX_COORD_INTERPOLATORS
UNROLL
for( int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++ )
{
Result.TexCoords[CoordinateIndex] = GetUV(Interpolants, CoordinateIndex);
}
#endif
#if USE_PARTICLE_SUBUVS
// Output TexCoord0 for when previewing materials that use ParticleSubUV.
Result.Particle.SubUVCoords[0] = GetUV(Interpolants, 0);
Result.Particle.SubUVCoords[1] = GetUV(Interpolants, 0);
#endif // USE_PARTICLE_SUBUVS
half3 TangentToWorld0 = GetTangentToWorld0(Interpolants).xyz;
half4 TangentToWorld2 = GetTangentToWorld2(Interpolants);
Result.UnMirrored = TangentToWorld2.w;
Result.VertexColor = GetColor(Interpolants);
// Required for previewing materials that use ParticleColor
Result.Particle.Color = half4(1,1,1,1);
#if USE_INSTANCING || USE_INSTANCE_CULLING
Result.PerInstanceParams = Interpolants.PerInstanceParams;
#endif
Result.TangentToWorld = AssembleTangentToWorld( TangentToWorld0, TangentToWorld2 );
#if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION
Result.WorldVertexNormal_Center = Interpolants.TangentToWorld2_Center.xyz;
#endif
#if LIGHTMAP_UV_ACCESS
#if NEEDS_LIGHTMAP_COORDINATE
#if (ES3_1_PROFILE)
// Not supported in pixel shader
Result.LightmapUVs = float2(0, 0);
#else
Result.LightmapUVs = Interpolants.LightMapCoordinate.xy;
#endif // ES3_1_PROFILE
#endif // NEEDS_LIGHTMAP_COORDINATE
#endif // LIGHTMAP_UV_ACCESS
Result.TwoSidedSign = 1;
Result.PrimitiveId = GetPrimitiveId(Interpolants);
#if USES_PER_INSTANCE_CUSTOM_DATA && VF_USE_PRIMITIVE_SCENE_DATA
Result.CustomDataOffset = Interpolants.CustomDataOffset;
Result.CustomDataCount = Interpolants.CustomDataCount;
#endif
#if USES_PER_INSTANCE_RANDOM && VF_USE_PRIMITIVE_SCENE_DATA
Result.PerInstanceRandom = Interpolants.PerInstanceRandom;
#endif
#if HAS_INSTANCE_LOCAL_TO_WORLD_PS
Result.InstanceLocalToWorld = GetInstanceLocalToWorld(Interpolants);
#endif
#if HAS_INSTANCE_WORLD_TO_LOCAL_PS
Result.InstanceWorldToLocal = GetInstanceWorldToLocal(Interpolants);
#endif
#if NEEDS_PARTICLE_LOCAL_TO_WORLD || NEEDS_PARTICLE_WORLD_TO_LOCAL
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(Result.PrimitiveId);
#endif
#if NEEDS_PARTICLE_LOCAL_TO_WORLD
Result.Particle.ParticleToWorld = PrimitiveData.LocalToWorld;
#endif
#if NEEDS_PARTICLE_WORLD_TO_LOCAL
Result.Particle.WorldToParticle = PrimitiveData.WorldToLocal;
#endif
return Result;
}
/** Initializes the subset of Parameters that was not set in GetMaterialPixelParameters. */
void CalcMaterialParametersEx(
in out FMaterialPixelParameters Parameters,
in out FPixelMaterialInputs PixelMaterialInputs,
float4 SvPosition,
float4 ScreenPosition,
FIsFrontFace bIsFrontFace,
float3 TranslatedWorldPosition,
float3 TranslatedWorldPositionExcludingShaderOffsets)
{
// Remove the pre view translation
Parameters.WorldPosition_CamRelative = TranslatedWorldPosition.xyz;
Parameters.AbsoluteWorldPosition = LWCSubtract(TranslatedWorldPosition.xyz, ResolvedView.PreViewTranslation);
// If the material uses any non-offset world position expressions, calculate those parameters. If not,
// the variables will have been initialised to 0 earlier.
#if USE_WORLD_POSITION_EXCLUDING_SHADER_OFFSETS
Parameters.WorldPosition_NoOffsets_CamRelative = TranslatedWorldPositionExcludingShaderOffsets;
Parameters.WorldPosition_NoOffsets = LWCSubtract(TranslatedWorldPositionExcludingShaderOffsets, ResolvedView.PreViewTranslation);
#endif
Parameters.SvPosition = SvPosition;
Parameters.ScreenPosition = ScreenPosition;
Parameters.ViewBufferUV = ScreenPositionToBufferUV(ScreenPosition);
// CameraVector is a normalised vector representing the "from surface to camera" direction.
#if RAYHITGROUPSHADER
Parameters.CameraVector = -WorldRayDirection();
#else
if (IsOrthoProjection(ResolvedView))
{
// CameraVector is just ViewForward in an ortho mode
Parameters.CameraVector = -ResolvedView.ViewForward;
}
else
{
// TranslatedWorldPosition is the world position translated to the camera position, which is just -CameraVector in perspective projection
Parameters.CameraVector = normalize(-Parameters.WorldPosition_CamRelative.xyz);
}
#endif
Parameters.LightVector = 0;
#if IS_NANITE_PASS
// Nanite does not support OPTIONAL_IsFrontFace, so Nanite sets the following to 1.0f or -1.0f in
// GetMaterialPixelParameters - here we pull it out into our own front facing bool.
const bool bNaniteIsFrontFace = Parameters.TwoSidedSign < 0.0f;
#endif
Parameters.TwoSidedSign = 1.0f;
#if MATERIAL_TWOSIDED && HAS_PRIMITIVE_UNIFORM_BUFFER
// #dxr: DirectX Raytracing's HitKind() intrinsic already accounts for negative scaling
#if PIXELSHADER
Parameters.TwoSidedSign *= ResolvedView.CullingSign * GetPrimitive_DeterminantSign(Parameters.PrimitiveId);
#endif
#endif
#if (MATERIAL_TWOSIDED && !MATERIAL_TWOSIDED_SEPARATE_PASS) || RAYHITGROUPSHADER
// Either we have a two-sided material that needs a sign flip, or we have a ray tracing material
// that needs to consider rays arriving from either side
#if IS_NANITE_PASS
Parameters.TwoSidedSign *= GetFloatFacingSign(bNaniteIsFrontFace);
#else
Parameters.TwoSidedSign *= GetFloatFacingSign(bIsFrontFace);
#endif
#endif
#if NUM_VIRTUALTEXTURE_SAMPLES || LIGHTMAP_VT_ENABLED
InitializeVirtualTextureFeedback(Parameters.VirtualTextureFeedback, (uint2)SvPosition.xy, View.FrameNumber);
#endif
#if USE_ANALYTIC_DERIVATIVES
if(!TEXTURE_SAMPLE_DEBUG || View.GeneralPurposeTweak >= 1.0f)
CalcPixelMaterialInputsAnalyticDerivatives(Parameters, PixelMaterialInputs);
else
#endif
{
CalcPixelMaterialInputs(Parameters, PixelMaterialInputs);
}
}