Files
UnrealEngineUWP/Engine/Shaders/Private/DistanceFieldVisualization.usf
daniel wright 4f05353e95 Fixed exposure in Mesh Distance Fields visualization
#ROBOMERGE-SOURCE: CL 15348315 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v771-15082668)

[CL 15359818 by daniel wright in ue5-main branch]
2021-02-08 16:16:15 -04:00

209 lines
7.6 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DistanceFieldVisualization.usf
=============================================================================*/
#include "Common.ush"
#include "DeferredShadingCommon.ush"
#include "DistanceFieldLightingShared.ush"
#include "DistanceFieldAOShared.ush"
#include "GlobalDistanceFieldShared.ush"
#include "GlobalDistanceFieldUtils.ush"
RWTexture2D<float4> RWVisualizeMeshDistanceFields;
#define MAX_INTERSECTING_OBJECTS 1024
groupshared uint IntersectingObjectIndices[MAX_INTERSECTING_OBJECTS];
groupshared uint NumIntersectingObjects;
void RayTraceThroughTileCulledDistanceFields(float3 WorldRayStart, float3 WorldRayEnd, float MaxRayTime, out float MinRayTime, out float TotalStepsTaken)
{
MinRayTime = MaxRayTime;
TotalStepsTaken = 0;
LOOP
for (uint ListObjectIndex = 0; ListObjectIndex < min(NumIntersectingObjects, (uint)MAX_INTERSECTING_OBJECTS); ListObjectIndex++)
{
uint ObjectIndex = IntersectingObjectIndices[ListObjectIndex];
FDFObjectData DFObjectData = LoadCulledDFObjectData(ObjectIndex);
float3 VolumeRayStart = mul(float4(WorldRayStart, 1), DFObjectData.WorldToVolume).xyz;
float3 VolumeRayEnd = mul(float4(WorldRayEnd, 1), DFObjectData.WorldToVolume).xyz;
float3 VolumeRayDirection = VolumeRayEnd - VolumeRayStart;
float VolumeRayLength = length(VolumeRayDirection);
VolumeRayDirection /= VolumeRayLength;
float2 IntersectionTimes = LineBoxIntersect(VolumeRayStart, VolumeRayEnd, -DFObjectData.LocalPositionExtent, DFObjectData.LocalPositionExtent);
if (IntersectionTimes.x < IntersectionTimes.y && IntersectionTimes.x < 1)
{
float SampleRayTime = IntersectionTimes.x * VolumeRayLength;
float MinDistance = 1000000;
uint StepIndex = 0;
uint MaxSteps = 256;
LOOP
for (; StepIndex < MaxSteps; StepIndex++)
{
float3 SampleVolumePosition = VolumeRayStart + VolumeRayDirection * SampleRayTime;
float3 ClampedSamplePosition = clamp(SampleVolumePosition, -DFObjectData.LocalPositionExtent, DFObjectData.LocalPositionExtent);
float3 VolumeUV = DistanceFieldVolumePositionToUV(ClampedSamplePosition, DFObjectData.UVScale, DFObjectData.UVAdd);
float DistanceField = SampleMeshDistanceField(VolumeUV, DFObjectData.DistanceFieldMAD).x;
MinDistance = min(MinDistance, DistanceField);
float MinStepSize = 1.0f / (4 * MaxSteps);
float StepDistance = max(DistanceField, MinStepSize);
SampleRayTime += StepDistance;
// Terminate the trace if we reached a negative area or went past the end of the ray
if (DistanceField < 0
|| SampleRayTime > IntersectionTimes.y * VolumeRayLength)
{
break;
}
}
//Result = max(Result, StepIndex / (float)MaxSteps);
if (MinDistance * DFObjectData.VolumeScale < 0 || StepIndex == MaxSteps)
{
const float NewHitDistance = length(VolumeRayDirection * SampleRayTime * DFObjectData.VolumeToLocalScale);
MinRayTime = min(MinRayTime, NewHitDistance);
}
TotalStepsTaken += StepIndex;
}
}
}
float2 NumGroups;
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
void VisualizeMeshDistanceFieldCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint ThreadIndex = GroupThreadId.y * THREADGROUP_SIZEX + GroupThreadId.x;
float2 ScreenUV = float2((DispatchThreadId.xy * DOWNSAMPLE_FACTOR + View.ViewRectMin.xy + .5f) * View.BufferSizeAndInvSize.zw);
float2 ScreenPosition = (ScreenUV.xy - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
float SceneDepth = CalcSceneDepth(ScreenUV);
float3 OpaqueWorldPosition = mul(float4(ScreenPosition * SceneDepth, SceneDepth, 1), View.ScreenToWorld).xyz;
float TraceDistance = 40000;
float3 WorldRayStart = View.WorldCameraOrigin;
float3 WorldRayEnd = WorldRayStart + normalize(OpaqueWorldPosition - View.WorldCameraOrigin) * TraceDistance;
float3 WorldRayDirection = normalize(WorldRayEnd - WorldRayStart);
#if USE_GLOBAL_DISTANCE_FIELD
FGlobalSDFTraceInput TraceInput = SetupGlobalSDFTraceInput(WorldRayStart, WorldRayDirection, 0.0f, TraceDistance, 1.0f, 1.0f);
FGlobalSDFTraceResult SDFTraceResult = RayTraceGlobalDistanceField(TraceInput);
float3 Result = saturate(SDFTraceResult.TotalStepsTaken / 400.0f);
if (!GlobalSDFTraceResultIsHit(SDFTraceResult))
{
// Sky
Result.xy *= 0.25f;
}
#else
if (ThreadIndex == 0)
{
NumIntersectingObjects = 0;
}
GroupMemoryBarrierWithGroupSync();
float3 TileConeVertex;
float3 TileConeAxis;
float TileConeAngleCos;
float TileConeAngleSin;
{
float2 ViewSize = float2(1 / View.ViewToClip[0][0], 1 / View.ViewToClip[1][1]);
float3 TileCorner00 = normalize(float3((GroupId.x + 0) / NumGroups.x * ViewSize.x * 2 - ViewSize.x, ViewSize.y - (GroupId.y + 0) / NumGroups.y * ViewSize.y * 2, 1));
float3 TileCorner10 = normalize(float3((GroupId.x + 1) / NumGroups.x * ViewSize.x * 2 - ViewSize.x, ViewSize.y - (GroupId.y + 0) / NumGroups.y * ViewSize.y * 2, 1));
float3 TileCorner01 = normalize(float3((GroupId.x + 0) / NumGroups.x * ViewSize.x * 2 - ViewSize.x, ViewSize.y - (GroupId.y + 1) / NumGroups.y * ViewSize.y * 2, 1));
float3 TileCorner11 = normalize(float3((GroupId.x + 1) / NumGroups.x * ViewSize.x * 2 - ViewSize.x, ViewSize.y - (GroupId.y + 1) / NumGroups.y * ViewSize.y * 2, 1));
float3 ViewSpaceTileConeAxis = normalize(TileCorner00 + TileCorner10 + TileCorner01 + TileCorner11);
TileConeAxis = mul(ViewSpaceTileConeAxis, (float3x3)View.ViewToTranslatedWorld);
TileConeAngleCos = dot(ViewSpaceTileConeAxis, TileCorner00);
TileConeAngleSin = sqrt(1 - TileConeAngleCos * TileConeAngleCos);
TileConeVertex = View.WorldCameraOrigin;
}
uint NumCulledObjects = GetCulledNumObjects();
LOOP
for (uint ObjectIndex = ThreadIndex; ObjectIndex < NumCulledObjects; ObjectIndex += THREADGROUP_TOTALSIZE)
{
float4 SphereCenterAndRadius = LoadObjectPositionAndRadius(ObjectIndex);
BRANCH
if (SphereIntersectCone(SphereCenterAndRadius, TileConeVertex, TileConeAxis, TileConeAngleCos, TileConeAngleSin))
{
uint ListIndex;
InterlockedAdd(NumIntersectingObjects, 1U, ListIndex);
if (ListIndex < MAX_INTERSECTING_OBJECTS)
{
IntersectingObjectIndices[ListIndex] = ObjectIndex;
}
}
}
GroupMemoryBarrierWithGroupSync();
float MinRayTime;
float TotalStepsTaken;
// Trace once to find the distance to first intersection
RayTraceThroughTileCulledDistanceFields(WorldRayStart, WorldRayEnd, TraceDistance, MinRayTime, TotalStepsTaken);
float TempMinRayTime;
// Recompute the ray end point
WorldRayEnd = WorldRayStart + WorldRayDirection * MinRayTime;
// Trace a second time to only accumulate steps taken before the first intersection, improves visualization
RayTraceThroughTileCulledDistanceFields(WorldRayStart, WorldRayEnd, MinRayTime, TempMinRayTime, TotalStepsTaken);
float3 Result = saturate(TotalStepsTaken / 200.0f);
if (MinRayTime < TraceDistance)
{
Result += .1f;
}
else
{
// Sky
Result.z += 0.05f;
}
#endif
RWVisualizeMeshDistanceFields[DispatchThreadId.xy] = float4(Result, 0);
}
Texture2D VisualizeDistanceFieldTexture;
SamplerState VisualizeDistanceFieldSampler;
void VisualizeDistanceFieldUpsamplePS(in float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
{
// Distance field AO was computed at 0,0 regardless of viewrect min
float2 DistanceFieldUVs = UVAndScreenPos.xy - View.ViewRectMin.xy * View.BufferSizeAndInvSize.zw;
float3 Value = Texture2DSampleLevel(VisualizeDistanceFieldTexture, VisualizeDistanceFieldSampler, DistanceFieldUVs, 0).xyz;
// Note: not applying pre-exposure as exposure is skipped in IsAutoExposureDebugMode
OutColor = float4(Value, 1);
}