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

879 lines
34 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
LandscapeVertexFactory.usf: Landscape vertex factory.
=============================================================================*/
// Compile Version 2
#include "VertexFactoryCommon.ush"
#define VERTEX_FACTORY_MODIFIES_INTERPOLATION 1
#define VF_SUPPORTS_RAYTRACING_VERTEX_MASK 1
// Set by FLightMapTexturePolicy
#include "/Engine/Generated/UniformBuffers/PrecomputedLightingBuffer.ush"
#include "LightmapData.ush"
#if LANDSCAPE_XYOFFSET
#define XYOFFSET_SCALE (1.0f/256.f)
#endif
/* Return index into buffers for this section instance. */
int GetComponentLinearIndex()
{
#if FIXED_GRID
// Fixed grid doesn't use LandscapeContinuousLODParameters buffers
return 0;
#else
return (LandscapeParameters.ComponentBaseY - LandscapeContinuousLODParameters.Min.y) * LandscapeContinuousLODParameters.Size.x + (LandscapeParameters.ComponentBaseX - LandscapeContinuousLODParameters.Min.x);
#endif
}
float GetViewLODData(uint ComponentIndex)
{
#if FIXED_GRID
// Fixed grid doesn't use LandscapeContinuousLODParameters buffers
return 0;
#else
const int AbsoluteSectionLODIndex = View.LandscapeIndirection[LandscapeContinuousLODParameters.LandscapeIndex] + ComponentIndex;
return View.LandscapePerComponentData[AbsoluteSectionLODIndex];
#endif
}
/* Get current Lod for this section instance. */
float GetSectionLod(uint ComponentIndex)
{
#if FIXED_GRID
// Fixed grid has fixed Lod
return LandscapeFixedGrid.LodValues.x;
#else
#if RAYTRACING_DYNAMIC_GEOMETRY_CONVERTER
int LODBias = LandscapeParameters.RayTracingLODBias;
#else
int LODBias = View.FarShadowStaticMeshLODBias;
#endif
return min(GetViewLODData(ComponentIndex) + LODBias, LandscapeParameters.LastLOD);
#endif
}
/**
* x = Current mesh LOD
* y = Unused
* z = SectionSizeQuads in current mesh LOD
* w = 1/SectionSizeQuads in current mesh LOD
**/
float4 GetLodValues(uint ComponentIndex)
{
#if FIXED_GRID
// Fixed grid has fixed Lod
return LandscapeFixedGrid.LodValues;
#else
// Get Lod for the current section and infer the values
uint Lod = (uint)floor(GetSectionLod(ComponentIndex));
float LodSubSectionSizeQuads = (float)((LandscapeParameters.SubsectionSizeVerts >> Lod) - 1);
return float4(Lod, 0, LodSubSectionSizeQuads, rcp(LodSubSectionSizeQuads));
#endif
}
uint GetRayTracingSectionVertCountForLOD(uint ComponentIndex)
{
uint Lod = (uint)floor(GetSectionLod(ComponentIndex));
return ((LandscapeParameters.RayTracingSectionSizeVerts >> Lod) + 1);
}
/**
* x = Heightmap texture LOD difference from current LOD to highest LOD
* y = todo: XYOffset texture LOD difference from current LOD to highest LOD
*/
float2 GetLodBias(uint ComponentIndex)
{
#if FIXED_GRID
// Fixed grid doesn't use LodBias
return 0;
#else
float LodBias = LandscapeContinuousLODParameters.SectionLODBias[ComponentIndex];
return float2(LodBias, 0.f);
#endif
}
float GetNeighborSectionLodFromOffset(int2 NeighborOffset, float CenterLOD)
{
#if FIXED_GRID
// Fixed grid has fixed Lod
return LandscapeFixedGrid.LodValues.x;
#else // !FIXED_GRID
// Calculate neighbor position, and clamp to valid range
int2 NeighborPos = NeighborOffset + int2(LandscapeParameters.ComponentBaseX, LandscapeParameters.ComponentBaseY);
NeighborPos = max(LandscapeContinuousLODParameters.Min , NeighborPos);
NeighborPos = min(LandscapeContinuousLODParameters.Min + LandscapeContinuousLODParameters.Size - int2(1, 1), NeighborPos);
// lookup neighbor LOD
uint NeighborComponentIndex = (NeighborPos.y - LandscapeContinuousLODParameters.Min.y) * LandscapeContinuousLODParameters.Size.x + (NeighborPos.x - LandscapeContinuousLODParameters.Min.x);
float NeighborLOD = GetSectionLod(NeighborComponentIndex);
// the Edge LOD is the max of Our LOD and the Neighbor LOD (both sides will agree on EdgeLOD this way)
float EdgeLOD = max(CenterLOD, NeighborLOD);
return EdgeLOD;
#endif // !FIXED_GRID
}
/** Calculate fractional render LOD for a vertex within a subsection. */
float CalcLOD(uint ComponentIndex, float2 xyLocalToSubsection, float2 Subsection)
{
#if FIXED_GRID
// Fixed grid has fixed Lod
return LandscapeFixedGrid.LodValues.x;
#else
float2 xy = (xyLocalToSubsection + Subsection) / LandscapeParameters.NumSubsections;
// calculate blend value (normalized distance from section border: 1 at the border, 0 at the center)
float2 Delta = xy * 2 - 1;
float2 AbsDelta = abs(Delta);
float LB = max(AbsDelta.x, AbsDelta.y);
// shift the blend towards 0 to reduce the lod blend range
float k = LandscapeParameters.InvLODBlendRange;
LB = saturate(1 - (1-LB) * k);
// determine which neighbor is closest
int2 NeighborOffset = (AbsDelta.x > AbsDelta.y) ? int2(sign(Delta.x), 0) : int2(0, sign(Delta.y));
// calculate lod for this section, and the closest neighbor
float CenterLod = GetSectionLod(ComponentIndex);
float NeighborLod = GetNeighborSectionLodFromOffset(NeighborOffset, CenterLod);
// blend from center lod to the neighbor lod, based on distance from the border
float LODCalculated = (1-LB) * CenterLod + LB * NeighborLod;
return LODCalculated;
#endif
}
struct FVertexFactoryInput
{
// UByte4
uint4 Position : ATTRIBUTE0;
#if LANDSCAPE_TILE
#undef GetInstanceIdFromVF
// UByte4
uint4 TileData : ATTRIBUTE1;
#if INSTANCED_STEREO
#define GetInstanceIdFromVF(VFInput) (VFInput.InstanceId)
uint InstanceId : SV_InstanceID;
#else
#define GetInstanceIdFromVF(VFInput) (0)
#endif // INSTANCED_STEREO
#else
// Dynamic instancing related attributes with InstanceIdOffset : ATTRIBUTE1
VF_GPUSCENE_DECLARE_INPUT_BLOCK(1)
VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
#endif
VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK()
};
// RHI_RAYTRACING
#if RAYHITGROUPSHADER
FVertexFactoryInput LoadVertexFactoryInputForHGS(uint TriangleIndex, int VertexIndex)
{
FVertexFactoryInput Input;
uint RTSectionSizeVertsCurrentLOD = GetRayTracingSectionVertCountForLOD(GetComponentLinearIndex());
uint RTSectionSizeQuadsCurrentLOD = RTSectionSizeVertsCurrentLOD - 1;
uint NumRayTracingSections = LandscapeParameters.NumRayTracingSections;
float RTSectionSizeScaler = (RTSectionSizeQuadsCurrentLOD - 1.0f / NumRayTracingSections) / RTSectionSizeQuadsCurrentLOD ;
#if 1
FTriangleBaseAttributes Tri = LoadTriangleBaseAttributes(TriangleIndex);
uint VertexId = Tri.Indices[VertexIndex];
#else
uint QuadId = TriangleIndex / 2;
uint QuadX = QuadId % RTSectionSizeQuadsCurrentLOD;
uint QuadY = QuadId / RTSectionSizeQuadsCurrentLOD;
uint i00 = (QuadX + 0) + (QuadY + 0) * RTSectionSizeVertsCurrentLOD;
uint i10 = (QuadX + 1) + (QuadY + 0) * RTSectionSizeVertsCurrentLOD;
uint i11 = (QuadX + 1) + (QuadY + 1) * RTSectionSizeVertsCurrentLOD;
uint i01 = (QuadX + 0) + (QuadY + 1) * RTSectionSizeVertsCurrentLOD;
uint Indices[6] = {i00, i11, i10, i00, i01, i10};
uint VertexId = Indices[TriangleIndex % 2 * 3 + VertexIndex];
#endif
// Note: GetInstanceUserData() stores the GPU-Scene instance ID
VF_GPUSCENE_SET_INPUT_FOR_RT(Input, GetInstanceUserData(), 0U);
Input.Position.x = (VertexId % RTSectionSizeVertsCurrentLOD + LandscapeMVF.RayTracingSectionXY.x * RTSectionSizeQuadsCurrentLOD) * RTSectionSizeScaler;
Input.Position.y = (VertexId / RTSectionSizeVertsCurrentLOD + LandscapeMVF.RayTracingSectionXY.y * RTSectionSizeQuadsCurrentLOD) * RTSectionSizeScaler;
Input.Position.z = LandscapeMVF.SubXY.x;
Input.Position.w = LandscapeMVF.SubXY.y;
return Input;
}
#endif
#if COMPUTESHADER
FVertexFactoryInput LoadVertexFactoryInputForDynamicUpdate(uint TriangleIndex, int VertexIndex, uint PrimitiveId, uint DrawInstanceId)
{
FVertexFactoryInput Input = (FVertexFactoryInput)0;
uint RTSectionSizeVertsCurrentLOD = GetRayTracingSectionVertCountForLOD(GetComponentLinearIndex());
uint RTSectionSizeQuadsCurrentLOD = RTSectionSizeVertsCurrentLOD - 1;
uint NumRayTracingSections = LandscapeParameters.NumRayTracingSections;
float RTSectionSizeScaler = (RTSectionSizeQuadsCurrentLOD - 1.0f / NumRayTracingSections) / RTSectionSizeQuadsCurrentLOD ;
uint VertexId = TriangleIndex * 3 + VertexIndex;
Input.Position.x = (VertexId % RTSectionSizeVertsCurrentLOD + LandscapeMVF.RayTracingSectionXY.x * RTSectionSizeQuadsCurrentLOD) * RTSectionSizeScaler;
Input.Position.y = (VertexId / RTSectionSizeVertsCurrentLOD + LandscapeMVF.RayTracingSectionXY.y * RTSectionSizeQuadsCurrentLOD) * RTSectionSizeScaler;
Input.Position.z = LandscapeMVF.SubXY.x;
Input.Position.w = LandscapeMVF.SubXY.y;
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(PrimitiveId);
VF_GPUSCENE_SET_INPUT_FOR_RT(Input, PrimitiveData.InstanceSceneDataOffset + DrawInstanceId, DrawInstanceId);
return Input;
}
uint GetNumRayTracingDynamicMeshVerticesIndirect()
{
return 0;
}
#endif
struct FVertexFactoryInterpolantsVSToPS
{
float2 LayerTexCoord : TEXCOORD0; // xy == texcoord
float4 WeightHeightMapTexCoord : TEXCOORD1;
float4 TransformedTexCoords : TEXCOORD2;
#if NEEDS_LIGHTMAP_COORDINATE
#if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1
float2 LightMapCoordinate[2] : TEXCOORD4;
float2 ShadowMapCoordinate : TEXCOORD6;
#else
float2 LightMapCoordinate : TEXCOORD3;
float2 ShadowMapCoordinate : TEXCOORD4;
#endif
#endif
#if VF_USE_PRIMITIVE_SCENE_DATA
nointerpolation uint PrimitiveId : PRIMITIVE_ID;
#endif
};
#if NUM_TEX_COORD_INTERPOLATORS
float2 GetUV(FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex)
{
return 0;
}
#endif
struct FLandscapeTexCoords
{
float2 LayerTexCoord : TEXCOORD0; // xy == texcoord
float2 WeightMapTexCoord;
float2 HeightMapTexCoord;
#if NEEDS_LIGHTMAP_COORDINATE
float2 LightMapCoordinate;
#endif
};
struct FVertexFactoryRayTracingInterpolants
{
FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS;
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
// First row of the tangent to world matrix
float3 TangentToWorld0 : VS_To_DS_TangentToWorld0;
// Last row of the tangent to world matrix in xyz
float4 TangentToWorld2 : VS_To_DS_TangentToWorld2;
// LOD of the vertex, used for fading out tessellation
float CalculatedLOD : VS_To_DS_CalculatedLOD;
#endif
};
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
float2 VertexFactoryGetRayTracingTextureCoordinate( FVertexFactoryRayTracingInterpolants Interpolants )
{
return Interpolants.InterpolantsVSToPS.WeightHeightMapTexCoord.zw;
}
#endif // #if NEEDS_VERTEX_FACTORY_INTERPOLATION
FVertexFactoryInterpolantsVSToPS VertexFactoryAssignInterpolants(FVertexFactoryRayTracingInterpolants Input)
{
return Input.InterpolantsVSToPS;
}
struct FVertexFactoryIntermediates
{
float4 InputPosition;
float3 LocalPosition;
float3 WorldNormal;
uint ComponentIndex;
float4 LodValues;
float2 LodBias;
/** Cached primitive and instance data */
FSceneDataIntermediates SceneData;
};
FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.Primitive;
}
FInstanceSceneData GetInstanceData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.InstanceData;
}
float3 GetLocalPosition(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.LocalPosition+float3(Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.ww,0);
}
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
FDFMatrix LocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
return TransformLocalToTranslatedWorld(GetLocalPosition(Intermediates), LocalToWorld);
}
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return GetLocalPosition(Intermediates);
}
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return Intermediates.WorldNormal;
}
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
// Note we still use LocalToWorld. Landscape's transform never changes.
float3 LocalPosition = GetLocalPosition(Intermediates);
FDFMatrix LocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
return TransformPreviousLocalPositionToTranslatedWorld(LocalPosition, LocalToWorld);
}
// local position relative to instance
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return GetLocalPosition(Intermediates);
}
/** Calculate the texture coordinates generated by Landscape */
FLandscapeTexCoords GetLandscapeTexCoords(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
FLandscapeTexCoords Result;
Result.LayerTexCoord.xy = Intermediates.LocalPosition.xy + LandscapeParameters.SubsectionSizeVertsLayerUVPan.zw + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.ww;
Result.WeightMapTexCoord = Intermediates.LocalPosition.xy * LandscapeParameters.WeightmapUVScaleBias.xy + LandscapeParameters.WeightmapUVScaleBias.zw + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.zz;
Result.HeightMapTexCoord = Intermediates.LocalPosition.xy * LandscapeParameters.HeightmapUVScaleBias.xy + LandscapeParameters.HeightmapUVScaleBias.zw + 0.5*LandscapeParameters.HeightmapUVScaleBias.xy + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.xy;
#if NEEDS_LIGHTMAP_COORDINATE
Result.LightMapCoordinate = (Intermediates.LocalPosition.xy * LandscapeParameters.LandscapeLightmapScaleBias.xy + LandscapeParameters.LandscapeLightmapScaleBias.wz + Intermediates.InputPosition.zw * LandscapeParameters.LightmapSubsectionOffsetParams.xy);
#endif
return Result;
}
float3x3 CalcTangentBasisFromWorldNormal(float3 Normal)
{
float3 LocalTangentX = normalize(float3(Normal.z, 0.0f, -Normal.x));
float3 LocalTangentY = cross(Normal, LocalTangentX);
float3x3 LocalToTangent = float3x3(LocalTangentX,LocalTangentY,Normal);
return LocalToTangent;
}
/** Lookup per-pixel tangent basis from heightmap texture */
float3x3 VertexFactoryGetPerPixelTangentBasis(FVertexFactoryInterpolantsVSToPS Interpolants)
{
float3x3 Result;
#if PIXELSHADER || RAYHITGROUPSHADER || COMPUTESHADER
float4 SampleValue = Texture2DSample(LandscapeParameters.NormalmapTexture, LandscapeParameters.NormalmapTextureSampler, Interpolants.WeightHeightMapTexCoord.zw);
float2 SampleNormal = float2(SampleValue.b, SampleValue.a) * float2(2.0,2.0) - float2(1.0,1.0);
float3 WorldNormal = float3( SampleNormal, sqrt(max(1.0-dot(SampleNormal,SampleNormal),0.0)) );
Result = CalcTangentBasisFromWorldNormal(WorldNormal);
#endif
return Result;
}
/** Lookup per-pixel height from heightmap texture. */
#define VF_PER_PIXEL_HEIGHTMAP 1
/** Use high quality barycentric version of interpolation. */
#ifndef PER_PIXEL_HEIGHTMAP_HQ
#define PER_PIXEL_HEIGHTMAP_HQ 0
#endif
bool HasVertexFactoryPerPixelHeight()
{
return LandscapeParameters.VirtualTexturePerPixelHeight != 0;
}
float GetVertexFactoryPerPixelHeight(FVertexFactoryInterpolantsVSToPS Interpolants)
{
float2 UV = Interpolants.WeightHeightMapTexCoord.zw;
#if PER_PIXEL_HEIGHTMAP_HQ
if (LandscapeParameters.VirtualTexturePerPixelHeight > 1)
{
// It only makes sense to use barycentric sampling when sampling resolution is sufficiently above heightmap resolution.
float2 TextureSize = LandscapeParameters.HeightmapTextureSize.xy;
float2 dUVdx = ddx(UV * TextureSize);
float2 dUVdy = ddy(UV * TextureSize);
float T = max(dot(dUVdx, dUVdx), dot(dUVdy, dUVdy));
if (T < 0.125f)
{
// Fake barycentric interpolation to try and match same result that we would get from vertex interpolation with a vertex located at each texel.
float2 PixelCoord = UV * TextureSize - 0.5f;
float2 FloorPixelCoord = floor(PixelCoord);
float2 SubPixelPos = PixelCoord - FloorPixelCoord;
float B1 = min(SubPixelPos.x, SubPixelPos.y);
float B2 = abs(SubPixelPos.y - SubPixelPos.x);
float B0 = 1.0f - B1 - B2;
// Which is the third sample/triangle vertex depends on if we are upper or lower triangle in the quad.
bool bLowerTriangle = (SubPixelPos.x > SubPixelPos.y);
float2 InvTextureSize = LandscapeParameters.HeightmapTextureSize.zw;
float2 BaseUV = (FloorPixelCoord + 0.5f) * InvTextureSize;
float2 UV1 = InvTextureSize;
float2 UV2 = bLowerTriangle ? float2(InvTextureSize.x, 0) : float2(0, InvTextureSize.y);
float2 S0 = LandscapeParameters.HeightmapTexture.SampleLevel(LandscapeParameters.HeightmapTextureSampler, BaseUV, 0).xy;
float2 S1 = LandscapeParameters.HeightmapTexture.SampleLevel(LandscapeParameters.HeightmapTextureSampler, BaseUV + UV1, 0).xy;
float2 S2 = LandscapeParameters.HeightmapTexture.SampleLevel(LandscapeParameters.HeightmapTextureSampler, BaseUV + UV2, 0).xy;
float H0 = DecodePackedHeight(S0);
float H1 = DecodePackedHeight(S1);
float H2 = DecodePackedHeight(S2);
return B0 * H0 + B1 * H1 + B2 * H2;
}
}
#endif // PER_PIXEL_HEIGHTMAP_HQ
// Use simple bilinear interpolation of heightmap.
// Ideally we want to reuse this sample from VertexFactoryGetPerPixelTangentBasis().
float4 SampleValue = LandscapeParameters.NormalmapTexture.Sample(LandscapeParameters.NormalmapTextureSampler, UV);
return DecodePackedHeight(SampleValue.xy);
}
/** 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 NEEDS_LIGHTMAP_COORDINATE
#if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1
// Not supported in pixel shader
float2 LightmapUVs = float2(0, 0);
#else
float2 LightmapUVs = Interpolants.LightMapCoordinate.xy;
#endif
#else
float2 LightmapUVs = float2(0, 0);
#endif
#if NUM_MATERIAL_TEXCOORDS // XY layer
Result.TexCoords[0] = Interpolants.LayerTexCoord.xy;
#if NUM_MATERIAL_TEXCOORDS > 1 // VS calcualted TexCoord 1, default is XZ layer
Result.TexCoords[1] = Interpolants.TransformedTexCoords.xy;
#if NUM_MATERIAL_TEXCOORDS > 2 // VS calcualted TexCoord 2, default is YZ layer
Result.TexCoords[2] = Interpolants.TransformedTexCoords.zw;
#if NUM_MATERIAL_TEXCOORDS > 3 // Weightmap
Result.TexCoords[3] = Interpolants.WeightHeightMapTexCoord.xy;
#if NUM_MATERIAL_TEXCOORDS > 4 // Lightmap
#if NEEDS_LIGHTMAP_COORDINATE
Result.TexCoords[4] = LightmapUVs;
#else
Result.TexCoords[4] = float2(0,0);
#endif
#if NUM_MATERIAL_TEXCOORDS > 5 // Height map
Result.TexCoords[5] = Interpolants.WeightHeightMapTexCoord.zw;
#if NUM_MATERIAL_TEXCOORDS > 6
for (uint CoordinateIndex = 6; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
{
Result.TexCoords[CoordinateIndex] = float2(0,0);
}
#endif // 6
#endif // 5
#endif // 4
#endif // 3
#endif // 2
#endif // 1
#endif // 0
// Calculate LocalToTangent directly from normal map texture.
float3x3 TangentToLocal = VertexFactoryGetPerPixelTangentBasis(Interpolants);
Result.TangentToWorld = mul(TangentToLocal, (float3x3)LandscapeParameters.LocalToWorldNoScaling);
Result.UnMirrored = 1;
Result.VertexColor = 1;
#if LIGHTMAP_UV_ACCESS
Result.LightmapUVs = LightmapUVs;
#endif
Result.TwoSidedSign = 1;
#if VF_USE_PRIMITIVE_SCENE_DATA
Result.PrimitiveId = Interpolants.PrimitiveId;
#endif
return Result;
}
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
/** 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,
float3x3 TangentToLocal,
bool bIsPreviousFrame = false)
{
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
Result.SceneData = Intermediates.SceneData;
Result.WorldPosition = WorldPosition;
if (bIsPreviousFrame)
{
Result.PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
}
else
{
Result.PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
}
Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive
Result.VertexColor = float4(1,1,1,1);
Result.TangentToWorld = mul(TangentToLocal, (float3x3)LandscapeParameters.LocalToWorldNoScaling);
Result.PreSkinnedPosition = Intermediates.LocalPosition.xyz;
Result.PreSkinnedNormal = TangentToLocal[2].xyz;
// Assumes no instancing and landscape's transform never change
Result.PrevFrameLocalToWorld = GetInstanceData(Intermediates).LocalToWorld;
FLandscapeTexCoords LandscapeTexCoords = GetLandscapeTexCoords(Input, Intermediates);
#if NUM_MATERIAL_TEXCOORDS_VERTEX // XY layer
Result.TexCoords[0] = LandscapeTexCoords.LayerTexCoord.xy;
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 1 // XZ layer
Result.TexCoords[1] = float2(LandscapeTexCoords.LayerTexCoord.x, Intermediates.LocalPosition.z);
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 2 // YZ layer
Result.TexCoords[2] = float2(LandscapeTexCoords.LayerTexCoord.y, Intermediates.LocalPosition.z);
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 3 // Weightmap
Result.TexCoords[3] = LandscapeTexCoords.WeightMapTexCoord;
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 4 // Lightmap
#if NEEDS_LIGHTMAP_COORDINATE
Result.TexCoords[4] = LandscapeTexCoords.LightMapCoordinate.xy;
#else
Result.TexCoords[4] = float2(0,0);
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 5 // Height map
Result.TexCoords[5] = LandscapeTexCoords.HeightMapTexCoord;
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 6
UNROLL
for (uint CoordinateIndex = 6; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++)
{
Result.TexCoords[CoordinateIndex] = float2(0,0);
}
#endif // 6
#endif // 5
#endif // 4
#endif // 3
#endif // 2
#endif // 1
#endif // 0
#if ENABLE_NEW_HLSL_GENERATOR
EvaluateVertexMaterialAttributes(Result);
#endif
Result.LWCData = MakeMaterialLWCData(Result);
return Result;
}
#if NEEDS_LIGHTMAP_COORDINATE
void GetLightMapCoordinates(FVertexFactoryInterpolantsVSToPS Interpolants, out float2 LightmapUV0, out float2 LightmapUV1, out uint LightmapDataIndex)
{
#if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1
LightmapUV0 = Interpolants.LightMapCoordinate[0];
LightmapUV1 = Interpolants.LightMapCoordinate[1];
#else
LightmapUV0 = Interpolants.LightMapCoordinate * float2( 1, 0.5 );
LightmapUV1 = LightmapUV0 + float2( 0, 0.5 );
#endif
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = GetPrimitiveData(Interpolants.PrimitiveId).LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
}
void GetShadowMapCoordinate(FVertexFactoryInterpolantsVSToPS Interpolants, out float2 ShadowMapCoordinate, out uint LightmapDataIndex)
{
ShadowMapCoordinate = Interpolants.ShadowMapCoordinate;
#if VF_USE_PRIMITIVE_SCENE_DATA
LightmapDataIndex = GetPrimitiveData(Interpolants.PrimitiveId).LightmapDataIndex;
#else
LightmapDataIndex = 0;
#endif
}
#endif
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
FVertexFactoryIntermediates Intermediates;
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
Intermediates.ComponentIndex = GetComponentLinearIndex();
Intermediates.LodValues = GetLodValues(Intermediates.ComponentIndex);
Intermediates.LodBias = GetLodBias(Intermediates.ComponentIndex);
Intermediates.InputPosition = Input.Position;
#if LANDSCAPE_TILE
Intermediates.InputPosition = Intermediates.InputPosition + Input.TileData;
Intermediates.InputPosition.xy = min(Intermediates.InputPosition.xy, float2(Intermediates.LodValues.z, Intermediates.LodValues.z));
#endif
float2 xy = Intermediates.InputPosition.xy * Intermediates.LodValues.w;
float LODCalculated = CalcLOD(Intermediates.ComponentIndex, xy, Intermediates.InputPosition.zw);
float LodValue = floor(LODCalculated);
float MorphAlpha = LODCalculated - LodValue;
// InputPositionLODAdjusted : Position for actual LOD in base LOD units
float2 ActualLODCoordsInt = floor(Intermediates.InputPosition.xy * pow(2, -(LodValue - Intermediates.LodValues.x)));
float InvLODScaleFactor = pow(2, -LodValue);
// Base to Actual LOD, Base to Next LOD
float2 CoordTranslate = float2( LandscapeParameters.SubsectionSizeVertsLayerUVPan.x * InvLODScaleFactor - 1, max(LandscapeParameters.SubsectionSizeVertsLayerUVPan.x * 0.5f * InvLODScaleFactor, 2) - 1 ) * LandscapeParameters.SubsectionSizeVertsLayerUVPan.y;
float2 InputPositionLODAdjusted = ActualLODCoordsInt / CoordTranslate.x;
// InputPositionNextLOD : Position for next LOD in base LOD units
float2 NextLODCoordsInt = floor(ActualLODCoordsInt * 0.5);
float2 InputPositionNextLOD = NextLODCoordsInt / CoordTranslate.y;
// Get the height and normal XY for current and next LOD out of the textures
float2 SampleCoords = InputPositionLODAdjusted * LandscapeParameters.HeightmapUVScaleBias.xy + LandscapeParameters.HeightmapUVScaleBias.zw + 0.5*LandscapeParameters.HeightmapUVScaleBias.xy + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.xy;
float4 SampleValue = Texture2DSampleLevel(LandscapeParameters.HeightmapTexture, LandscapeParameters.HeightmapTextureSampler, SampleCoords, LodValue-Intermediates.LodBias.x);
float Height = DecodePackedHeight(SampleValue.xy);
float2 SampleCoordsNextLOD = InputPositionNextLOD * LandscapeParameters.HeightmapUVScaleBias.xy + LandscapeParameters.HeightmapUVScaleBias.zw + 0.5*LandscapeParameters.HeightmapUVScaleBias.xy + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.xy;
float4 SampleValueNextLOD = Texture2DSampleLevel(LandscapeParameters.HeightmapTexture, LandscapeParameters.HeightmapTextureSampler, SampleCoordsNextLOD, LodValue+1-Intermediates.LodBias.x);
float HeightNextLOD = DecodePackedHeight(SampleValueNextLOD.xy);
#if LANDSCAPE_XYOFFSET // FEATURE_LEVEL >= FEATURE_LEVEL_SM4 only
float2 SampleCoords2 = float2(InputPositionLODAdjusted * LandscapeParameters.WeightmapUVScaleBias.xy + LandscapeParameters.WeightmapUVScaleBias.zw + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.zz);
float4 OffsetValue = Texture2DSampleLevel( LandscapeParameters.XYOffsetmapTexture, LandscapeParameters.XYOffsetmapTextureSampler, SampleCoords2, LodValue- Intermediates.LodBias.y );
float2 SampleCoordsNextLOD2 = float2(InputPositionNextLOD * LandscapeParameters.WeightmapUVScaleBias.xy + LandscapeParameters.WeightmapUVScaleBias.zw + Intermediates.InputPosition.zw * LandscapeParameters.SubsectionOffsetParams.zz);
float4 OffsetValueNextLOD = Texture2DSampleLevel( LandscapeParameters.XYOffsetmapTexture, LandscapeParameters.XYOffsetmapTextureSampler, SampleCoordsNextLOD2, LodValue+1-Intermediates.LodBias.y );
float2 XYOffset = float2(((OffsetValue.r * 255.0 * 256.0 + OffsetValue.g * 255.0) - 32768.0) * XYOFFSET_SCALE, ((OffsetValue.b * 255.0 * 256.0 + OffsetValue.a * 255.0) - 32768.0) * XYOFFSET_SCALE );
float2 XYOffsetNextLOD = float2(((OffsetValueNextLOD.r * 255.0 * 256.0 + OffsetValueNextLOD.g * 255.0) - 32768.0) * XYOFFSET_SCALE, ((OffsetValueNextLOD.b * 255.0 * 256.0 + OffsetValueNextLOD.a * 255.0) - 32768.0) * XYOFFSET_SCALE );
InputPositionLODAdjusted = InputPositionLODAdjusted + XYOffset;
InputPositionNextLOD = InputPositionNextLOD + XYOffsetNextLOD;
#endif
Intermediates.LocalPosition = lerp( float3(InputPositionLODAdjusted, Height), float3(InputPositionNextLOD, HeightNextLOD), MorphAlpha );
float2 Normal = float2(SampleValue.b, SampleValue.a);
float2 NormalNextLOD = float2(SampleValueNextLOD.b, SampleValueNextLOD.a);
float2 InterpNormal = lerp( Normal, NormalNextLOD, MorphAlpha ) * float2(2.0,2.0) - float2(1.0,1.0);
Intermediates.WorldNormal = float3( InterpNormal, sqrt(max(1.0-dot(InterpNormal,InterpNormal),0.0)) );
return Intermediates;
}
/**
* Get the 3x3 tangent basis vectors for this vertex factory
* this vertex factory will calculate the binormal on-the-fly
*
* @param Input - vertex input stream structure
* @return 3x3 matrix
*/
float3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
float3x3 Result = CalcTangentBasisFromWorldNormal(Intermediates.WorldNormal);
return Result;
}
float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition)
{
return InWorldPosition;
}
float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition)
{
return TranslatedWorldPosition;
}
FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
FVertexFactoryInterpolantsVSToPS Interpolants;
Interpolants = (FVertexFactoryInterpolantsVSToPS)0;
FLandscapeTexCoords LandscapeTexCoords = GetLandscapeTexCoords(Input, Intermediates);
Interpolants.LayerTexCoord = LandscapeTexCoords.LayerTexCoord;
Interpolants.WeightHeightMapTexCoord.xy = LandscapeTexCoords.WeightMapTexCoord;
Interpolants.WeightHeightMapTexCoord.zw = LandscapeTexCoords.HeightMapTexCoord;
// Landscape material doesn't support custom vertex interpolators for now but GetMaterialCustomizedUVs is declared using NUM_TEX_COORD_INTERPOLATORS
#if NUM_TEX_COORD_INTERPOLATORS
float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS];
GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs);
#endif // NUM_TEX_COORD_INTERPOLATORS
// Landscape material only supports up to NUM_MATERIAL_TEXCOORDS 5 (0-3 are reserved for landscape-specific stuff: see UMaterialExpressionLandscapeLayerCoords, 4 is for lightmaps and 5 for heightmap coordinates,
// anything above is not supported : see GetMaterialPixelParameters) :
#if NUM_MATERIAL_TEXCOORDS
Interpolants.LayerTexCoord.xy = CustomizedUVs[0];
Interpolants.TransformedTexCoords = 0;
#if NUM_MATERIAL_TEXCOORDS > 1
Interpolants.TransformedTexCoords.xy = CustomizedUVs[1];
#if NUM_MATERIAL_TEXCOORDS > 2
Interpolants.TransformedTexCoords.zw = CustomizedUVs[2];
#endif // NUM_MATERIAL_TEXCOORDS > 2
#endif // NUM_MATERIAL_TEXCOORDS > 1
#endif // NUM_MATERIAL_TEXCOORDS
#if NEEDS_LIGHTMAP_COORDINATE
#if VF_USE_PRIMITIVE_SCENE_DATA
const uint LightmapDataIndex = GetPrimitiveData(Intermediates).LightmapDataIndex;
#else
const uint LightmapDataIndex = 0;
#endif
#if FEATURE_LEVEL == FEATURE_LEVEL_ES3_1
Interpolants.LightMapCoordinate[0] = LandscapeTexCoords.LightMapCoordinate * VF_GPUSCENE_GET_LIGHTMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).xy + VF_GPUSCENE_GET_LIGHTMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).zw;
Interpolants.LightMapCoordinate[0].y *= 0.5;
Interpolants.LightMapCoordinate[1] = Interpolants.LightMapCoordinate[0].xy;
Interpolants.LightMapCoordinate[1].y += 0.5;
#else
Interpolants.LightMapCoordinate = LandscapeTexCoords.LightMapCoordinate * VF_GPUSCENE_GET_LIGHTMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).xy + VF_GPUSCENE_GET_LIGHTMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).zw;
#endif
#if STATICLIGHTING_TEXTUREMASK
Interpolants.ShadowMapCoordinate = LandscapeTexCoords.LightMapCoordinate * VF_GPUSCENE_GET_SHADOWMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).xy + VF_GPUSCENE_GET_SHADOWMAP_UV_SCALE_BIAS(Input, LightmapDataIndex).zw;
#else
Interpolants.ShadowMapCoordinate = 0;
#endif
#endif
#if VF_USE_PRIMITIVE_SCENE_DATA
Interpolants.PrimitiveId = Intermediates.SceneData.PrimitiveId;
#endif
return Interpolants;
}
FVertexFactoryRayTracingInterpolants VertexFactoryGetRayTracingInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
FVertexFactoryRayTracingInterpolants Interpolants;
Interpolants.InterpolantsVSToPS = VertexFactoryGetInterpolantsVSToPS(Input, Intermediates, VertexParameters);
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
// Calculate LocalToTangent directly from normal map texture.
float3x3 TangentToLocal = CalcTangentBasisFromWorldNormal(Intermediates.WorldNormal);
float3x3 TangentToWorld = mul(TangentToLocal, (float3x3)LandscapeParameters.LocalToWorldNoScaling);
Interpolants.TangentToWorld0 = TangentToWorld[0];
Interpolants.TangentToWorld2 = float4(TangentToWorld[2], 1);
float2 xy = Intermediates.InputPosition.xy * Intermediates.LodValues.w;
Interpolants.CalculatedLOD = CalcLOD(Intermediates.ComponentIndex, xy, Intermediates.InputPosition.zw);
#endif
return Interpolants;
}
FVertexFactoryRayTracingInterpolants VertexFactoryInterpolate(FVertexFactoryRayTracingInterpolants a, float aInterp, FVertexFactoryRayTracingInterpolants b, float bInterp)
{
FVertexFactoryRayTracingInterpolants O;
#if NEEDS_LIGHTMAP_COORDINATE && FEATURE_LEVEL > FEATURE_LEVEL_ES3_1
INTERPOLATE_MEMBER(InterpolantsVSToPS.LightMapCoordinate);
#endif
INTERPOLATE_MEMBER(InterpolantsVSToPS.LayerTexCoord);
INTERPOLATE_MEMBER(InterpolantsVSToPS.WeightHeightMapTexCoord);
INTERPOLATE_MEMBER(InterpolantsVSToPS.TransformedTexCoords);
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
INTERPOLATE_MEMBER(TangentToWorld0);
INTERPOLATE_MEMBER(TangentToWorld2);
INTERPOLATE_MEMBER(CalculatedLOD);
#endif
#if VF_USE_PRIMITIVE_SCENE_DATA
O.InterpolantsVSToPS.PrimitiveId = a.InterpolantsVSToPS.PrimitiveId;
#endif
return O;
}
#if NUM_VF_PACKED_INTERPOLANTS > 0
void VertexFactoryPackInterpolants(inout FVertexFactoryInterpolantsVSToPS Interpolants, float4 PackedInterpolants[NUM_VF_PACKED_INTERPOLANTS])
{
Interpolants.TransformedTexCoord0.zw = PackedInterpolants[0].xy;
Interpolants.TransformedTexCoord1.zw = PackedInterpolants[0].zw;
}
void VertexFactoryUnpackInterpolants(FVertexFactoryInterpolantsVSToPS Interpolants, out float4 PackedInterpolants[NUM_VF_PACKED_INTERPOLANTS])
{
PackedInterpolants[0].xy = Interpolants.TransformedTexCoord0.zw;
PackedInterpolants[0].zw = Interpolants.TransformedTexCoord1.zw;
#if NUM_VF_PACKED_INTERPOLANTS > 1
UNROLL
for (int i = 1; i < NUM_VF_PACKED_INTERPOLANTS; ++i)
{
PackedInterpolants[i] = 0;
}
#endif
}
#endif // NUM_VF_PACKED_INTERPOLANTS > 0
float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
{
return 0;
}
uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
return Interpolants.PrimitiveId;
#else // VF_USE_PRIMITIVE_SCENE_DATA
return 0;
#endif // VF_USE_PRIMITIVE_SCENE_DATA
}
// Tell the VSM depth shader that we are supplying a constant bias
#define VF_USE_VSM_CONSTANT_BIAS 1
float VertexFactoryGetNonNaniteVirtualShadowMapConstantDepthBias()
{
return LandscapeParameters.NonNaniteVirtualShadowMapConstantDepthBias;
}
#include "VertexFactoryDefaultInterface.ush"