|
|
|
|
|
|
|
|
|
|
struct ScreenSpaceRayHit |
|
|
|
{ |
|
|
|
float distance; // Distance raymarched |
|
|
|
float distanceSS; // Distance raymarched (SS) |
|
|
|
float linearDepth; // Linear depth of the hit point |
|
|
|
uint2 positionSS; // Position of the hit point (SS) |
|
|
|
float2 positionNDC; // Position of the hit point (NDC) |
|
|
|
|
|
|
// Utilities |
|
|
|
// ------------------------------------------------- |
|
|
|
|
|
|
|
// Calculate the ray origin and direction in TXS |
|
|
|
// out positionTXS : (x, y, 1/depth) |
|
|
|
// out rayTXS : (x, y, 1/depth) |
|
|
|
void CalculateRayTXS( |
|
|
|
// Calculate the ray origin and direction in SS |
|
|
|
// out positionSS : (x, y, 1/depth) |
|
|
|
// out raySS : (x, y, 1/depth) |
|
|
|
void CalculateRaySS( |
|
|
|
out float3 positionTXS, |
|
|
|
out float3 rayTXS) |
|
|
|
out float3 positionSS, |
|
|
|
out float3 raySS) |
|
|
|
{ |
|
|
|
float3 positionVS = rayOriginVS; |
|
|
|
float3 rayEndVS = rayOriginVS + rayDirVS * 10; |
|
|
|
|
|
|
float2 positionNDC = ComputeNormalizedDeviceCoordinates(positionVS, projectionMatrix); |
|
|
|
float2 rayEndNDC = ComputeNormalizedDeviceCoordinates(rayEndVS, projectionMatrix); |
|
|
|
|
|
|
|
float3 rayStartTXS = float3( |
|
|
|
float3 rayStartSS = float3( |
|
|
|
float3 rayEndTXS = float3( |
|
|
|
float3 rayEndSS = float3( |
|
|
|
positionTXS = rayStartTXS; |
|
|
|
rayTXS = rayEndTXS - rayStartTXS; |
|
|
|
positionSS = rayStartSS; |
|
|
|
raySS = rayEndSS - rayStartSS; |
|
|
|
} |
|
|
|
|
|
|
|
// Check whether the depth of the ray is above the sampled depth |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Sample the Depth buffer at a specific mip and linear depth |
|
|
|
float LoadDepth(float2 positionTXS, int level) |
|
|
|
float LoadDepth(float2 positionSS, int level) |
|
|
|
float pyramidDepth = LOAD_TEXTURE2D_LOD(_PyramidDepthTexture, int2(positionTXS.xy) >> level, level).r; |
|
|
|
float pyramidDepth = LOAD_TEXTURE2D_LOD(_PyramidDepthTexture, int2(positionSS.xy) >> level, level).r; |
|
|
|
float LoadInvDepth(float2 positionTXS, int level) |
|
|
|
float LoadInvDepth(float2 positionSS, int level) |
|
|
|
float linearDepth = LoadDepth(positionTXS, level); |
|
|
|
float linearDepth = LoadDepth(positionSS, level); |
|
|
|
float invLinearDepth = 1 / linearDepth; |
|
|
|
return invLinearDepth; |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Calculate intersection between the ray and the depth plane |
|
|
|
// positionTXS.z is 1/depth |
|
|
|
// rayTXS.z is 1/depth |
|
|
|
float3 IntersectDepthPlane(float3 positionTXS, float3 rayTXS, float invDepth, out float distance) |
|
|
|
// positionSS.z is 1/depth |
|
|
|
// raySS.z is 1/depth |
|
|
|
float3 IntersectDepthPlane(float3 positionSS, float3 raySS, float invDepth, out float distanceSS) |
|
|
|
// The depth of the intersection with the depth plane is: positionTXS.z + rayTXS.z * t = invDepth |
|
|
|
distance = (invDepth - positionTXS.z) / rayTXS.z; |
|
|
|
// The depth of the intersection with the depth plane is: positionSS.z + raySS.z * t = invDepth |
|
|
|
distanceSS = (invDepth - positionSS.z) / raySS.z; |
|
|
|
distance = distance >= 0.0f ? (distance + EPSILON) : 1E5; |
|
|
|
distanceSS = distanceSS >= 0.0f ? (distanceSS + EPSILON) : 1E5; |
|
|
|
return positionTXS + rayTXS * distance; |
|
|
|
return positionSS + raySS * distanceSS; |
|
|
|
float3 positionTXS, |
|
|
|
float3 rayTXS, |
|
|
|
float2 invRayTXS, |
|
|
|
float3 positionSS, |
|
|
|
float3 raySS, |
|
|
|
float2 invRaySS, |
|
|
|
out float distance) |
|
|
|
out float distanceSS) |
|
|
|
float2 distanceToCellAxes = float2(planes - positionTXS.xy) * invRayTXS; // (distance to x axis, distance to y axis) |
|
|
|
distance = min(distanceToCellAxes.x, distanceToCellAxes.y); |
|
|
|
float2 distanceToCellAxes = float2(planes - positionSS.xy) * invRaySS; // (distance to x axis, distance to y axis) |
|
|
|
distanceSS = min(distanceToCellAxes.x, distanceToCellAxes.y); |
|
|
|
float3 testHitPositionTXS = positionTXS + rayTXS * distance; |
|
|
|
float3 testHitPositionSS = positionSS + raySS * distanceSS; |
|
|
|
testHitPositionTXS.xy += (distanceToCellAxes.x < distanceToCellAxes.y) |
|
|
|
testHitPositionSS.xy += (distanceToCellAxes.x < distanceToCellAxes.y) |
|
|
|
return testHitPositionTXS; |
|
|
|
return testHitPositionSS; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef DEBUG_DISPLAY |
|
|
|
|
|
|
void FillScreenSpaceRaymarchingHitDebug( |
|
|
|
uint2 bufferSize, |
|
|
|
float3 rayDirVS, |
|
|
|
float3 rayTXS, |
|
|
|
float3 startPositionTXS, |
|
|
|
float3 raySS, |
|
|
|
float3 startPositionSS, |
|
|
|
bool hitSuccessful, |
|
|
|
int iteration, |
|
|
|
int maxIterations, |
|
|
|
|
|
|
switch (_DebugLightingSubMode) |
|
|
|
{ |
|
|
|
case DEBUGSCREENSPACETRACING_POSITION_NDC: |
|
|
|
debugOutput = float3(float2(startPositionTXS.xy) / bufferSize, 0); |
|
|
|
debugOutput = float3(float2(startPositionSS.xy) / bufferSize, 0); |
|
|
|
debugOutput = float3(rayTXS.xy * 0.5 + 0.5, frac(0.1 / rayTXS.z)); |
|
|
|
debugOutput = float3(raySS.xy * 0.5 + 0.5, frac(0.1 / raySS.z)); |
|
|
|
debugOutput = frac(hit.distance * 0.1); |
|
|
|
debugOutput = frac(hit.distanceSS * 0.1); |
|
|
|
break; |
|
|
|
case DEBUGSCREENSPACETRACING_HIT_DEPTH: |
|
|
|
debugOutput = frac(hit.linearDepth * 0.1); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void FillScreenSpaceRaymarchingPreLoopDebug( |
|
|
|
float3 startPositionTXS, |
|
|
|
float3 startPositionSS, |
|
|
|
debug.startPositionSSX = uint(startPositionTXS.x); |
|
|
|
debug.startPositionSSY = uint(startPositionTXS.y); |
|
|
|
debug.startLinearDepth = 1 / startPositionTXS.z; |
|
|
|
debug.startPositionSSX = uint(startPositionSS.x); |
|
|
|
debug.startPositionSSY = uint(startPositionSS.y); |
|
|
|
debug.startLinearDepth = 1 / startPositionSS.z; |
|
|
|
float3 rayTXS, |
|
|
|
float3 raySS, |
|
|
|
debug.hitDistance = hit.distance; |
|
|
|
debug.rayTXS = rayTXS; |
|
|
|
debug.raySS = raySS; |
|
|
|
debug.resultHitDepth = hit.linearDepth; |
|
|
|
} |
|
|
|
|
|
|
|
void FillScreenSpaceRaymarchingPreIterationDebug( |
|
|
|
|
|
|
void FillScreenSpaceRaymarchingPostIterationDebug( |
|
|
|
int iteration, |
|
|
|
uint2 cellSize, |
|
|
|
float3 positionTXS, |
|
|
|
float3 positionSS, |
|
|
|
float hitDistanceSS, |
|
|
|
float invHiZDepth, |
|
|
|
inout ScreenSpaceTracingDebug debug) |
|
|
|
{ |
|
|
|
|
|
|
debug.cellSizeH = cellSize.y; |
|
|
|
debug.positionTXS = positionTXS; |
|
|
|
debug.hitLinearDepth = 1 / positionTXS.z; |
|
|
|
debug.hitPositionSS = uint2(positionTXS.xy); |
|
|
|
debug.positionSS = positionSS; |
|
|
|
debug.hitLinearDepth = 1 / positionSS.z; |
|
|
|
debug.hitPositionSS = uint2(positionSS.xy); |
|
|
|
debug.hitDistanceSS = hitDistanceSS; |
|
|
|
debug.hiZLinearDepth = 1 / invHiZDepth; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
{ |
|
|
|
uint mipLevel = clamp(_SSRayMinLevel, 0, int(_PyramidDepthMipSize.z)); |
|
|
|
uint2 bufferSize = uint2(_PyramidDepthMipSize.xy); |
|
|
|
uint2 referencePositionSS = input.referencePositionNDC * bufferSize; |
|
|
|
float depth = LoadDepth(input.referencePositionNDC * (bufferSize >> mipLevel), mipLevel); |
|
|
|
float depth = LoadDepth(referencePositionSS >> mipLevel, mipLevel); |
|
|
|
|
|
|
|
// Calculate projected distance from the ray origin to the depth plane |
|
|
|
float depthFromReference = depth - input.referenceLinearDepth; |
|
|
|
|
|
|
float hitDistance = depthFromRayOrigin / dot(input.depthNormalWS, input.rayDirWS); |
|
|
|
float3 hitPositionWS = input.rayOriginWS + input.rayDirWS * hitDistance; |
|
|
|
|
|
|
|
hit.distance = hitDistance; |
|
|
|
hit.distanceSS = length(hit.positionSS - referencePositionSS); |
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_DISPLAY |
|
|
|
|
|
|
float3(0, 0, 0), // rayTXS |
|
|
|
float3(0, 0, 0), // startPositionTXS |
|
|
|
float3(0, 0, 0), // raySS |
|
|
|
float3(0, 0, 0), // startPositionSS |
|
|
|
true, // hitSuccessful |
|
|
|
1, // iteration |
|
|
|
1, // iterationMax |
|
|
|
|
|
|
FillScreenSpaceRaymarchingPostIterationDebug( |
|
|
|
1, // iteration |
|
|
|
uint2(1, 1), // cellSize |
|
|
|
float3(0, 0, 0), // positionTXS |
|
|
|
float3(0, 0, 0), // positionSS |
|
|
|
hit.distanceSS, // hitDistanceSS |
|
|
|
float3(0, 0, 0), // rayTXS |
|
|
|
float3(0, 0, 0), // raySS |
|
|
|
hit, |
|
|
|
debug); |
|
|
|
_DebugScreenSpaceTracingData[0] = debug; |
|
|
|
|
|
|
int maxMipLevel = min(_SSRayMaxLevel, int(_PyramidDepthMipSize.z)); |
|
|
|
uint2 bufferSize = uint2(_PyramidDepthMipSize.xy); |
|
|
|
|
|
|
|
float3 startPositionTXS; |
|
|
|
float3 rayTXS; |
|
|
|
CalculateRayTXS( |
|
|
|
float3 startPositionSS; |
|
|
|
float3 raySS; |
|
|
|
CalculateRaySS( |
|
|
|
startPositionTXS, |
|
|
|
rayTXS); |
|
|
|
startPositionSS, |
|
|
|
raySS); |
|
|
|
FillScreenSpaceRaymarchingPreLoopDebug(startPositionTXS, debug); |
|
|
|
FillScreenSpaceRaymarchingPreLoopDebug(startPositionSS, debug); |
|
|
|
float2 invRayTXS = float2(1, 1) / rayTXS.xy; |
|
|
|
float2 invRaySS = float2(1, 1) / raySS.xy; |
|
|
|
int2 cellPlanes = sign(rayTXS.xy); |
|
|
|
int2 cellPlanes = sign(raySS.xy); |
|
|
|
float2 crossOffset = CROSS_OFFSET * cellPlanes; |
|
|
|
cellPlanes = clamp(cellPlanes, 0, 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float3 positionTXS = startPositionTXS; |
|
|
|
float3 positionSS = startPositionSS; |
|
|
|
|
|
|
|
while (currentLevel >= minMipLevel) |
|
|
|
{ |
|
|
|
|
|
|
int mipLevelDelta = -1; |
|
|
|
|
|
|
|
// Sampled as 1/Z so it interpolate properly in screen space. |
|
|
|
const float invHiZDepth = LoadInvDepth(positionTXS.xy, currentLevel); |
|
|
|
float iterationDistance = 0; |
|
|
|
const float invHiZDepth = LoadInvDepth(positionSS.xy, currentLevel); |
|
|
|
float iterationDistanceSS = 0; |
|
|
|
if (IsPositionAboveDepth(positionTXS.z, invHiZDepth)) |
|
|
|
if (IsPositionAboveDepth(positionSS.z, invHiZDepth)) |
|
|
|
float3 candidatePositionTXS = IntersectDepthPlane(positionTXS, rayTXS, invHiZDepth, iterationDistance); |
|
|
|
float3 candidatePositionSS = IntersectDepthPlane(positionSS, raySS, invHiZDepth, iterationDistanceSS); |
|
|
|
const int2 cellId = int2(positionTXS.xy) / cellSize; |
|
|
|
const int2 candidateCellId = int2(candidatePositionTXS.xy) / cellSize; |
|
|
|
const int2 cellId = int2(positionSS.xy) / cellSize; |
|
|
|
const int2 candidateCellId = int2(candidatePositionSS.xy) / cellSize; |
|
|
|
candidatePositionTXS = IntersectCellPlanes( |
|
|
|
positionTXS, |
|
|
|
rayTXS, |
|
|
|
invRayTXS, |
|
|
|
candidatePositionSS = IntersectCellPlanes( |
|
|
|
positionSS, |
|
|
|
raySS, |
|
|
|
invRaySS, |
|
|
|
iterationDistance); |
|
|
|
iterationDistanceSS); |
|
|
|
positionTXS = candidatePositionTXS; |
|
|
|
positionSS = candidatePositionSS; |
|
|
|
hit.distance += iterationDistance; |
|
|
|
hit.distanceSS += iterationDistanceSS; |
|
|
|
|
|
|
|
currentLevel = min(currentLevel + mipLevelDelta, maxMipLevel); |
|
|
|
|
|
|
|
|
|
|
iteration, |
|
|
|
cellSize, |
|
|
|
positionTXS, |
|
|
|
iterationDistance, |
|
|
|
positionSS, |
|
|
|
iterationDistanceSS, |
|
|
|
hit.distanceSS, |
|
|
|
if (any(int2(positionTXS.xy) > bufferSize) |
|
|
|
|| any(positionTXS.xy < 0)) |
|
|
|
if (any(int2(positionSS.xy) > bufferSize) |
|
|
|
|| any(positionSS.xy < 0)) |
|
|
|
{ |
|
|
|
hitSuccessful = false; |
|
|
|
break; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
hit.linearDepth = 1 / positionTXS.z; |
|
|
|
hit.positionNDC = float2(positionTXS.xy) / float2(bufferSize); |
|
|
|
hit.positionSS = uint2(positionTXS.xy); |
|
|
|
hit.linearDepth = 1 / positionSS.z; |
|
|
|
hit.positionNDC = float2(positionSS.xy) / float2(bufferSize); |
|
|
|
hit.positionSS = uint2(positionSS.xy); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef DEBUG_DISPLAY |
|
|
|
|
|
|
rayTXS, |
|
|
|
raySS, |
|
|
|
bufferSize, input.rayDirVS, rayTXS, startPositionTXS, hitSuccessful, iteration, MAX_ITERATIONS, maxMipLevel, maxUsedLevel, |
|
|
|
bufferSize, input.rayDirVS, raySS, startPositionSS, hitSuccessful, iteration, MAX_ITERATIONS, maxMipLevel, maxUsedLevel, |
|
|
|
hit); |
|
|
|
if (input.writeStepDebug) |
|
|
|
_DebugScreenSpaceTracingData[0] = debug; |
|
|
|
|
|
|
int level = clamp(_SSRayMinLevel, 0, int(_PyramidDepthMipSize.z)); |
|
|
|
uint2 bufferSize = uint2(_PyramidDepthMipSize.xy); |
|
|
|
|
|
|
|
float3 startPositionTXS; |
|
|
|
float3 rayTXS; |
|
|
|
CalculateRayTXS( |
|
|
|
float3 startPositionSS; |
|
|
|
float3 raySS; |
|
|
|
CalculateRaySS( |
|
|
|
startPositionTXS, |
|
|
|
rayTXS); |
|
|
|
startPositionSS, |
|
|
|
raySS); |
|
|
|
FillScreenSpaceRaymarchingPreLoopDebug(startPositionTXS, debug); |
|
|
|
FillScreenSpaceRaymarchingPreLoopDebug(startPositionSS, debug); |
|
|
|
float maxAbsAxis = max(abs(rayTXS.x), abs(rayTXS.y)); |
|
|
|
float maxAbsAxis = max(abs(raySS.x), abs(raySS.y)); |
|
|
|
hit.distance = 1 / startPositionTXS.z; |
|
|
|
hit.linearDepth = 1 / startPositionTXS.z; |
|
|
|
hit.positionSS = uint2(startPositionTXS.xy); |
|
|
|
hit.distanceSS = 1 / startPositionSS.z; |
|
|
|
hit.linearDepth = 1 / startPositionSS.z; |
|
|
|
hit.positionSS = uint2(startPositionSS.xy); |
|
|
|
rayTXS /= max(abs(rayTXS.x), abs(rayTXS.y)); |
|
|
|
rayTXS *= _SSRayMinLevel; |
|
|
|
raySS /= max(abs(raySS.x), abs(raySS.y)); |
|
|
|
raySS *= _SSRayMinLevel; |
|
|
|
float3 positionTXS = startPositionTXS; |
|
|
|
float distanceStepSS = length(raySS.xy); |
|
|
|
|
|
|
|
float3 positionSS = startPositionSS; |
|
|
|
// TODO: We should have a for loop from the starting point to the far/near plane |
|
|
|
while (iteration < MAX_ITERATIONS) |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
positionTXS += rayTXS; |
|
|
|
float invHiZDepth = LoadInvDepth(positionTXS.xy, _SSRayMinLevel); |
|
|
|
positionSS += raySS; |
|
|
|
hit.distanceSS += distanceStepSS; |
|
|
|
float invHiZDepth = LoadInvDepth(positionSS.xy, _SSRayMinLevel); |
|
|
|
positionTXS, |
|
|
|
1 / rayTXS.z, |
|
|
|
positionSS, |
|
|
|
1 / raySS.z, |
|
|
|
hit.distanceSS, |
|
|
|
if (!IsPositionAboveDepth(positionTXS.z, invHiZDepth)) |
|
|
|
if (!IsPositionAboveDepth(positionSS.z, invHiZDepth)) |
|
|
|
{ |
|
|
|
hitSuccessful = true; |
|
|
|
break; |
|
|
|
|
|
|
if (any(int2(positionTXS.xy) > bufferSize) |
|
|
|
|| any(positionTXS.xy < 0)) |
|
|
|
if (any(int2(positionSS.xy) > bufferSize) |
|
|
|
|| any(positionSS.xy < 0)) |
|
|
|
{ |
|
|
|
hitSuccessful = false; |
|
|
|
break; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
hit.linearDepth = 1 / positionTXS.z; |
|
|
|
hit.positionNDC = float2(positionTXS.xy) / float2(bufferSize); |
|
|
|
hit.positionSS = uint2(positionTXS.xy); |
|
|
|
hit.linearDepth = 1 / positionSS.z; |
|
|
|
hit.positionNDC = float2(positionSS.xy) / float2(bufferSize); |
|
|
|
hit.positionSS = uint2(positionSS.xy); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef DEBUG_DISPLAY |
|
|
|
|
|
|
rayTXS, |
|
|
|
raySS, |
|
|
|
bufferSize, input.rayDirVS, rayTXS, startPositionTXS, hitSuccessful, iteration, MAX_ITERATIONS, 0, 0, |
|
|
|
bufferSize, input.rayDirVS, raySS, startPositionSS, hitSuccessful, iteration, MAX_ITERATIONS, 0, 0, |
|
|
|
hit); |
|
|
|
if (input.writeStepDebug) |
|
|
|
_DebugScreenSpaceTracingData[0] = debug; |
|
|
|