Lumen:Radiosity

DistanceFieldTracing

[numthreads( 64 , 1, 1)]
void LumenRadiosityDistanceFieldTracingCS(
	uint DispatchThreadId : SV_DispatchThreadID)
{
	uint CardTileIndex;
	uint2 CoordInCardTile;
	uint2 TraceTexelCoord;
	UnswizzleTexelTraceCoords(DispatchThreadId, CardTileIndex, CoordInCardTile, TraceTexelCoord);

	FRadiosityTexel RadiosityTexel = GetRadiosityTexelFromCardTile(CardTileIndex, CoordInCardTile);

	if (RadiosityTexel.bInsideAtlas)
	{
		float3 Radiance = 0.0f;
		float TraceHitDistance = MaxTraceDistance;

		if (RadiosityTexel.bValid)
		{
			float3 WorldPosition = RadiosityTexel.WorldPosition;
			float3 WorldNormal = RadiosityTexel.WorldNormal;

			float3 WorldRayDirection;
			float ConeHalfAngle;
			float PDF;
			GetRadiosityRay(RadiosityTexel, RadiosityTexel.CardCoord >> ProbeSpacingInRadiosityTexelsDivideShift, TraceTexelCoord, WorldRayDirection, ConeHalfAngle, PDF);


			WorldPosition += WorldNormal * SurfaceBias;

			float VoxelTraceStartDistance = CalculateVoxelTraceStartDistance(MinTraceDistance, MaxTraceDistance, MaxMeshSDFTraceDistance, false);


			float3 SamplePosition = WorldPosition + SurfaceBias * WorldRayDirection;

			float3 TranslatedSamplePosition = WorldPosition +  LWCToFloat( GetPrimaryView() .PreViewTranslation) ;

			FConeTraceInput TraceInput;
			TraceInput.Setup(SamplePosition, TranslatedSamplePosition, WorldRayDirection, ConeHalfAngle, 0, MinTraceDistance, MaxTraceDistance, 1);
			TraceInput.VoxelTraceStartDistance = VoxelTraceStartDistance;
			TraceInput.SDFStepFactor = 1;
			TraceInput.DitherScreenCoord = (RadiosityTexel.CardCoord >> ProbeSpacingInRadiosityTexelsDivideShift) + TraceTexelCoord;

			FConeTraceResult TraceResult = (FConeTraceResult)0;
			TraceResult.Transparency = 1;


			{
				RayTraceGlobalDistanceField(TraceInput, TraceResult);
			}


			if (TraceResult.Transparency < 0.5f)
			{
				Radiance = TraceResult.Lighting;



				float3 HitPosition = SamplePosition + WorldRayDirection * (TraceResult.OpaqueHitDistance + TraceResult.ExpandSurfaceAmount);
				TraceHitDistance = length(RadiosityTexel.WorldPosition - HitPosition);
			}
			else
			{
				Radiance = EvaluateSkyRadiance(WorldRayDirection);
			}

			float MaxLighting = max3(Radiance.x, Radiance.y, Radiance.z);

			if (MaxLighting > MaxRayIntensity * View_OneOverPreExposure)
			{
				Radiance *= MaxRayIntensity * View_OneOverPreExposure / MaxLighting;
			}
		}

		FCardTileData CardTile = GetCardTile(CardTileIndex);
		FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
		uint2 RadiosityProbeTracingAtlasCoord = GetRadiosityProbeAtlasCoord(CardPage, CardTile, CoordInCardTile) * HemisphereProbeResolution + TraceTexelCoord;
		RWTraceRadianceAtlas[RadiosityProbeTracingAtlasCoord] = Radiance;

		if (UseProbeOcclusion > 0)
		{
			RWTraceHitDistanceAtlas[RadiosityProbeTracingAtlasCoord] = TraceHitDistance;
		}
	}
}
void RayTraceGlobalDistanceField(
	FConeTraceInput TraceInput,
	inout FConeTraceResult OutResult)
{
	FGlobalSDFTraceResult SDFTraceResult;


	{
		FGlobalSDFTraceInput SDFTraceInput = SetupGlobalSDFTraceInput(TraceInput.ConeOrigin, TraceInput.ConeDirection, TraceInput.MinTraceDistance, TraceInput.MaxTraceDistance, TraceInput.SDFStepFactor, TraceInput.MinSDFStepFactor);
		SDFTraceInput.bDitheredTransparency = TraceInput.bDitheredTransparency;
		SDFTraceInput.DitherScreenCoord = TraceInput.DitherScreenCoord;
		SDFTraceInput.bExpandSurfaceUsingRayTimeInsteadOfMaxDistance = TraceInput.bExpandSurfaceUsingRayTimeInsteadOfMaxDistance;
		SDFTraceInput.InitialMaxDistance = TraceInput.InitialMaxDistance;

		SDFTraceResult = RayTraceGlobalDistanceField(SDFTraceInput);
	}

	float4 LightingAndAlpha = float4(0, 0, 0, 1);

	if (GlobalSDFTraceResultIsHit(SDFTraceResult))
	{
		LightingAndAlpha = EvaluateGlobalDistanceFieldHit(TraceInput, SDFTraceResult);
	}

	OutResult = (FConeTraceResult)0;

		OutResult.Lighting = LightingAndAlpha.rgb;

	OutResult.Transparency = LightingAndAlpha.a;
	OutResult.NumSteps = SDFTraceResult.TotalStepsTaken;
	OutResult.OpaqueHitDistance = GlobalSDFTraceResultIsHit(SDFTraceResult) ? SDFTraceResult.HitTime : TraceInput.MaxTraceDistance;
	OutResult.ExpandSurfaceAmount = SDFTraceResult.ExpandSurfaceAmount;
}
FGlobalSDFTraceResult RayTraceGlobalDistanceField(FGlobalSDFTraceInput TraceInput)
{
	FGlobalSDFTraceResult TraceResult;
	TraceResult.HitTime = -1.0f;
	TraceResult.HitClipmapIndex = 0;
	TraceResult.TotalStepsTaken = 0;
	TraceResult.ExpandSurfaceAmount = 0;

	float TraceNoise = InterleavedGradientNoise(TraceInput.DitherScreenCoord.xy, View_StateFrameIndexMod8);
	uint MinClipmapIndex = ComputeGlobalDistanceFieldClipmapIndex(TraceInput.WorldRayStart + TraceInput.MinTraceDistance * TraceInput.WorldRayDirection);
	float MaxDistance = TraceInput.InitialMaxDistance;
	float MinRayTime = TraceInput.MinTraceDistance;

	[loop]
	for (uint ClipmapIndex = MinClipmapIndex; ClipmapIndex <  View_NumGlobalSDFClipmaps  && TraceResult.HitTime < 0.0f; ++ClipmapIndex)
	{
		float ClipmapVoxelExtent =  View_GlobalVolumeCenterAndExtent [ClipmapIndex].w *  View_GlobalVolumeTexelSize ;
		float MinStepSize = TraceInput.MinStepFactor * ClipmapVoxelExtent;
		float ExpandSurfaceDistance = ClipmapVoxelExtent;
		float ClipmapRayBias = ClipmapVoxelExtent * TraceInput.VoxelSizeRelativeBias;
		float ClipmapRayLength = TraceInput.MaxTraceDistance - ClipmapVoxelExtent * TraceInput.VoxelSizeRelativeRayEndBias;

		float3 GlobalVolumeCenter =  View_GlobalVolumeCenterAndExtent [ClipmapIndex].xyz;
		float GlobalVolumeExtent =  View_GlobalVolumeCenterAndExtent [ClipmapIndex].w - ClipmapVoxelExtent;
		float3 WorldRayEnd = TraceInput.WorldRayStart + TraceInput.WorldRayDirection * ClipmapRayLength;
		float2 IntersectionTimes = LineBoxIntersect(TraceInput.WorldRayStart, WorldRayEnd, GlobalVolumeCenter - GlobalVolumeExtent.xxx, GlobalVolumeCenter + GlobalVolumeExtent.xxx);
		IntersectionTimes.xy *= ClipmapRayLength;
		IntersectionTimes.x = max(IntersectionTimes.x, MinRayTime);
		IntersectionTimes.x = max(IntersectionTimes.x, ClipmapRayBias);

		if (IntersectionTimes.x < IntersectionTimes.y)
		{

			MinRayTime = IntersectionTimes.y;

			float SampleRayTime = IntersectionTimes.x;
			const float ClipmapInfluenceRange =  4  * 2.0f *  View_GlobalVolumeCenterAndExtent [ClipmapIndex].w *  View_GlobalVolumeTexelSize ;

			uint StepIndex = 0;
			const uint MaxSteps = 256;

			[loop]
			for (; StepIndex < MaxSteps; ++StepIndex)
			{
				float3 SampleWorldPosition = TraceInput.WorldRayStart + TraceInput.WorldRayDirection * SampleRayTime;

				float3 ClipmapVolumeUV = ComputeGlobalUV(SampleWorldPosition, ClipmapIndex);
				float3 MipUV = ComputeGlobalMipUV(SampleWorldPosition, ClipmapIndex);

				float DistanceFieldMipValue = Texture3DSampleLevel( View_GlobalDistanceFieldMipTexture ,  View_SharedTrilinearClampedSampler , MipUV, 0).x;
				float DistanceField = DecodeGlobalDistanceFieldPageDistance(DistanceFieldMipValue,  View_GlobalDistanceFieldMipFactor  * ClipmapInfluenceRange);
				float Coverage = 1;
				FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, ClipmapIndex);

				if (Page.bValid && DistanceFieldMipValue <  View_GlobalDistanceFieldMipTransition )
				{
					float3 PageUV = ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page);


					if (Page.bCoverage)
					{
						float3 CoveragePageUV;
						ComputeGlobalDistanceFieldPageUV(ClipmapVolumeUV, Page, PageUV, CoveragePageUV);
						Coverage = Texture3DSampleLevel( View_GlobalDistanceFieldCoverageAtlasTexture ,  View_SharedTrilinearWrappedSampler , CoveragePageUV, 0).x;
					}


					float DistanceFieldValue = Texture3DSampleLevel( View_GlobalDistanceFieldPageAtlasTexture ,  View_SharedTrilinearWrappedSampler , PageUV, 0).x;
					DistanceField = DecodeGlobalDistanceFieldPageDistance(DistanceFieldValue, ClipmapInfluenceRange);
				}

				MaxDistance = max(DistanceField, MaxDistance);

				float ExpandSurfaceTime = TraceInput.bExpandSurfaceUsingRayTimeInsteadOfMaxDistance ? SampleRayTime - ClipmapRayBias : MaxDistance;
				float ExpandSurfaceScale = lerp( View_NotCoveredExpandSurfaceScale ,  View_CoveredExpandSurfaceScale , Coverage);

				const float ExpandSurfaceFalloff = 2.0f * ExpandSurfaceDistance;
				const float ExpandSurfaceAmount = ExpandSurfaceDistance * saturate(ExpandSurfaceTime / ExpandSurfaceFalloff) * ExpandSurfaceScale;

				float StepNoise = InterleavedGradientNoise(TraceInput.DitherScreenCoord.xy, View_StateFrameIndexMod8 * MaxSteps + StepIndex);

				if (DistanceField < ExpandSurfaceAmount
					&& (!TraceInput.bDitheredTransparency || (StepNoise * (1 - Coverage) <=  View_DitheredTransparencyStepThreshold  && TraceNoise * (1 - Coverage) <=  View_DitheredTransparencyTraceThreshold )))
				{
					TraceResult.HitTime = max(SampleRayTime + DistanceField - ExpandSurfaceAmount, 0.0f);
					TraceResult.HitClipmapIndex = ClipmapIndex;
					TraceResult.ExpandSurfaceAmount = ExpandSurfaceAmount;
					break;
				}

				float LocalMinStepSize = MinStepSize * lerp( View_NotCoveredMinStepScale , 1.0f, Coverage);
				float StepDistance = max(DistanceField * TraceInput.StepFactor, LocalMinStepSize);
				SampleRayTime += StepDistance;


				if (SampleRayTime > IntersectionTimes.y || TraceResult.HitTime >= 0.0f)
				{
					break;
				}
			}

			TraceResult.TotalStepsTaken += StepIndex;
		}
	}

	return TraceResult;
}
float4 EvaluateGlobalDistanceFieldHit(FConeTraceInput TraceInput, FGlobalSDFTraceResult SDFTraceResult)
{
	float3 SampleWorldPosition = TraceInput.ConeOrigin + TraceInput.ConeDirection * SDFTraceResult.HitTime;
	float3 SampleWorldNormal = ComputeGlobalDistanceFieldNormal(SampleWorldPosition, SDFTraceResult.HitClipmapIndex, -TraceInput.ConeDirection);
	float ClipmapVoxelExtent =  View_GlobalVolumeCenterAndExtent [SDFTraceResult.HitClipmapIndex].w *  View_GlobalVolumeTexelSize ;

	float RadianceFactor = 1.0f;
	if (TraceInput.bExpandSurfaceUsingRayTimeInsteadOfMaxDistance)
	{

		RadianceFactor = smoothstep(1.5f * ClipmapVoxelExtent, 2.0f * ClipmapVoxelExtent, SDFTraceResult.HitTime);
	}

	if (TraceInput.bZeroRadianceIfRayStartsInsideGeometry && SDFTraceResult.HitTime <= TraceInput.MinTraceDistance)
	{
		RadianceFactor = 0.0f;
	}

	float3 LightingSum = 0.0f;
	float SampleWeightSum = 0.0f;

	float3 ClipmapVolumeUV = ComputeGlobalUV(SampleWorldPosition, SDFTraceResult.HitClipmapIndex);
	FGlobalDistanceFieldPage Page = GetGlobalDistanceFieldPage(ClipmapVolumeUV, SDFTraceResult.HitClipmapIndex);
	if (RadianceFactor > 0.0f && Page.bValid)
	{
		float3 PageTableCoord = saturate(ClipmapVolumeUV) *  View_GlobalDistanceFieldClipmapSizeInPages ;
		uint3 CellCoordInPage = frac(frac(PageTableCoord)) *  4 ;
		uint CellOffsetInPage = ZOrder3DEncode(CellCoordInPage, log2( 4 ));

		uint4 DistanceFieldObjectGridCell = GlobalDistanceFieldPageObjectGridBuffer[ ( 4 * 4 * 4 )  * Page.PageIndex + CellOffsetInPage];

		for (uint ObjectIndexInList = 0; ObjectIndexInList <  4 ; ++ObjectIndexInList)
		{
			FObjectGridCellIndex GridCellIndex = UnpackObjectGridCellIndex(DistanceFieldObjectGridCell[ObjectIndexInList]);
			if (GridCellIndex.bValid)
			{
				uint MeshCardsIndex = GetMeshCardsIndexFromSceneInstanceIndex(GridCellIndex.GPUSceneInstanceIndex);
				if (MeshCardsIndex < LumenCardScene_NumMeshCards)
				{
					uint2 ScreenCoord = TraceInput.DitherScreenCoord;
					float SurfaceCacheBias =  2.0f  * ClipmapVoxelExtent;
					float SampleRadius = 100.0f;

					FSurfaceCacheSample SurfaceCacheSample = SampleLumenMeshCards(
						ScreenCoord,
						MeshCardsIndex,
						SampleWorldPosition,
						SampleWorldNormal,
						SampleRadius,
						SurfaceCacheBias,
						false,
						0
					);

					LightingSum += SurfaceCacheSample.LightingSum;
					SampleWeightSum += SurfaceCacheSample.SampleWeightSum;

					if (SampleWeightSum >= 0.9f)
					{
						break;
					}
				}
			}
			else
			{
				break;
			}
		}

	}

	float4 LightingAndAlpha = 0.0f;

	if (SampleWeightSum > 0.0f)
	{
		LightingAndAlpha.xyz = RadianceFactor * (LightingSum / SampleWeightSum);
	}

	return LightingAndAlpha;
}
FSurfaceCacheSample SampleLumenMeshCards(
	uint2 ScreenCoord,
	uint MeshCardsIndex,
	float3 WorldSpacePosition,
	float3 WorldSpaceNormal,
	float SampleRadius,
	float SurfaceCacheBias,
	bool bHiResSurface,
	uint AtlasId)
{
	FSurfaceCacheSample SurfaceCacheSample = InitSurfaceCacheSample();

	if (MeshCardsIndex < LumenCardScene_NumMeshCards)
	{
		FLumenMeshCardsData MeshCardsData = GetLumenMeshCardsData(MeshCardsIndex);

		float3 MeshCardsSpacePosition = mul(WorldSpacePosition - MeshCardsData.WorldOrigin, MeshCardsData.WorldToLocalRotation);
		float3 MeshCardsSpaceNormal = mul(WorldSpaceNormal, MeshCardsData.WorldToLocalRotation);

		uint CardMask = 0;
		float3 AxisWeights = MeshCardsSpaceNormal * MeshCardsSpaceNormal;


		if (AxisWeights.x > 0.0f)
		{
			CardMask |= MeshCardsData.CardLookup[MeshCardsSpaceNormal.x < 0.0f ? 0 : 1];
		}
		if (AxisWeights.y > 0.0f)
		{
			CardMask |= MeshCardsData.CardLookup[MeshCardsSpaceNormal.y < 0.0f ? 2 : 3];
		}
		if (AxisWeights.z > 0.0f)
		{
			CardMask |= MeshCardsData.CardLookup[MeshCardsSpaceNormal.z < 0.0f ? 4 : 5];
		}


		{
			uint CulledCardMask = 0;
			while (CardMask != 0)
			{
				const uint NextBitIndex = firstbitlow(CardMask);
				const uint NextBitMask = 1u << NextBitIndex;
				CardMask ^= NextBitMask;

				uint CardIndex = MeshCardsData.CardOffset + NextBitIndex;
				FLumenCardData LumenCardData = GetLumenCardData(CardIndex);

				if (all(abs(MeshCardsSpacePosition - LumenCardData.MeshCardsOrigin) <= LumenCardData.MeshCardsExtent + 0.5f * SurfaceCacheBias))
				{
					CulledCardMask |= NextBitMask;
				}
			}
			CardMask = CulledCardMask;
		}

		if (MeshCardsData.bHeightfield)
		{
			CardMask = (1 <<  0 );
		}

		FCardSampleAccumulator CardSampleAccumulator;
		InitCardSampleAccumulator(CardSampleAccumulator);


		while (CardMask != 0)
		{
			const uint NextBitIndex = firstbitlow(CardMask);
			CardMask ^= 1u << NextBitIndex;

			uint CardIndex = MeshCardsData.CardOffset + NextBitIndex;
			FLumenCardData LumenCardData = GetLumenCardData(CardIndex);
			if (LumenCardData.bVisible)
			{
				SampleLumenCard(
					MeshCardsSpacePosition,
					MeshCardsSpaceNormal,
					SampleRadius,
					SurfaceCacheBias,
					CardIndex,
					AxisWeights,
					bHiResSurface,
					MeshCardsData.bHeightfield,
					AtlasId,
					CardSampleAccumulator);
			}
		}

		if (CardSampleAccumulator.SampleWeightSum > 0.0f)
		{
			SurfaceCacheSample.LightingSum = CardSampleAccumulator.LightingSum;
			SurfaceCacheSample.OpacitySum = CardSampleAccumulator.OpacitySum;
			SurfaceCacheSample.SampleWeightSum = CardSampleAccumulator.SampleWeightSum;

			SurfaceCacheSample.Lighting = CardSampleAccumulator.LightingSum / CardSampleAccumulator.SampleWeightSum;
			SurfaceCacheSample.Opacity = CardSampleAccumulator.OpacitySum / CardSampleAccumulator.SampleWeightSum;
			SurfaceCacheSample.bValid = true;
		}
	}

	return SurfaceCacheSample;
}
void SampleLumenCard(
	float3 MeshCardsSpacePosition,
	float3 MeshCardsSpaceNormal,
	float SampleRadius,
	float SurfaceCacheBias,
	uint CardIndex,
	float3 AxisWeights,
	bool bHiResSurface,
	bool bHeightfield,
	uint AtlasId,
	inout FCardSampleAccumulator CardSampleAccumulator)
{
	if (CardIndex < LumenCardScene_NumCards)
	{
		FLumenCardData LumenCardData = GetLumenCardData(CardIndex);
		if (LumenCardData.bVisible)
		{
			float3 CardSpacePosition = mul(MeshCardsSpacePosition - LumenCardData.MeshCardsOrigin, LumenCardData.MeshCardsToLocalRotation);
			if (all(abs(CardSpacePosition) <= LumenCardData.LocalExtent + 0.5f * SurfaceCacheBias))
			{
				CardSpacePosition.xy = clamp(CardSpacePosition.xy, -LumenCardData.LocalExtent.xy, LumenCardData.LocalExtent.xy);

				FLumenCardSample CardSample = ComputeSurfaceCacheSample(LumenCardData, CardIndex, CardSpacePosition.xy, SampleRadius, bHiResSurface);
				if (CardSample.bValid)
				{

					float NormalWeight = 1.0f;
					if (!bHeightfield)
					{
						if (LumenCardData.AxisAlignedDirection < 2)
						{
							NormalWeight = AxisWeights.x;
						}
						else if (LumenCardData.AxisAlignedDirection < 4)
						{
							NormalWeight = AxisWeights.y;
						}
						else
						{
							NormalWeight = AxisWeights.z;
						}
					}

					if (NormalWeight > 0.0f)
					{
						float4 TexelDepths = DepthAtlas.Gather( View_SharedPointClampedSampler , CardSample.PhysicalAtlasUV, 0.0f);

						float NormalizedHitDistance = -(CardSpacePosition.z / LumenCardData.LocalExtent.z) * 0.5f + 0.5f;
						float BiasTreshold = SurfaceCacheBias / LumenCardData.LocalExtent.z;
						float BiasFalloff = 0.25f * BiasTreshold;

						float4 TexelVisibility = 0.0f;
						for (uint TexelIndex = 0; TexelIndex < 4; ++TexelIndex)
						{

							if (IsSurfaceCacheDepthValid(TexelDepths[TexelIndex]))
							{

								if (bHeightfield)
								{
									TexelVisibility[TexelIndex] = 1.0f;
								}
								else
								{
									TexelVisibility[TexelIndex] = 1.0f - saturate((abs(NormalizedHitDistance - TexelDepths[TexelIndex]) - BiasTreshold) / BiasFalloff);
								}
							}
						}

						float4 TexelWeights = CardSample.TexelBilinearWeights * TexelVisibility;

						float CardSampleWeight = NormalWeight * dot(TexelWeights, 1.0f);
						if (CardSampleWeight > 0.0f)
						{

							float TexelWeightSum = dot(TexelWeights, 1.0f);
							TexelWeights /= TexelWeightSum;

							float Opacity = SampleSurfaceCacheAtlas(OpacityAtlas, CardSample.PhysicalAtlasUV, TexelWeights).x;
							float3 Lighting = 0.0f;

							if (AtlasId ==  0 )
							{
								Lighting = SampleSurfaceCacheAtlas(FinalLightingAtlas, CardSample.PhysicalAtlasUV, TexelWeights);
							}
							else if (AtlasId ==  1 )
							{
								float3 DirectLighting = SampleSurfaceCacheAtlas(DirectLightingAtlas, CardSample.PhysicalAtlasUV, TexelWeights);
								float3 IndirectLighting = SampleSurfaceCacheAtlas(IndirectLightingAtlas, CardSample.IndirectLightingPhysicalAtlasUV, TexelWeights);
								Lighting = DirectLighting + IndirectLighting;
							}
							else
							{
								Lighting = SampleSurfaceCacheAtlas(IndirectLightingAtlas, CardSample.PhysicalAtlasUV, TexelWeights);
							}

							CardSampleAccumulator.LightingSum += Lighting * CardSampleWeight;
							CardSampleAccumulator.OpacitySum += Opacity * CardSampleWeight;
							CardSampleAccumulator.SampleWeightSum += CardSampleWeight;


							if (CardSampleWeight > CardSampleAccumulator.MaxSampleWeight)
							{
								CardSampleAccumulator.CardSample = CardSample;
								CardSampleAccumulator.MaxSampleWeight = CardSampleWeight;
							}
						}
					}
				}
			}
		}
	}
}
FLumenCardSample ComputeSurfaceCacheSample(FLumenCardData Card, uint CardIndex, float2 LocalSamplePosition, float SampleRadius, bool bHiResSurface)
{

	float2 CardUV = min(SamplePositonToCardUV(Card, LocalSamplePosition), 0.999999f);

	uint2 SizeInPages = Card.SizeInPages;
	uint PageTableOffset = Card.PageTableOffset;

	if (bHiResSurface)
	{
		SizeInPages = Card.HiResSizeInPages;
		PageTableOffset = Card.HiResPageTableOffset;
	}

	uint2 PageCoord = CardUV * SizeInPages;
	uint LinearPageCoord = PageCoord.x + PageCoord.y * SizeInPages.x;

	const uint PageTableIndex = PageTableOffset + LinearPageCoord;
	const uint2 PageTableValue = LumenCardScene_PageTableBuffer.Load2(8 * PageTableIndex);

	uint2 AtlasBias;
	AtlasBias.x = ((PageTableValue.x >> 0) & 0xFFF) *  8 ;
	AtlasBias.y = ((PageTableValue.x >> 12) & 0xFFF) *  8 ;

	uint2 ResLevelXY;
	ResLevelXY.x = (PageTableValue.x >> 24) & 0xF;
	ResLevelXY.y = (PageTableValue.x >> 28) & 0xF;


	const uint CardPageIndex = PageTableValue.y;


	SizeInPages = ResLevelXYToSizeInPages(ResLevelXY);
	PageCoord = CardUV * SizeInPages;
	uint2 AtlasScale =  select_internal(ResLevelXY > 7 , 128 ,(1u << ResLevelXY)) ;

	float2 PageUV = frac(CardUV * SizeInPages);



	float2 MinUVBorder =  select_internal(PageCoord.xy == 0,0.0f,0.5f) ;
	float2 MaxUVBorder =  select_internal(PageCoord.xy + 1 == SizeInPages.xy,0.0f,0.5f) ;
	float2 CoordInPage = (PageUV * (AtlasScale - MinUVBorder - MaxUVBorder)) + MinUVBorder;


	CoordInPage = clamp(CoordInPage, 0.5f, AtlasScale - 1.0f - 0.5f);

	float2 PhysicalAtlasUV = (CoordInPage + AtlasBias) * LumenCardScene_InvPhysicalAtlasSize;


	float ILFactor = LumenCardScene_IndirectLightingAtlasDownsampleFactor;
	float2 IndirectLightingPhysicalAtlasUV = (PageUV * (AtlasScale / ILFactor - 1.0f) + AtlasBias / ILFactor + 0.5f) * ILFactor * LumenCardScene_InvPhysicalAtlasSize;


	uint2 PackedFeedback = 0;
	float2 FracUV = frac(PhysicalAtlasUV * LumenCardScene_PhysicalAtlasSize + 0.5f + 1.0f / 512.0f);

	float4 TexelBilinearWeights;
	TexelBilinearWeights.x = (1.0 - FracUV.x) * (FracUV.y);
	TexelBilinearWeights.y = (FracUV.x) * (FracUV.y);
	TexelBilinearWeights.z = (FracUV.x) * (1 - FracUV.y);
	TexelBilinearWeights.w = (1 - FracUV.x) * (1 - FracUV.y);

	FLumenCardSample CardSample;
	CardSample.CardIndex = CardIndex;
	CardSample.CardPageIndex = CardPageIndex;
	CardSample.PhysicalAtlasUV = PhysicalAtlasUV;
	CardSample.TexelBilinearWeights = TexelBilinearWeights;
	CardSample.IndirectLightingPhysicalAtlasUV = IndirectLightingPhysicalAtlasUV;
	CardSample.bValid = ResLevelXY.x > 0;
	CardSample.PackedFeedback = PackedFeedback;
	return CardSample;
}
float3 SampleSurfaceCacheAtlas(Texture2D AtlasTexture, float2 AtlasUV, float4 TexelWeights)
{
	float4 SampleX4 = AtlasTexture.GatherRed( View_SharedPointClampedSampler , AtlasUV);
	float4 SampleY4 = AtlasTexture.GatherGreen( View_SharedPointClampedSampler , AtlasUV);
	float4 SampleZ4 = AtlasTexture.GatherBlue( View_SharedPointClampedSampler , AtlasUV);

	float3 Sample;
	Sample.x = dot(SampleX4, TexelWeights);
	Sample.y = dot(SampleY4, TexelWeights);
	Sample.z = dot(SampleZ4, TexelWeights);
	return Sample;
}

你可能感兴趣的:(#,UE之Lumen,UE)