// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #if IS_MATERIAL_SHADER #define DISTANCE_FIELD_IN_VIEW_UB 1 #endif #if DISTANCE_FIELD_IN_VIEW_UB #define MaxGlobalDFAOConeDistance View.MaxGlobalDFAOConeDistance #else float MaxGlobalDFAOConeDistance; #endif #if PLATFORM_SUPPORTS_DISTANCE_FIELDS // Must match C++ // Most functions in this file still using 4 to match legacy #define MAX_GLOBAL_DF_CLIPMAPS 6 #if DISTANCE_FIELD_IN_VIEW_UB // for materials, these are in the view UB #define GlobalDistanceFieldPageAtlasTexture View.GlobalDistanceFieldPageAtlasTexture #define GlobalDistanceFieldCoverageAtlasTexture View.GlobalDistanceFieldCoverageAtlasTexture #define GlobalDistanceFieldPageTableTexture View.GlobalDistanceFieldPageTableTexture #define GlobalDistanceFieldMipTexture View.GlobalDistanceFieldMipTexture #define GlobalVolumeTranslatedCenterAndExtent View.GlobalVolumeTranslatedCenterAndExtent #define GlobalVolumeTranslatedWorldToUVAddAndMul View.GlobalVolumeTranslatedWorldToUVAddAndMul #define GlobalDistanceFieldMipTranslatedWorldToUVScale View.GlobalDistanceFieldMipTranslatedWorldToUVScale #define GlobalDistanceFieldMipTranslatedWorldToUVBias View.GlobalDistanceFieldMipTranslatedWorldToUVBias #define GlobalDistanceFieldMipUVMinZ View.GlobalDistanceFieldMipUVMinZ #define GlobalDistanceFieldMipUVMaxZ View.GlobalDistanceFieldMipUVMaxZ #define GlobalDistanceFieldMipFactor View.GlobalDistanceFieldMipFactor #define GlobalDistanceFieldMipTransition View.GlobalDistanceFieldMipTransition #define GlobalDistanceFieldInvPageAtlasSize View.GlobalDistanceFieldInvPageAtlasSize #define GlobalDistanceFieldInvCoverageAtlasSize View.GlobalDistanceFieldInvCoverageAtlasSize #define GlobalDistanceFieldClipmapSizeInPages View.GlobalDistanceFieldClipmapSizeInPages #define GlobalVolumeDimension View.GlobalVolumeDimension #define GlobalVolumeTexelSize View.GlobalVolumeTexelSize #define NumGlobalSDFClipmaps View.NumGlobalSDFClipmaps #define CoveredExpandSurfaceScale View.CoveredExpandSurfaceScale #define NotCoveredExpandSurfaceScale View.NotCoveredExpandSurfaceScale #define NotCoveredMinStepScale View.NotCoveredMinStepScale #define DitheredTransparencyStepThreshold View.DitheredTransparencyStepThreshold #define DitheredTransparencyTraceThreshold View.DitheredTransparencyTraceThreshold #else // these are only used for the precomputation shaders; which don't have a view UB Texture3D GlobalDistanceFieldPageAtlasTexture; Texture3D GlobalDistanceFieldCoverageAtlasTexture; Texture3D GlobalDistanceFieldPageTableTexture; Texture3D GlobalDistanceFieldMipTexture; float4 GlobalVolumeTranslatedCenterAndExtent[MAX_GLOBAL_DF_CLIPMAPS]; float4 GlobalVolumeTranslatedWorldToUVAddAndMul[MAX_GLOBAL_DF_CLIPMAPS]; float4 GlobalDistanceFieldMipTranslatedWorldToUVScale[MAX_GLOBAL_DF_CLIPMAPS]; float4 GlobalDistanceFieldMipTranslatedWorldToUVBias[MAX_GLOBAL_DF_CLIPMAPS]; float GlobalDistanceFieldMipFactor; float GlobalDistanceFieldMipTransition; float3 GlobalDistanceFieldInvPageAtlasSize; float3 GlobalDistanceFieldInvCoverageAtlasSize; uint GlobalDistanceFieldClipmapSizeInPages; float GlobalVolumeDimension; float GlobalVolumeTexelSize; uint NumGlobalSDFClipmaps; float CoveredExpandSurfaceScale; float NotCoveredExpandSurfaceScale; float NotCoveredMinStepScale; float DitheredTransparencyStepThreshold; float DitheredTransparencyTraceThreshold; #endif #if SUPPORTS_INDEPENDENT_SAMPLERS #define GlobalDistanceFieldCoverageAtlasTextureSampler GlobalTrilinearWrappedSampler #define GlobalDistanceFieldPageAtlasTextureSampler GlobalTrilinearWrappedSampler #define GlobalDistanceFieldMipTextureSampler GlobalTrilinearClampedSampler #else #if DISTANCE_FIELD_IN_VIEW_UB #define GlobalDistanceFieldCoverageAtlasTextureSampler View.GlobalDistanceFieldCoverageAtlasTextureSampler #define GlobalDistanceFieldPageAtlasTextureSampler View.GlobalDistanceFieldPageAtlasTextureSampler #define GlobalDistanceFieldMipTextureSampler View.GlobalDistanceFieldMipTextureSampler #else SamplerState GlobalDistanceFieldCoverageAtlasTextureSampler; SamplerState GlobalDistanceFieldPageAtlasTextureSampler; SamplerState GlobalDistanceFieldMipTextureSampler; #endif #endif // Must match GlobalDistanceField.cpp #define GLOBAL_DISTANCE_FIELD_PAGE_BORDER 0.5f #define GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION_IN_ATLAS 8 // Includes 0.5 texel trilinear filter margin #define GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION (GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION_IN_ATLAS - 1) #define GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_X 128 #define GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_Y 128 #define GLOBAL_DISTANCE_FIELD_INFLUENCE_RANGE_IN_VOXELS 4 #define GLOBAL_DISTANCE_FIELD_INVALID_PAGE_ID 0xFFFFFFFF #define GLOBAL_DISTANCE_FIELD_PAGE_COVERAGE_BIT 0x80000000 // Coverage is a payload layer alongside the SDF #define GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION_IN_ATLAS 4 // Includes 0.5 texel trilinear filter margin #define GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION (GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION_IN_ATLAS - 1) #define GLOBAL_DISTANCE_FIELD_COVERAGE_DOWNSAMPLE_FACTOR (GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION_IN_ATLAS / GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION_IN_ATLAS) struct FGlobalDistanceFieldPage { uint PageIndex; bool bValid; bool bCoverage; }; uint3 GlobalDistanceFieldPageLinearIndexToPageAtlasOffset(FGlobalDistanceFieldPage Page) { uint3 PageAtlasOffset; //PageAtlasOffset.x = (Page.PageIndex % GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_X); //PageAtlasOffset.y = ((Page.PageIndex / GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_X) % GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_Y); //PageAtlasOffset.z = ((Page.PageIndex / GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_X) / GLOBAL_DISTANCE_FIELD_PAGE_ATLAS_SIZE_IN_PAGES_Y); // Same as above, but with bit operations PageAtlasOffset.x = Page.PageIndex & 0x7F; PageAtlasOffset.y = (Page.PageIndex >> 7) & 0x7F; PageAtlasOffset.z = Page.PageIndex >> 14; return PageAtlasOffset; } uint PackGlobalDistanceFieldPage(FGlobalDistanceFieldPage Page) { uint PackedPage = GLOBAL_DISTANCE_FIELD_INVALID_PAGE_ID; if (Page.bValid) { PackedPage = Page.PageIndex & 0x00FFFFFF; PackedPage |= Page.bCoverage ? GLOBAL_DISTANCE_FIELD_PAGE_COVERAGE_BIT : 0; } return PackedPage; } FGlobalDistanceFieldPage UnpackGlobalDistanceFieldPage(uint PackedPage) { FGlobalDistanceFieldPage Page; Page.PageIndex = PackedPage & 0x00FFFFFF; Page.bCoverage = PackedPage & GLOBAL_DISTANCE_FIELD_PAGE_COVERAGE_BIT; Page.bValid = PackedPage < GLOBAL_DISTANCE_FIELD_INVALID_PAGE_ID; return Page; } FGlobalDistanceFieldPage GetGlobalDistanceFieldPage(float3 VolumeUV, uint ClipmapIndex) { int4 PageTableCoord = int4(saturate(VolumeUV) * GlobalDistanceFieldClipmapSizeInPages + int3(0, 0, ClipmapIndex * GlobalDistanceFieldClipmapSizeInPages), 0); uint PackedPage = GlobalDistanceFieldPageTableTexture.Load(PageTableCoord); return UnpackGlobalDistanceFieldPage(PackedPage); } void ComputeGlobalDistanceFieldPageUV(float3 VolumeUV, FGlobalDistanceFieldPage Page, out float3 OutPageUV, out float3 OutCoveragePageUV) { uint3 PageAtlasOffset = GlobalDistanceFieldPageLinearIndexToPageAtlasOffset(Page); float3 VolumePageUV = frac(VolumeUV * GlobalDistanceFieldClipmapSizeInPages); float3 PageAtlasCoord = PageAtlasOffset * GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION_IN_ATLAS + VolumePageUV * GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION + 0.5f; OutPageUV = PageAtlasCoord * GlobalDistanceFieldInvPageAtlasSize; float3 CoveragePageAtlasCoord = PageAtlasOffset * GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION_IN_ATLAS + VolumePageUV * GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION + 0.5f; OutCoveragePageUV = CoveragePageAtlasCoord * GlobalDistanceFieldInvCoverageAtlasSize; } float3 ComputeGlobalDistanceFieldPageUV(float3 VolumeUV, FGlobalDistanceFieldPage Page) { uint3 PageAtlasOffset = GlobalDistanceFieldPageLinearIndexToPageAtlasOffset(Page); float3 VolumePageUV = frac(VolumeUV * GlobalDistanceFieldClipmapSizeInPages); float3 PageAtlasCoord = PageAtlasOffset * GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION_IN_ATLAS + VolumePageUV * GLOBAL_DISTANCE_FIELD_PAGE_RESOLUTION + 0.5f; float3 PageUV = PageAtlasCoord * GlobalDistanceFieldInvPageAtlasSize; return PageUV; } float3 ComputeGlobalDistanceFieldCoveragePageUV(float3 VolumeUV, FGlobalDistanceFieldPage Page) { uint3 PageAtlasOffset = GlobalDistanceFieldPageLinearIndexToPageAtlasOffset(Page); float3 VolumePageUV = frac(VolumeUV * GlobalDistanceFieldClipmapSizeInPages); float3 CoveragePageAtlasCoord = PageAtlasOffset * GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION_IN_ATLAS + VolumePageUV * GLOBAL_DISTANCE_FIELD_COVERAGE_PAGE_RESOLUTION + 0.5f; float3 CoveragePageUV = CoveragePageAtlasCoord * GlobalDistanceFieldInvCoverageAtlasSize; return CoveragePageUV; } float EncodeGlobalDistanceFieldPageDistance(float Distance, float ClipmapInfluenceRange) { return saturate(Distance / (2.0f * ClipmapInfluenceRange) + 0.5f); } float DecodeGlobalDistanceFieldPageDistance(float EncodedDistance, float ClipmapInfluenceRange) { return (EncodedDistance * 2.0f - 1.0f) * ClipmapInfluenceRange; } float3 ComputeGlobalUV(float3 TranslatedWorldPosition, uint ClipmapIndex) { //return ((TranslatedWorldPosition - GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].xyz + GlobalVolumeScollOffset[ClipmapIndex].xyz) / (GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].w * 2) + .5f); float4 TranslatedWorldToUVAddAndMul = GlobalVolumeTranslatedWorldToUVAddAndMul[ClipmapIndex]; float3 UV = frac(TranslatedWorldPosition * TranslatedWorldToUVAddAndMul.www + TranslatedWorldToUVAddAndMul.xyz); // wraparound addressing UV = frac(UV); // apply frac twice to prevent UV == 1.0f because frac(-0.00...001f) = 1.0f return UV; } float3 ComputeGlobalMipUV(float3 TranslatedWorldPosition, uint ClipmapIndex) { float3 MipUV = saturate(TranslatedWorldPosition * GlobalDistanceFieldMipTranslatedWorldToUVScale[ClipmapIndex].xyz + GlobalDistanceFieldMipTranslatedWorldToUVBias[ClipmapIndex].xyz); // Clamp MipUV.z to a valid bilinear region to prevent filtering across clipmaps float MipUVMinZ = GlobalDistanceFieldMipTranslatedWorldToUVScale[ClipmapIndex].w; float MipUVMaxZ = GlobalDistanceFieldMipTranslatedWorldToUVBias[ClipmapIndex].w; MipUV.z = clamp(MipUV.z, MipUVMinZ, MipUVMaxZ); return MipUV; } float GetDistanceToNearestSurfaceGlobal(float3 TranslatedWorldPosition) { float Distance = 0.0f; for (uint ClipmapIndex = 0; ClipmapIndex < NumGlobalSDFClipmaps; ClipmapIndex++) { float DistanceFromClipmap = ComputeDistanceFromBoxToPointInside(GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].xyz, GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].www, TranslatedWorldPosition); const float ClipmapVoxelExtent = GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].w * GlobalVolumeTexelSize; const float ClipmapInfluenceRange = 2.0f * GLOBAL_DISTANCE_FIELD_INFLUENCE_RANGE_IN_VOXELS * ClipmapVoxelExtent; Distance = ClipmapInfluenceRange; if (DistanceFromClipmap > ClipmapVoxelExtent) { float3 ClipmapVolumeUV = ComputeGlobalUV(TranslatedWorldPosition, ClipmapIndex); FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, ClipmapIndex); if (Page.bValid) { float3 PageUV = ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page); float DistanceFieldValue = Texture3DSampleLevel(GlobalDistanceFieldPageAtlasTexture, GlobalDistanceFieldPageAtlasTextureSampler, PageUV, 0).x; if (DistanceFieldValue < 1.0f) { Distance = DecodeGlobalDistanceFieldPageDistance(DistanceFieldValue, ClipmapInfluenceRange); break; } } } } return Distance; } float SampleGlobalDistanceField(float3 TranslatedWorldPosition, float MaxDistance, int MinClipmapIndex) { for (uint ClipmapIndex = MinClipmapIndex; ClipmapIndex < NumGlobalSDFClipmaps; ++ClipmapIndex) { const float ClipmapInfluenceRange = GLOBAL_DISTANCE_FIELD_INFLUENCE_RANGE_IN_VOXELS * 2.0f * GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].w * GlobalVolumeTexelSize; float3 ClipmapVolumeUV = ComputeGlobalUV(TranslatedWorldPosition, ClipmapIndex); FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, ClipmapIndex); if (Page.bValid) { float3 PageUV = ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page); float DistanceFieldValue = Texture3DSampleLevel(GlobalDistanceFieldPageAtlasTexture, GlobalDistanceFieldPageAtlasTextureSampler, PageUV, 0).x; if (DistanceFieldValue < 1.0f) { return DecodeGlobalDistanceFieldPageDistance(DistanceFieldValue, ClipmapInfluenceRange); } } } return MaxDistance; } float GlobalDistanceFieldSampleClipmap(float3 ClipmapVolumeUV, uint ClipmapIndex) { float DistanceFieldValue = 1.0f; // Wrap UV sampling ClipmapVolumeUV = frac(ClipmapVolumeUV); FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, ClipmapIndex); if (Page.bValid) { float3 PageUV = ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page); DistanceFieldValue = Texture3DSampleLevel(GlobalDistanceFieldPageAtlasTexture, GlobalDistanceFieldPageAtlasTextureSampler, PageUV, 0).x; } return DistanceFieldValue; } float3 GlobalDistanceFieldPageCentralDiff(float3 ClipmapVolumeUV, uint ClipmapIndex) { float3 TexelOffset = 0.5f * GlobalVolumeTexelSize; float R = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(+TexelOffset.x, 0, 0), ClipmapIndex); float L = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(-TexelOffset.x, 0, 0), ClipmapIndex); float F = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(0, +TexelOffset.y, 0), ClipmapIndex); float B = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(0, -TexelOffset.y, 0), ClipmapIndex); float U = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(0, 0, +TexelOffset.z), ClipmapIndex); float D = GlobalDistanceFieldSampleClipmap(ClipmapVolumeUV + float3(0, 0, -TexelOffset.z), ClipmapIndex); return float3(R - L, F - B, U - D); } float3 ComputeGlobalDistanceFieldNormal(float3 SampleTranslatedWorldPosition, uint ClipmapIndex, float3 FallbackNormal) { float3 ClipmapVolumeUV = ComputeGlobalUV(SampleTranslatedWorldPosition, ClipmapIndex); float3 DistanceFieldGradient = GlobalDistanceFieldPageCentralDiff(ClipmapVolumeUV, ClipmapIndex); float DistanceFieldGradientLength = length(DistanceFieldGradient); float3 DistanceFieldNormal = DistanceFieldGradientLength > 0.001f ? DistanceFieldGradient / DistanceFieldGradientLength : FallbackNormal; return DistanceFieldNormal; } // Returns vector (unnormalized) to the nearest surface point. // This vector is rescaled by 128 * GlobalVolumeTexelSize for backwards compatiblity. float3 GetDistanceFieldGradientGlobal(float3 TranslatedWorldPosition) { float3 Gradient = float3(0, 0, 0.001f); for (uint ClipmapIndex = 0; ClipmapIndex < NumGlobalSDFClipmaps; ClipmapIndex++) { const float ClipmapVoxelExtent = GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].w * GlobalVolumeTexelSize; const float DistanceFromClipmap = ComputeDistanceFromBoxToPointInside(GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].xyz, GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].www, TranslatedWorldPosition); if (DistanceFromClipmap >= ClipmapVoxelExtent) { const float3 ClipmapVolumeUV = ComputeGlobalUV(TranslatedWorldPosition, ClipmapIndex); FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, ClipmapIndex); if (Page.bValid) { float3 PageUV = ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page); float DistanceFieldValue = Texture3DSampleLevel(GlobalDistanceFieldPageAtlasTexture, GlobalDistanceFieldPageAtlasTextureSampler, PageUV, 0).x; // Find a page which where narrow band isn't clipped if (DistanceFieldValue < 0.9f) { float3 PageCentralDiff = GlobalDistanceFieldPageCentralDiff(ClipmapVolumeUV, ClipmapIndex); const float ClipmapInfluenceRange = GLOBAL_DISTANCE_FIELD_INFLUENCE_RANGE_IN_VOXELS * 2.0f * ClipmapVoxelExtent; const float PageDistanceDecodeFactor = 2.0f * ClipmapInfluenceRange; float ClipmapExtent = GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].w; // Rescale to keep backwards compatability const float BackwardsCompatibilityFactor = 128.0f * GlobalVolumeTexelSize; Gradient = PageDistanceDecodeFactor * PageCentralDiff / (ClipmapExtent * BackwardsCompatibilityFactor); break; } } } } return Gradient; } float CalculateDistanceFieldApproxAO(float3 TranslatedWorldPosition, float3 WorldNormal, uint NumSteps, float StepDistance, float StepScale, float DistanceBias, float MaxDistance) { // determine the min clipmap that covers all the sampling points (sphere centered on TranslatedWorldPosition with radius = MaxDistance) int MinClipmapIndex = -1; for (uint ClipmapIndex = 0; ClipmapIndex < NumGlobalSDFClipmaps; ++ClipmapIndex) { float DistanceFromClipmap = ComputeDistanceFromBoxToPointInside(GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].xyz, GlobalVolumeTranslatedCenterAndExtent[ClipmapIndex].www, TranslatedWorldPosition); if (DistanceFromClipmap > MaxDistance) { MinClipmapIndex = ClipmapIndex; break; } } if (MinClipmapIndex < 0) { return 1.0f; } float Occlusion = 0.0f; float W = 0.5f; float TotalW = 0.0f; for (uint StepIndex = 0; StepIndex < NumSteps; ++StepIndex) { const float CurrentDistance = StepDistance + DistanceBias; const float Dist = max(0.0f, SampleGlobalDistanceField(TranslatedWorldPosition + WorldNormal * CurrentDistance, CurrentDistance, MinClipmapIndex)); Occlusion += W * Dist / CurrentDistance; TotalW += W; W *= 0.5f; StepDistance *= StepScale; } return saturate(Occlusion / TotalW); } #else float GetDistanceToNearestSurfaceGlobal(float3 TranslatedWorldPosition) { return MaxGlobalDFAOConeDistance; } float3 GetDistanceFieldGradientGlobal(float3 TranslatedWorldPosition) { return float3(0, 0, .001f); } float CalculateDistanceFieldApproxAO(float3 TranslatedWorldPosition, float3 WorldNormal, uint NumSteps, float StepDistance, float StepScale, float DistanceBias, float MaxDistance) { return 1.0f; } #endif float GetDistanceToNearestSurfaceGlobal(FDFVector3 WorldPosition) { const float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldPosition, ResolvedView.PreViewTranslation); return GetDistanceToNearestSurfaceGlobal(TranslatedWorldPosition); } float GetDistanceToNearestSurfaceGlobal(FLWCVector3 WorldPosition) { return GetDistanceToNearestSurfaceGlobal(DFFromTileOffset(WorldPosition)); } float3 GetDistanceFieldGradientGlobal(FDFVector3 WorldPosition) { const float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldPosition, ResolvedView.PreViewTranslation); return GetDistanceFieldGradientGlobal(TranslatedWorldPosition); } float3 GetDistanceFieldGradientGlobal(FLWCVector3 WorldPosition) { return GetDistanceFieldGradientGlobal(DFFromTileOffset(WorldPosition)); } float CalculateDistanceFieldApproxAO(FDFVector3 WorldPosition, float3 WorldNormal, uint NumSteps, float StepDistance, float StepScale, float DistanceBias, float MaxDistance) { const float3 TranslatedWorldPosition = DFFastToTranslatedWorld(WorldPosition, ResolvedView.PreViewTranslation); return CalculateDistanceFieldApproxAO(TranslatedWorldPosition, WorldNormal, NumSteps, StepDistance, StepScale, DistanceBias, MaxDistance); } float CalculateDistanceFieldApproxAO(FLWCVector3 WorldPosition, float3 WorldNormal, uint NumSteps, float StepDistance, float StepScale, float DistanceBias, float MaxDistance) { const float3 TranslatedWorldPosition = LWCToFloat(LWCAdd(WorldPosition, ResolvedView.TileOffset.PreViewTranslation)); return CalculateDistanceFieldApproxAO(TranslatedWorldPosition, WorldNormal, NumSteps, StepDistance, StepScale, DistanceBias, MaxDistance); }