您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

498 行
16 KiB

#ifndef UNITY_SCREEN_SPACE_TRACING_INCLUDED
#define UNITY_SCREEN_SPACE_TRACING_INCLUDED
// -------------------------------------------------
// Algorithm uniform parameters
// -------------------------------------------------
const float DepthPlaneBias = 1E-5;
// -------------------------------------------------
// Output
// -------------------------------------------------
struct ScreenSpaceRayHit
{
uint2 positionSS; // Position of the hit point (SS)
float2 positionNDC; // Position of the hit point (NDC)
float linearDepth; // Linear depth of the hit point
#ifdef DEBUG_DISPLAY
float3 debugOutput;
#endif
};
struct ScreenSpaceHiZRaymarchInput
{
float3 rayOriginWS; // Ray origin (WS)
float3 rayDirWS; // Ray direction (WS)
uint maxIterations; // Number of iterations before failing
#ifdef DEBUG_DISPLAY
bool debug;
#endif
};
struct ScreenSpaceProxyRaycastInput
{
float3 rayOriginWS; // Ray origin (WS)
float3 rayDirWS; // Ray direction (WS)
EnvLightData proxyData;
#ifdef DEBUG_DISPLAY
bool debug;
#endif
};
// -------------------------------------------------
// Utilities
// -------------------------------------------------
// Calculate the ray origin and direction in SS
// out positionSS : (x, y, 1/depth)
// out raySS : (x, y, 1/depth)
void CalculateRaySS(
float3 rayOriginWS,
float3 rayDirWS,
uint2 bufferSize,
out float3 positionSS,
out float3 raySS
)
{
float3 positionWS = rayOriginWS;
float3 rayEndWS = rayOriginWS + rayDirWS * 10;
float4 positionCS = ComputeClipSpacePosition(positionWS, GetWorldToHClipMatrix());
float4 rayEndCS = ComputeClipSpacePosition(rayEndWS, GetWorldToHClipMatrix());
float2 positionNDC = ComputeNormalizedDeviceCoordinates(positionWS, GetWorldToHClipMatrix());
float2 rayEndNDC = ComputeNormalizedDeviceCoordinates(rayEndWS, GetWorldToHClipMatrix());
float3 rayStartSS = float3(
positionNDC.xy * bufferSize,
1.0 / positionCS.w); // Screen space depth interpolate properly in 1/z
float3 rayEndSS = float3(
rayEndNDC.xy * bufferSize,
1.0 / rayEndCS.w); // Screen space depth interpolate properly in 1/z
positionSS = rayStartSS;
raySS = rayEndSS - rayStartSS;
}
// Check whether the depth of the ray is above the sampled depth
// Arguments are inversed linear depth
bool IsPositionAboveDepth(float rayDepth, float invLinearDepth)
{
// as depth is inverted, we must invert the check as well
// rayZ > HiZ <=> 1/rayZ < 1/HiZ
return rayDepth > invLinearDepth;
}
// Sample the Depth buffer at a specific mip and linear depth
float LoadDepth(float2 positionSS, int level)
{
float pyramidDepth = LOAD_TEXTURE2D_LOD(_DepthPyramidTexture, int2(positionSS.xy) >> level, level).r;
float linearDepth = LinearEyeDepth(pyramidDepth, _ZBufferParams);
return linearDepth;
}
// Sample the Depth buffer at a specific mip and return 1/linear depth
float LoadInvDepth(float2 positionSS, int level)
{
float linearDepth = LoadDepth(positionSS, level);
float invLinearDepth = 1 / linearDepth;
return invLinearDepth;
}
bool CellAreEquals(int2 cellA, int2 cellB)
{
return cellA.x == cellB.x && cellA.y == cellB.y;
}
// Calculate intersection between the ray and the depth plane
// positionSS.z is 1/depth
// raySS.z is 1/depth
float3 IntersectDepthPlane(float3 positionSS, float3 raySS, float invDepth)
{
// The depth of the intersection with the depth plane is: positionSS.z + raySS.z * t = invDepth
float t = (invDepth - positionSS.z) / raySS.z;
// (t<0) When the ray is going away from the depth plane,
// put the intersection away.
// Instead the intersection with the next tile will be used.
// (t>=0) Add a small distance to go through the depth plane.
t = t >= 0.0f ? (t + DepthPlaneBias) : 1E5;
// Return the point on the ray
return positionSS + raySS * t;
}
// Calculate intersection between a ray and a cell
float3 IntersectCellPlanes(
float3 positionSS,
float3 raySS,
float2 invRaySS,
int2 cellId,
uint2 cellSize,
int2 cellPlanes,
float2 crossOffset
)
{
const float SQRT_2 = sqrt(2);
const float CellPlaneBias = 1E-2;
// Planes to check
int2 planes = (cellId + cellPlanes) * cellSize;
// Hit distance to each planes
float2 distanceToCellAxes = float2(planes - positionSS.xy) * invRaySS; // (distance to x axis, distance to y axis)
float t = min(distanceToCellAxes.x, distanceToCellAxes.y)
// Offset by 1E-3 to ensure cell boundary crossing
// This assume that length(raySS.xy) == 1;
+ CellPlaneBias;
// Interpolate screen space to get next test point
float3 testHitPositionSS = positionSS + raySS * t;
return testHitPositionSS;
}
#ifdef DEBUG_DISPLAY
// -------------------------------------------------
// Debug Utilities
// -------------------------------------------------
void DebugComputeCommonOutput(
float3 rayDirWS,
bool hitSuccessful,
inout ScreenSpaceRayHit hit
)
{
switch (_DebugLightingSubMode)
{
case DEBUGSCREENSPACETRACING_RAY_DIR_WS:
hit.debugOutput = rayDirWS * 0.5 + 0.5;
break;
case DEBUGSCREENSPACETRACING_HIT_DEPTH:
hit.debugOutput = frac(hit.linearDepth * 0.1);
break;
case DEBUGSCREENSPACETRACING_HIT_SUCCESS:
hit.debugOutput = GetIndexColor(hitSuccessful ? 1 : 2);
break;
}
}
void DebugComputeHiZOutput(
int iteration,
float3 startPositionSS,
float3 rayDirSS,
int maxIterations,
int maxUsedLevel,
int maxMipLevel,
int intersectionKind,
inout ScreenSpaceRayHit hit
)
{
switch (_DebugLightingSubMode)
{
case DEBUGSCREENSPACETRACING_HI_ZPOSITION_NDC:
hit.debugOutput = float3(float2(startPositionSS.xy) * _ScreenSize.zw, 0);
break;
case DEBUGSCREENSPACETRACING_HI_ZITERATION_COUNT:
hit.debugOutput = float(iteration) / float(maxIterations);
break;
case DEBUGSCREENSPACETRACING_HI_ZRAY_DIR_NDC:
hit.debugOutput = float3(rayDirSS.xy * 0.5 + 0.5, frac(0.1 / rayDirSS.z));
break;
case DEBUGSCREENSPACETRACING_HI_ZMAX_USED_MIP_LEVEL:
hit.debugOutput = float(maxUsedLevel) / float(maxMipLevel);
break;
case DEBUGSCREENSPACETRACING_HI_ZINTERSECTION_KIND:
hit.debugOutput = GetIndexColor(intersectionKind);
break;
}
}
#endif
#endif
// -------------------------------------------------
// Algorithm: Proxy raycast
// -------------------------------------------------
#ifdef SSRTID
#define SSRT_SETTING(name, SSRTID) _SS ## SSRTID ## name
#define SSRT_FUNC(name, SSRTID) name ## SSRTID
CBUFFER_START(SSRT_FUNC(UnityScreenSpaceRaymarching, SSRTID))
int SSRT_SETTING(RayMinLevel, SSRTID);
int SSRT_SETTING(RayMaxLevel, SSRTID);
int SSRT_SETTING(RayMaxIterations, SSRTID);
float SSRT_SETTING(RayDepthSuccessBias, SSRTID);
CBUFFER_END
bool SSRT_FUNC(ScreenSpaceProxyRaycast, SSRTID)(
ScreenSpaceProxyRaycastInput input,
out ScreenSpaceRayHit hit
)
{
ZERO_INITIALIZE(ScreenSpaceRayHit, hit);
float3x3 worldToPS = WorldToProxySpace(input.proxyData);
float3 rayOriginPS = WorldToProxyPosition(input.proxyData, worldToPS, input.rayOriginWS);
float3 rayDirPS = mul(input.rayDirWS, worldToPS);
float projectionDistance = 0.0;
switch(input.proxyData.influenceShapeType)
{
case ENVSHAPETYPE_SPHERE:
case ENVSHAPETYPE_SKY:
{
projectionDistance = IntersectSphereProxy(input.proxyData, rayDirPS, rayOriginPS);
break;
}
case ENVSHAPETYPE_BOX:
projectionDistance = IntersectBoxProxy(input.proxyData, rayDirPS, rayOriginPS);
break;
}
float3 hitPositionWS = input.rayOriginWS + input.rayDirWS * projectionDistance;
float4 hitPositionCS = ComputeClipSpacePosition(hitPositionWS, GetWorldToHClipMatrix());
float4 rayOriginCS = ComputeClipSpacePosition(input.rayOriginWS, GetWorldToHClipMatrix());
float2 hitPositionNDC = ComputeNormalizedDeviceCoordinates(hitPositionWS, GetWorldToHClipMatrix());
uint2 hitPositionSS = uint2(hitPositionNDC *_ScreenSize.xy);
float hitLinearDepth = hitPositionCS.w;
hit.positionNDC = hitPositionNDC;
hit.positionSS = hitPositionSS;
hit.linearDepth = hitLinearDepth;
bool hitSuccessful = hitLinearDepth > 0; // Negative means that the hit is behind the camera
#ifdef DEBUG_DISPLAY
DebugComputeCommonOutput(input.rayDirWS, hitSuccessful, hit);
if (input.debug && _DebugScreenSpaceTracingData[0].tracingModel == -1)
{
ScreenSpaceTracingDebug debug;
ZERO_INITIALIZE(ScreenSpaceTracingDebug, debug);
float2 rayOriginNDC = ComputeNormalizedDeviceCoordinates(input.rayOriginWS, GetWorldToHClipMatrix());
uint2 rayOriginSS = uint2(rayOriginNDC * _ScreenSize.xy);
debug.tracingModel = PROJECTIONMODEL_PROXY;
debug.loopStartPositionSSX = rayOriginSS.x;
debug.loopStartPositionSSY = rayOriginSS.y;
debug.loopStartLinearDepth = rayOriginCS.w;
debug.endHitSuccess = hitSuccessful;
debug.endLinearDepth = hitLinearDepth;
debug.endPositionSSX = hitPositionSS.x;
debug.endPositionSSY = hitPositionSS.y;
debug.proxyShapeType = input.proxyData.influenceShapeType;
debug.projectionDistance = projectionDistance;
_DebugScreenSpaceTracingData[0] = debug;
}
#endif
return hitSuccessful;
}
// -------------------------------------------------
// Algorithm: HiZ raymarching
// -------------------------------------------------
// Based on Yasin Uludag, 2014. "Hi-Z Screen-Space Cone-Traced Reflections", GPU Pro5: Advanced Rendering Techniques
bool SSRT_FUNC(ScreenSpaceHiZRaymarch, SSRTID)(
ScreenSpaceHiZRaymarchInput input,
out ScreenSpaceRayHit hit
)
{
const float2 CROSS_OFFSET = float2(1, 1);
// Initialize loop
ZERO_INITIALIZE(ScreenSpaceRayHit, hit);
bool hitSuccessful = true;
uint iteration = 0u;
int minMipLevel = max(SSRT_SETTING(RayMinLevel, SSRTID), 0);
int maxMipLevel = min(SSRT_SETTING(RayMaxLevel, SSRTID), int(_DepthPyramidScale.z));
uint2 bufferSize = uint2(_DepthPyramidSize.xy);
uint maxIterations = min(input.maxIterations, SSRT_SETTING(RayMaxIterations, SSRTID));
float3 startPositionSS;
float3 raySS;
CalculateRaySS(
input.rayOriginWS,
input.rayDirWS,
bufferSize,
startPositionSS,
raySS
);
#ifdef DEBUG_DISPLAY
// Initialize debug variables
int debugLoopMipMaxUsedLevel = minMipLevel;
int debugIterationMipLevel = minMipLevel;
uint2 debugIterationCellSize = uint2(0u, 0u);
float3 debugIterationPositionSS = float3(0, 0, 0);
uint debugIteration = 0u;
uint debugIterationIntersectionKind = 0u;
float debugIterationLinearDepthBuffer = 0;
#endif
int intersectionKind = 0;
float raySSLength = length(raySS.xy);
raySS /= raySSLength;
// Initialize raymarching
float2 invRaySS = float2(1, 1) / raySS.xy;
// Calculate planes to intersect for each cell
int2 cellPlanes = sign(raySS.xy);
float2 crossOffset = CROSS_OFFSET * cellPlanes;
cellPlanes = clamp(cellPlanes, 0, 1);
int currentLevel = minMipLevel;
uint2 cellCount = bufferSize >> currentLevel;
uint2 cellSize = uint2(1, 1) << currentLevel;
float3 positionSS = startPositionSS;
float invHiZDepth = 0;
while (currentLevel >= minMipLevel)
{
if (iteration >= maxIterations)
{
hitSuccessful = false;
break;
}
cellCount = bufferSize >> currentLevel;
cellSize = uint2(1, 1) << currentLevel;
#ifdef DEBUG_DISPLAY
// Fetch pre iteration debug values
if (input.debug && _DebugStep >= iteration)
debugIterationMipLevel = currentLevel;
#endif
// Go down in HiZ levels by default
int mipLevelDelta = -1;
// Sampled as 1/Z so it interpolate properly in screen space.
invHiZDepth = LoadInvDepth(positionSS.xy, currentLevel);
intersectionKind = HIZINTERSECTIONKIND_NONE;
if (IsPositionAboveDepth(positionSS.z, invHiZDepth))
{
float3 candidatePositionSS = IntersectDepthPlane(positionSS, raySS, invHiZDepth);
intersectionKind = HIZINTERSECTIONKIND_DEPTH;
const int2 cellId = int2(positionSS.xy) / cellSize;
const int2 candidateCellId = int2(candidatePositionSS.xy) / cellSize;
// If we crossed the current cell
if (!CellAreEquals(cellId, candidateCellId))
{
candidatePositionSS = IntersectCellPlanes(
positionSS,
raySS,
invRaySS,
cellId,
cellSize,
cellPlanes,
crossOffset);
intersectionKind = HIZINTERSECTIONKIND_CELL;
// Go up a level to go faster
mipLevelDelta = 1;
}
positionSS = candidatePositionSS;
}
currentLevel = min(currentLevel + mipLevelDelta, maxMipLevel);
#ifdef DEBUG_DISPLAY
// Fetch post iteration debug values
if (input.debug && _DebugStep >= iteration)
{
debugLoopMipMaxUsedLevel = max(debugLoopMipMaxUsedLevel, currentLevel);
debugIterationPositionSS = positionSS;
debugIterationLinearDepthBuffer = 1 / invHiZDepth;
debugIteration = iteration;
debugIterationIntersectionKind = intersectionKind;
debugIterationCellSize = cellSize;
}
#endif
// Check if we are out of the buffer
if (any(int2(positionSS.xy) > int2(bufferSize))
|| any(positionSS.xy < 0)
)
{
hitSuccessful = false;
break;
}
++iteration;
}
hit.linearDepth = 1 / positionSS.z;
hit.positionNDC = float2(positionSS.xy) / float2(bufferSize);
hit.positionSS = uint2(positionSS.xy);
if (hit.linearDepth > (1 / invHiZDepth) + SSRT_SETTING(RayDepthSuccessBias, SSRTID))
hitSuccessful = false;
#ifdef DEBUG_DISPLAY
DebugComputeCommonOutput(input.rayDirWS, hitSuccessful, hit);
DebugComputeHiZOutput(
iteration,
startPositionSS,
raySS,
SSRT_SETTING(RayMaxIterations, SSRTID),
debugLoopMipMaxUsedLevel,
maxMipLevel,
intersectionKind,
hit
);
if (input.debug && _DebugScreenSpaceTracingData[0].tracingModel == -1)
{
// Build debug structure
ScreenSpaceTracingDebug debug;
ZERO_INITIALIZE(ScreenSpaceTracingDebug, debug);
debug.tracingModel = PROJECTIONMODEL_HI_Z;
debug.loopStartPositionSSX = uint(startPositionSS.x);
debug.loopStartPositionSSY = uint(startPositionSS.y);
debug.loopStartLinearDepth = 1 / startPositionSS.z;
debug.loopRayDirectionSS = raySS;
debug.loopMipLevelMax = debugLoopMipMaxUsedLevel;
debug.loopIterationMax = iteration;
debug.iterationPositionSS = debugIterationPositionSS;
debug.iterationMipLevel = debugIterationMipLevel;
debug.iteration = debugIteration;
debug.iterationLinearDepthBuffer = debugIterationLinearDepthBuffer;
debug.iterationIntersectionKind = debugIterationIntersectionKind;
debug.iterationCellSizeW = debugIterationCellSize.x;
debug.iterationCellSizeH = debugIterationCellSize.y;
debug.endHitSuccess = hitSuccessful;
debug.endLinearDepth = hit.linearDepth;
debug.endPositionSSX = hit.positionSS.x;
debug.endPositionSSY = hit.positionSS.y;
_DebugScreenSpaceTracingData[0] = debug;
}
#endif
return hitSuccessful;
}
#undef SSRT_SETTING
#undef SSRT_FUNC
#endif