You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
DF shadowing from point lights now clamps cone angles to 10 degrees to avoid artifacts [CL 2303478 by Daniel Wright in Main branch]
568 lines
21 KiB
Plaintext
568 lines
21 KiB
Plaintext
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
DistanceFieldShadowing.usf
|
|
=============================================================================*/
|
|
|
|
#include "Common.usf"
|
|
#include "DeferredShadingCommon.usf"
|
|
#include "DistanceFieldLightingShared.usf"
|
|
|
|
bool RayHitSphere(float3 RayOrigin, float3 UnitRayDirection, float RayLength, float3 SphereCenter, float SphereRadius)
|
|
{
|
|
float DistanceAlongRay = dot(SphereCenter - RayOrigin, UnitRayDirection);
|
|
float3 ClosestPointOnRay = DistanceAlongRay * UnitRayDirection;
|
|
float3 CenterToRay = RayOrigin + ClosestPointOnRay - SphereCenter;
|
|
return dot(CenterToRay, CenterToRay) <= Square(SphereRadius) && DistanceAlongRay > -SphereRadius && DistanceAlongRay - SphereRadius < RayLength;
|
|
}
|
|
|
|
RWBuffer<uint> RWTileHeadDataUnpacked;
|
|
RWBuffer<uint> RWTileArrayData;
|
|
|
|
float2 NumGroups;
|
|
|
|
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
|
|
void ClearTilesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint TileIndex = DispatchThreadId.y * NumGroups.x + DispatchThreadId.x;
|
|
|
|
RWTileHeadDataUnpacked[TileIndex * 2 + 0] = TileIndex;
|
|
RWTileHeadDataUnpacked[TileIndex * 2 + 1] = 0;
|
|
}
|
|
|
|
struct FShadowObjectCullVertexOutput
|
|
{
|
|
float4 Position : SV_POSITION;
|
|
nointerpolation float4 PositionAndRadius : TEXCOORD0;
|
|
nointerpolation uint ObjectIndex : TEXCOORD1;
|
|
};
|
|
|
|
float ConservativeRadiusScale;
|
|
float MinRadius;
|
|
float4x4 WorldToShadow;
|
|
|
|
/** Used when culling objects into screenspace tile lists */
|
|
void ShadowObjectCullVS(
|
|
float4 InPosition : ATTRIBUTE0,
|
|
uint ObjectIndex : SV_InstanceID,
|
|
out FShadowObjectCullVertexOutput Output
|
|
)
|
|
{
|
|
float4 ObjectPositionAndRadius = LoadObjectPositionAndRadius(ObjectIndex);
|
|
// ConservativeRadiusScale pushes the sphere vertices out so the triangles between them lie completely outside the sphere
|
|
// MinRadius is for conservative rasterization
|
|
float EffectiveRadius = (ObjectPositionAndRadius.w + MinRadius) * ConservativeRadiusScale;
|
|
float3 WorldPosition = InPosition.xyz * EffectiveRadius + ObjectPositionAndRadius.xyz;
|
|
Output.Position = mul(float4(WorldPosition, 1), WorldToShadow);
|
|
Output.PositionAndRadius = ObjectPositionAndRadius;
|
|
Output.ObjectIndex = ObjectIndex;
|
|
}
|
|
|
|
/** Intersects a single object with the tile and adds to the intersection list if needed. */
|
|
void ShadowObjectCullPS(
|
|
FShadowObjectCullVertexOutput Input,
|
|
in float4 SVPos : SV_POSITION,
|
|
out float4 OutColor : COLOR0)
|
|
{
|
|
OutColor = 0;
|
|
|
|
uint2 TilePosition = (uint2)SVPos.xy;
|
|
uint TileIndex = TilePosition.y * NumGroups.x + TilePosition.x;
|
|
|
|
#define OBJECT_OBB_INTERSECTION 1
|
|
#if OBJECT_OBB_INTERSECTION
|
|
|
|
float3 ShadowTileMin;
|
|
float3 ShadowTileMax;
|
|
|
|
float2 TilePositionForCulling = float2(TilePosition.x, NumGroups.y - TilePosition.y);
|
|
//@todo - why is this expand needed
|
|
float TileExpand = 1;
|
|
ShadowTileMin.xy = (TilePositionForCulling - TileExpand) / (float2)NumGroups * 2 - 1;
|
|
ShadowTileMax.xy = (TilePositionForCulling + 1 + TileExpand) / (float2)NumGroups * 2 - 1;
|
|
ShadowTileMin.z = 0;
|
|
ShadowTileMax.z = 1;
|
|
|
|
float3 ObjectViewSpaceMin;
|
|
float3 ObjectViewSpaceMax;
|
|
LoadObjectViewSpaceBox(Input.ObjectIndex, ObjectViewSpaceMin, ObjectViewSpaceMax);
|
|
|
|
BRANCH
|
|
// Separating axis test on the AABB
|
|
if (all(ObjectViewSpaceMax > ShadowTileMin) && all(ObjectViewSpaceMin < ShadowTileMax))
|
|
{
|
|
float3 ObjectCenter = .5f * (ObjectViewSpaceMin + ObjectViewSpaceMax);
|
|
float3 MinProjections = 500000;
|
|
float3 MaxProjections = -500000;
|
|
|
|
{
|
|
float3 Corners[8];
|
|
Corners[0] = float3(ShadowTileMin.x, ShadowTileMin.y, ShadowTileMin.z);
|
|
Corners[1] = float3(ShadowTileMax.x, ShadowTileMin.y, ShadowTileMin.z);
|
|
Corners[2] = float3(ShadowTileMin.x, ShadowTileMax.y, ShadowTileMin.z);
|
|
Corners[3] = float3(ShadowTileMax.x, ShadowTileMax.y, ShadowTileMin.z);
|
|
Corners[4] = float3(ShadowTileMin.x, ShadowTileMin.y, ShadowTileMax.z);
|
|
Corners[5] = float3(ShadowTileMax.x, ShadowTileMin.y, ShadowTileMax.z);
|
|
Corners[6] = float3(ShadowTileMin.x, ShadowTileMax.y, ShadowTileMax.z);
|
|
Corners[7] = float3(ShadowTileMax.x, ShadowTileMax.y, ShadowTileMax.z);
|
|
|
|
float3 ObjectAxisX;
|
|
float3 ObjectAxisY;
|
|
float3 ObjectAxisZ;
|
|
LoadObjectAxes(Input.ObjectIndex, ObjectAxisX, ObjectAxisY, ObjectAxisZ);
|
|
|
|
UNROLL
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
float3 CenterToVertex = Corners[i] - ObjectCenter;
|
|
float3 Projections = float3(dot(CenterToVertex, ObjectAxisX), dot(CenterToVertex, ObjectAxisY), dot(CenterToVertex, ObjectAxisZ));
|
|
MinProjections = min(MinProjections, Projections);
|
|
MaxProjections = max(MaxProjections, Projections);
|
|
}
|
|
}
|
|
|
|
BRANCH
|
|
// Separating axis test on the OBB
|
|
if (all(MinProjections < 1) && all(MaxProjections > -1))
|
|
{
|
|
uint ArrayIndex;
|
|
InterlockedAdd(RWTileHeadDataUnpacked[TileIndex * 2 + 1], 1U, ArrayIndex);
|
|
|
|
if (ArrayIndex < MAX_OBJECTS_PER_TILE)
|
|
{
|
|
uint DataIndex = (ArrayIndex * (uint)(NumGroups.x * NumGroups.y + .5f) + TileIndex);
|
|
RWTileArrayData[DataIndex] = Input.ObjectIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
{
|
|
uint ArrayIndex;
|
|
InterlockedAdd(RWTileHeadDataUnpacked[TileIndex * 2 + 1], 1U, ArrayIndex);
|
|
|
|
if (ArrayIndex < MAX_OBJECTS_PER_TILE)
|
|
{
|
|
uint DataIndex = (ArrayIndex * (uint)(NumGroups.x * NumGroups.y + .5f) + TileIndex);
|
|
RWTileArrayData[DataIndex] = Input.ObjectIndex;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
RWTexture2D<float2> RWShadowFactors;
|
|
|
|
/** From point being shaded toward light, for directional lights. */
|
|
float3 LightDirection;
|
|
float4 LightPositionAndInvRadius;
|
|
float LightSourceRadius;
|
|
float2 TanLightAngleAndNormalThreshold;
|
|
int4 ScissorRectMinAndSize;
|
|
|
|
#if SCATTER_TILE_CULLING
|
|
|
|
Buffer<uint> TileHeadDataUnpacked;
|
|
Buffer<uint> TileArrayData;
|
|
uint2 TileListGroupSize;
|
|
|
|
uint2 GetTileHead(uint2 TileCoordinate)
|
|
{
|
|
uint TileIndex = TileCoordinate.y * TileListGroupSize.x + TileCoordinate.x;
|
|
|
|
return uint2(
|
|
TileHeadDataUnpacked[TileIndex * 2 + 0],
|
|
min(TileHeadDataUnpacked[TileIndex * 2 + 1], (uint)MAX_OBJECTS_PER_TILE));
|
|
}
|
|
|
|
#else // SCATTER_TILE_CULLING
|
|
|
|
/** Min and Max depth for this tile. */
|
|
groupshared uint IntegerTileMinZ;
|
|
groupshared uint IntegerTileMaxZ;
|
|
|
|
/** Inner Min and Max depth for this tile. */
|
|
groupshared uint IntegerTileMinZ2;
|
|
groupshared uint IntegerTileMaxZ2;
|
|
|
|
/** Number of objects affecting the tile, after culling. */
|
|
groupshared uint TileNumObjects0;
|
|
groupshared uint TileNumObjects1;
|
|
|
|
#define MAX_INTERSECTING_OBJECTS 256
|
|
groupshared uint IntersectingObjectIndices[MAX_INTERSECTING_OBJECTS * 2];
|
|
|
|
#endif
|
|
|
|
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
|
|
void DistanceFieldShadowingCS(
|
|
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 + ScissorRectMinAndSize.xy + .5f) * View.ViewSizeAndSceneTexelSize.zw);
|
|
float2 ScreenPosition = (ScreenUV.xy - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
|
|
|
|
float SceneDepth = CalcSceneDepth(ScreenUV);
|
|
float4 HomogeneousWorldPosition = mul(float4(ScreenPosition * SceneDepth, SceneDepth, 1), View.ScreenToWorld);
|
|
float3 OpaqueWorldPosition = HomogeneousWorldPosition.xyz / HomogeneousWorldPosition.w;
|
|
|
|
// Distance for directional lights to trace
|
|
float TraceDistance = 10000;
|
|
|
|
//@todo - take advantage of CSM covering near pixels
|
|
//@todo - handle invalid tile depth ranges due to MinDepth
|
|
float MinDepth = 0;
|
|
|
|
uint NumIntersectingObjects = NumObjects;
|
|
bool bTileShouldComputeShadowing = SceneDepth > MinDepth;
|
|
|
|
#define USE_CULLING 1
|
|
#if USE_CULLING
|
|
|
|
#if SCATTER_TILE_CULLING
|
|
|
|
// Transform into shadow space
|
|
float4 HomogeneousShadowPosition = mul(float4(OpaqueWorldPosition, 1), WorldToShadow);
|
|
float2 NormalizedShadowPosition = HomogeneousShadowPosition.xy * .5f + .5f;
|
|
NormalizedShadowPosition.y = 1 - NormalizedShadowPosition.y;
|
|
// Quantize the shadow position to get our tile position
|
|
uint2 TilePosition = (uint2)(NormalizedShadowPosition * TileListGroupSize);
|
|
// Fetch the tile head information
|
|
uint2 TileHead = GetTileHead(TilePosition);
|
|
NumIntersectingObjects = TileHead.y;
|
|
|
|
#else
|
|
|
|
// Initialize per-tile variables
|
|
if (ThreadIndex == 0)
|
|
{
|
|
IntegerTileMinZ = 0x7F7FFFFF;
|
|
IntegerTileMaxZ = 0;
|
|
IntegerTileMinZ2 = 0x7F7FFFFF;
|
|
IntegerTileMaxZ2 = 0;
|
|
TileNumObjects0 = 0;
|
|
TileNumObjects1 = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (SceneDepth > MinDepth)
|
|
{
|
|
// Use shared memory atomics to build the depth bounds for this tile
|
|
// Each thread is assigned to a pixel at this point
|
|
//@todo - move depth range computation to a central point where it can be reused by all the frame's tiled deferred passes!
|
|
InterlockedMin(IntegerTileMinZ, asuint(SceneDepth));
|
|
InterlockedMax(IntegerTileMaxZ, asuint(SceneDepth));
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
float MinTileZ = asfloat(IntegerTileMinZ);
|
|
float MaxTileZ = asfloat(IntegerTileMaxZ);
|
|
|
|
float HalfZ = .5f * (MinTileZ + MaxTileZ);
|
|
|
|
// Compute a second min and max Z, clipped by HalfZ, so that we get two depth bounds per tile
|
|
// This results in more conservative tile depth bounds and fewer intersections
|
|
if (SceneDepth >= HalfZ && SceneDepth > MinDepth)
|
|
{
|
|
InterlockedMin(IntegerTileMinZ2, asuint(SceneDepth));
|
|
}
|
|
|
|
if (SceneDepth <= HalfZ && SceneDepth > MinDepth)
|
|
{
|
|
InterlockedMax(IntegerTileMaxZ2, asuint(SceneDepth));
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
float MinTileZ2 = asfloat(IntegerTileMinZ2);
|
|
float MaxTileZ2 = asfloat(IntegerTileMaxZ2);
|
|
|
|
float3 ViewTileMin;
|
|
float3 ViewTileMax;
|
|
|
|
float3 ViewTileMin2;
|
|
float3 ViewTileMax2;
|
|
|
|
float ExpandRadius = 0;
|
|
|
|
float2 TanViewFOV = float2(1 / View.ViewToClip[0][0], 1 / View.ViewToClip[1][1]);
|
|
// tan(FOV) = HalfUnitPlaneWidth / 1, so TanViewFOV * 2 is the size of the whole unit view plane
|
|
// We are operating on a subset of that defined by ScissorRectMinAndSize
|
|
float2 TileSize = TanViewFOV * 2 * ScissorRectMinAndSize.zw / ((float2)View.ViewSizeAndSceneTexelSize.xy * NumGroups);
|
|
float2 UnitPlaneMin = -TanViewFOV + TanViewFOV * 2 * ScissorRectMinAndSize.xy / (float2)View.ViewSizeAndSceneTexelSize.xy;
|
|
float2 UnitPlaneTileMin = (GroupId.xy * TileSize + UnitPlaneMin) * float2(1, -1);
|
|
float2 UnitPlaneTileMax = ((GroupId.xy + 1) * TileSize + UnitPlaneMin) * float2(1, -1);
|
|
|
|
ViewTileMin.xy = min(MinTileZ * UnitPlaneTileMin, MaxTileZ2 * UnitPlaneTileMin) - ExpandRadius;
|
|
ViewTileMax.xy = max(MinTileZ * UnitPlaneTileMax, MaxTileZ2 * UnitPlaneTileMax) + ExpandRadius;
|
|
ViewTileMin.z = MinTileZ - ExpandRadius;
|
|
ViewTileMax.z = MaxTileZ2 + ExpandRadius;
|
|
ViewTileMin2.xy = min(MinTileZ2 * UnitPlaneTileMin, MaxTileZ * UnitPlaneTileMin) - ExpandRadius;
|
|
ViewTileMax2.xy = max(MinTileZ2 * UnitPlaneTileMax, MaxTileZ * UnitPlaneTileMax) + ExpandRadius;
|
|
ViewTileMin2.z = MinTileZ2 - ExpandRadius;
|
|
ViewTileMax2.z = MaxTileZ + ExpandRadius;
|
|
|
|
float3 ViewGroup0Center = (ViewTileMax + ViewTileMin) / 2;
|
|
float3 WorldGroup0Center = mul(float4(ViewGroup0Center, 1), View.ViewToTranslatedWorld).xyz - View.PreViewTranslation;
|
|
float Group0BoundingRadius = length(ViewGroup0Center - ViewTileMax);
|
|
|
|
float3 ViewGroup1Center = (ViewTileMax2 + ViewTileMin2) / 2;
|
|
float3 WorldGroup1Center = mul(float4(ViewGroup1Center, 1), View.ViewToTranslatedWorld).xyz - View.PreViewTranslation;
|
|
float Group1BoundingRadius = length(ViewGroup1Center - ViewTileMax2);
|
|
|
|
bool bCullTile = true;
|
|
|
|
#if POINT_LIGHT
|
|
float3 LightVector0 = LightPositionAndInvRadius.xyz - WorldGroup0Center;
|
|
float LightVector0Length = length(LightVector0);
|
|
float3 LightVector1 = LightPositionAndInvRadius.xyz - WorldGroup1Center;
|
|
float LightVector1Length = length(LightVector1);
|
|
float3 LightDirection0 = LightVector0 / LightVector0Length;
|
|
float3 LightDirection1 = LightVector1 / LightVector1Length;;
|
|
float RayLength0 = LightVector0Length;
|
|
float RayLength1 = LightVector1Length;
|
|
|
|
// Don't operate on tiles completely outside of the light's influence
|
|
bCullTile = bTileShouldComputeShadowing = LightVector0Length < 1.0f / LightPositionAndInvRadius.w + Group0BoundingRadius
|
|
|| LightVector1Length < 1.0f / LightPositionAndInvRadius.w + Group1BoundingRadius;
|
|
|
|
#else
|
|
float3 LightDirection0 = LightDirection;
|
|
float3 LightDirection1 = LightDirection;
|
|
float RayLength0 = TraceDistance;
|
|
float RayLength1 = TraceDistance;
|
|
#endif
|
|
|
|
BRANCH
|
|
if (bCullTile)
|
|
{
|
|
// Compute per-tile lists of affecting objects through bounds culling
|
|
// Each thread now operates on a sample instead of a pixel
|
|
LOOP
|
|
for (uint ObjectIndex = ThreadIndex; ObjectIndex < NumObjects; ObjectIndex += THREADGROUP_TOTALSIZE)
|
|
{
|
|
float4 SphereCenterAndRadius = LoadObjectPositionAndRadius(ObjectIndex);
|
|
|
|
BRANCH
|
|
if (RayHitSphere(WorldGroup0Center, LightDirection0, RayLength0, SphereCenterAndRadius.xyz, SphereCenterAndRadius.w + Group0BoundingRadius))
|
|
{
|
|
uint ListIndex;
|
|
InterlockedAdd(TileNumObjects0, 1U, ListIndex);
|
|
// Don't overwrite on overflow
|
|
ListIndex = min(ListIndex, MAX_INTERSECTING_OBJECTS - 1);
|
|
IntersectingObjectIndices[MAX_INTERSECTING_OBJECTS * 0 + ListIndex] = ObjectIndex;
|
|
}
|
|
|
|
BRANCH
|
|
if (RayHitSphere(WorldGroup1Center, LightDirection1, RayLength1, SphereCenterAndRadius.xyz, SphereCenterAndRadius.w + Group1BoundingRadius))
|
|
{
|
|
uint ListIndex;
|
|
InterlockedAdd(TileNumObjects1, 1U, ListIndex);
|
|
// Don't write out of bounds on overflow
|
|
ListIndex = min(ListIndex, MAX_INTERSECTING_OBJECTS - 1);
|
|
IntersectingObjectIndices[MAX_INTERSECTING_OBJECTS * 1 + ListIndex] = ObjectIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
uint GroupIndex = SceneDepth > MaxTileZ2 ? 1 : 0;
|
|
NumIntersectingObjects = GroupIndex == 0 ? TileNumObjects0 : TileNumObjects1;
|
|
#endif
|
|
#endif // USE_CULLING
|
|
|
|
float Result = 0;
|
|
|
|
#define COMPUTE_SHADOWING 1
|
|
#if COMPUTE_SHADOWING
|
|
BRANCH
|
|
if (bTileShouldComputeShadowing)
|
|
{
|
|
FGBufferData GBufferData = GetGBufferData(ScreenUV);
|
|
float3 WorldNormal = GBufferData.WorldNormal;
|
|
|
|
#if !POINT_LIGHT
|
|
BRANCH
|
|
if (dot(WorldNormal, LightDirection) > TanLightAngleAndNormalThreshold.y)
|
|
#endif
|
|
{
|
|
// World space offset along the start of the ray to avoid incorrect self-shadowing
|
|
float RayStartOffset = 2;
|
|
// Keeps result from going all the way sharp
|
|
float MinSphereRadius = .4f;
|
|
// Maintain reasonable culling bounds
|
|
float MaxSphereRadius = 100;
|
|
|
|
#if POINT_LIGHT
|
|
|
|
float3 LightVector = LightPositionAndInvRadius.xyz - OpaqueWorldPosition;
|
|
float LightVectorLength = length(LightVector);
|
|
float3 WorldRayStart = OpaqueWorldPosition + LightVector / LightVectorLength * RayStartOffset;
|
|
float3 WorldRayEnd = LightPositionAndInvRadius.xyz;
|
|
float MaxRayTime = LightVectorLength;
|
|
float MaxAngle = tan(10 * PI / 180.0f);
|
|
// Comparing tangents instead of angles, but tangent is always increasing in this range
|
|
float TanLightAngle = min(LightSourceRadius / LightVectorLength, MaxAngle);
|
|
|
|
#else
|
|
|
|
float3 WorldRayStart = OpaqueWorldPosition + LightDirection * RayStartOffset;
|
|
float3 WorldRayEnd = OpaqueWorldPosition + LightDirection * TraceDistance;
|
|
float MaxRayTime = TraceDistance;
|
|
float TanLightAngle = TanLightAngleAndNormalThreshold.x;
|
|
|
|
#endif
|
|
|
|
float MinRayTime = MaxRayTime;
|
|
float MinConeVisibility = 1;
|
|
|
|
LOOP
|
|
for (uint ListObjectIndex = 0; ListObjectIndex < NumIntersectingObjects && MinRayTime >= MaxRayTime; ListObjectIndex++)
|
|
{
|
|
#if USE_CULLING
|
|
#if SCATTER_TILE_CULLING
|
|
uint ObjectIndex = TileArrayData.Load(ListObjectIndex * TileListGroupSize.x * TileListGroupSize.y + TileHead.x);
|
|
#else
|
|
uint ObjectIndex = IntersectingObjectIndices[MAX_INTERSECTING_OBJECTS * GroupIndex + ListObjectIndex];
|
|
#endif
|
|
#else
|
|
uint ObjectIndex = ListObjectIndex;
|
|
#endif
|
|
|
|
float3 LocalPositionExtent = LoadObjectLocalPositionExtent(ObjectIndex);
|
|
float4x4 WorldToVolume = LoadObjectWorldToVolume(ObjectIndex);
|
|
float4 UVScaleAndVolumeScale = LoadObjectUVScale(ObjectIndex);
|
|
float3 UVAdd = LoadObjectUVAdd(ObjectIndex);
|
|
|
|
float3 VolumeRayStart = mul(float4(WorldRayStart, 1), WorldToVolume).xyz;
|
|
float3 VolumeRayEnd = mul(float4(WorldRayEnd, 1), WorldToVolume).xyz;
|
|
float3 VolumeRayDirection = VolumeRayEnd - VolumeRayStart;
|
|
float VolumeRayLength = length(VolumeRayDirection);
|
|
VolumeRayDirection /= VolumeRayLength;
|
|
float VolumeMinSphereRadius = MinSphereRadius / UVScaleAndVolumeScale.w;
|
|
float VolumeMaxSphereRadius = MaxSphereRadius / UVScaleAndVolumeScale.w;
|
|
|
|
float4 SphereCenterAndRadius = LoadObjectPositionAndRadius(ObjectIndex);
|
|
float ObjectCenterDistanceAlongRay = max(dot(SphereCenterAndRadius.xyz - WorldRayStart, WorldRayEnd - WorldRayStart), 0);
|
|
// Expand the intersection box by the radius of the cone at the distance of the object along the cone
|
|
float LocalConeRadiusAtObject = min(TanLightAngle * ObjectCenterDistanceAlongRay / UVScaleAndVolumeScale.w, VolumeMaxSphereRadius);
|
|
|
|
float2 IntersectionTimes = LineBoxIntersect(VolumeRayStart, VolumeRayEnd, -LocalPositionExtent - LocalConeRadiusAtObject, LocalPositionExtent + LocalConeRadiusAtObject);
|
|
|
|
BRANCH
|
|
if (IntersectionTimes.x < IntersectionTimes.y && IntersectionTimes.x < 1)
|
|
{
|
|
float SampleRayTime = IntersectionTimes.x * VolumeRayLength;
|
|
uint MaxSteps = 64;
|
|
float MinStepSize = 1.0f / (4 * MaxSteps);
|
|
|
|
float MinDistance = 1000000;
|
|
float3 IntersectionPosition = float3(0, 0, 0);
|
|
|
|
uint StepIndex = 0;
|
|
|
|
LOOP
|
|
for (; StepIndex < MaxSteps; StepIndex++)
|
|
{
|
|
float3 SampleVolumePosition = VolumeRayStart + VolumeRayDirection * SampleRayTime;
|
|
float3 ClampedSamplePosition = clamp(SampleVolumePosition, -LocalPositionExtent, LocalPositionExtent);
|
|
float DistanceToClamped = length(ClampedSamplePosition - SampleVolumePosition);
|
|
float3 VolumeUV = DistanceFieldVolumePositionToUV(ClampedSamplePosition, UVScaleAndVolumeScale.xyz, UVAdd);
|
|
float DistanceField = Texture3DSampleLevel(DistanceFieldTexture, DistanceFieldSampler, VolumeUV, 0).x + DistanceToClamped;
|
|
|
|
MinDistance = min(MinDistance, DistanceField);
|
|
float SphereRadius = clamp(TanLightAngle * SampleRayTime, VolumeMinSphereRadius, VolumeMaxSphereRadius);
|
|
|
|
MinConeVisibility = min(MinConeVisibility, saturate(DistanceField / SphereRadius));
|
|
IntersectionPosition = SampleVolumePosition;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (MinDistance < 0 || StepIndex == MaxSteps)
|
|
{
|
|
MinConeVisibility = 0;
|
|
MinRayTime = min(MinRayTime, SampleRayTime * UVScaleAndVolumeScale.w);
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = MinConeVisibility;
|
|
}
|
|
}
|
|
|
|
#else
|
|
//Result = .0001f * NumIntersectingObjects;
|
|
Result = NumIntersectingObjects / 30.0f;
|
|
#endif
|
|
|
|
RWShadowFactors[DispatchThreadId.xy] = float2(Result, SceneDepth);
|
|
}
|
|
|
|
Texture2D ShadowFactorsTexture;
|
|
SamplerState ShadowFactorsSampler;
|
|
|
|
void DistanceFieldShadowingUpsamplePS(
|
|
in float4 UVAndScreenPos : TEXCOORD0,
|
|
in float4 SVPos : SV_POSITION,
|
|
out float4 OutColor : SV_Target0)
|
|
{
|
|
// Distance field shadowing was computed at 0,0 regardless of viewrect min
|
|
float2 DistanceFieldUVs = UVAndScreenPos.xy - ScissorRectMinAndSize.xy * View.ViewSizeAndSceneTexelSize.zw;
|
|
|
|
#define BILATERAL_UPSAMPLE 1
|
|
#if BILATERAL_UPSAMPLE
|
|
float2 LowResBufferSize = floor(View.RenderTargetSize / DOWNSAMPLE_FACTOR);
|
|
float2 LowResTexelSize = 1.0f / LowResBufferSize;
|
|
float2 Corner00UV = floor(DistanceFieldUVs * LowResBufferSize - .5f) / LowResBufferSize + .5f * LowResTexelSize;
|
|
float2 BilinearWeights = (DistanceFieldUVs - Corner00UV) * LowResBufferSize;
|
|
|
|
float2 TextureValues00 = Texture2DSampleLevel(ShadowFactorsTexture, ShadowFactorsSampler, Corner00UV, 0).xy;
|
|
float2 TextureValues10 = Texture2DSampleLevel(ShadowFactorsTexture, ShadowFactorsSampler, Corner00UV + float2(LowResTexelSize.x, 0), 0).xy;
|
|
float2 TextureValues01 = Texture2DSampleLevel(ShadowFactorsTexture, ShadowFactorsSampler, Corner00UV + float2(0, LowResTexelSize.y), 0).xy;
|
|
float2 TextureValues11 = Texture2DSampleLevel(ShadowFactorsTexture, ShadowFactorsSampler, Corner00UV + LowResTexelSize, 0).xy;
|
|
|
|
float4 CornerWeights = float4(
|
|
(1 - BilinearWeights.y) * (1 - BilinearWeights.x),
|
|
(1 - BilinearWeights.y) * BilinearWeights.x,
|
|
BilinearWeights.y * (1 - BilinearWeights.x),
|
|
BilinearWeights.y * BilinearWeights.x);
|
|
|
|
float Epsilon = .0001f;
|
|
|
|
float4 CornerDepths = abs(float4(TextureValues00.y, TextureValues10.y, TextureValues01.y, TextureValues11.y));
|
|
float SceneDepth = CalcSceneDepth(UVAndScreenPos.xy);
|
|
float4 DepthWeights = 1.0f / (abs(CornerDepths - SceneDepth.xxxx) + Epsilon);
|
|
float4 FinalWeights = CornerWeights * DepthWeights;
|
|
|
|
float InterpolatedResult =
|
|
(FinalWeights.x * TextureValues00.x
|
|
+ FinalWeights.y * TextureValues10.x
|
|
+ FinalWeights.z * TextureValues01.x
|
|
+ FinalWeights.w * TextureValues11.x)
|
|
/ dot(FinalWeights, 1);
|
|
|
|
float Output = InterpolatedResult;
|
|
|
|
#else
|
|
float Output = Texture2DSampleLevel(ShadowFactorsTexture, ShadowFactorsSampler, DistanceFieldUVs, 0).x;
|
|
#endif
|
|
|
|
OutColor = EncodeLightAttenuation(half4(Output, Output, Output, Output));
|
|
} |