/** 
 | 
\author Michael Mara and Morgan McGuire, Casual Effects. 2015. 
 | 
*/ 
 | 
  
 | 
#ifndef __SCREEN_SPACE_RAYTRACE__ 
 | 
#define __SCREEN_SPACE_RAYTRACE__ 
 | 
  
 | 
sampler2D_float _CameraDepthTexture; 
 | 
  
 | 
float distanceSquared(float2 A, float2 B) 
 | 
{ 
 | 
    A -= B; 
 | 
    return dot(A, A); 
 | 
} 
 | 
  
 | 
float distanceSquared(float3 A, float3 B) 
 | 
{ 
 | 
    A -= B; 
 | 
    return dot(A, A); 
 | 
} 
 | 
  
 | 
void swap(inout float v0, inout float v1) 
 | 
{ 
 | 
    float temp = v0; 
 | 
    v0 = v1; 
 | 
    v1 = temp; 
 | 
} 
 | 
  
 | 
bool isIntersecting(float rayZMin, float rayZMax, float sceneZ, float layerThickness) 
 | 
{ 
 | 
    return (rayZMax >= sceneZ - layerThickness) && (rayZMin <= sceneZ); 
 | 
} 
 | 
  
 | 
void rayIterations(in bool traceBehindObjects, inout float2 P, inout float stepDirection, inout float end, inout int stepCount, inout int maxSteps, inout bool intersecting, 
 | 
        inout float sceneZ, inout float2 dP, inout float3 Q, inout float3 dQ, inout float k, inout float dk, 
 | 
        inout float rayZMin, inout float rayZMax, inout float prevZMaxEstimate, inout bool permute, inout float2 hitPixel, 
 | 
        inout float2 invSize, inout float layerThickness) 
 | 
{ 
 | 
    bool stop = intersecting; 
 | 
  
 | 
    UNITY_LOOP 
 | 
    for (; (P.x * stepDirection) <= end && stepCount < maxSteps && !stop; P += dP, Q.z += dQ.z, k += dk, stepCount += 1) 
 | 
    { 
 | 
        // The depth range that the ray covers within this loop iteration. 
 | 
        // Assume that the ray is moving in increasing z and swap if backwards. 
 | 
        rayZMin = prevZMaxEstimate; 
 | 
        //rayZMin = (dQ.z * -0.5 + Q.z) / (dk * -0.5 + k); 
 | 
        // Compute the value at 1/2 pixel into the future 
 | 
        rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k); 
 | 
        prevZMaxEstimate = rayZMax; 
 | 
  
 | 
        if (rayZMin > rayZMax) 
 | 
        { 
 | 
            swap(rayZMin, rayZMax); 
 | 
        } 
 | 
  
 | 
        // Undo the homogeneous operation to obtain the camera-space 
 | 
        // Q at each point 
 | 
        hitPixel = permute ? P.yx : P; 
 | 
  
 | 
        sceneZ = tex2Dlod(_CameraDepthTexture, float4(hitPixel * invSize,0,0)).r; 
 | 
        sceneZ = -LinearEyeDepth(sceneZ); 
 | 
  
 | 
        bool isBehind = (rayZMin <= sceneZ); 
 | 
        intersecting = isBehind && (rayZMax >= sceneZ - layerThickness); 
 | 
        stop = traceBehindObjects ? intersecting : isBehind; 
 | 
  
 | 
    } // pixel on ray 
 | 
  
 | 
    P -= dP, Q.z -= dQ.z, k -= dk; 
 | 
} 
 | 
  
 | 
/** 
 | 
  \param csOrigin must have z < -0.01, and project within the valid screen rectangle 
 | 
  \param stepRate Set to 1.0 by default, higher to step faster 
 | 
 */ 
 | 
bool castDenseScreenSpaceRay 
 | 
   (float3          csOrigin, 
 | 
    float3          csDirection, 
 | 
    float4x4        projectToPixelMatrix, 
 | 
    float2          csZBufferSize, 
 | 
    float3          clipInfo, 
 | 
    float           jitterFraction, 
 | 
    int             maxSteps, 
 | 
    float           layerThickness, 
 | 
    float           maxRayTraceDistance, 
 | 
    out float2      hitPixel, 
 | 
    int             stepRate, 
 | 
    bool            traceBehindObjects, 
 | 
    out float3      csHitPoint, 
 | 
    out float       stepCount) { 
 | 
  
 | 
    float2 invSize = float2(1.0 / csZBufferSize.x, 1.0 / csZBufferSize.y); 
 | 
  
 | 
    // Initialize to off screen 
 | 
    hitPixel = float2(-1, -1); 
 | 
  
 | 
    float nearPlaneZ = -0.01; 
 | 
    // Clip ray to a near plane in 3D (doesn't have to be *the* near plane, although that would be a good idea) 
 | 
    float rayLength = ((csOrigin.z + csDirection.z * maxRayTraceDistance) > nearPlaneZ) ? 
 | 
                        ((nearPlaneZ - csOrigin.z) / csDirection.z) : 
 | 
                        maxRayTraceDistance; 
 | 
  
 | 
    float3 csEndPoint = csDirection * rayLength + csOrigin; 
 | 
  
 | 
    // Project into screen space 
 | 
    // This matrix has a lot of zeroes in it. We could expand 
 | 
    // out these multiplies to avoid multiplying by zero 
 | 
    // ...but 16 MADDs are not a big deal compared to what's ahead 
 | 
    float4 H0 = mul(projectToPixelMatrix, float4(csOrigin, 1.0)); 
 | 
    float4 H1 = mul(projectToPixelMatrix, float4(csEndPoint, 1.0)); 
 | 
  
 | 
    // There are a lot of divisions by w that can be turned into multiplications 
 | 
    // at some minor precision loss...and we need to interpolate these 1/w values 
 | 
    // anyway. 
 | 
    // 
 | 
    // Because the caller was required to clip to the near plane, 
 | 
    // this homogeneous division (projecting from 4D to 2D) is guaranteed 
 | 
    // to succeed. 
 | 
    float k0 = 1.0 / H0.w; 
 | 
    float k1 = 1.0 / H1.w; 
 | 
  
 | 
    // Screen-space endpoints 
 | 
    float2 P0 = H0.xy * k0; 
 | 
    float2 P1 = H1.xy * k1; 
 | 
  
 | 
    // Switch the original points to values that interpolate linearly in 2D: 
 | 
    float3 Q0 = csOrigin * k0; 
 | 
    float3 Q1 = csEndPoint * k1; 
 | 
  
 | 
#if 1 // Clipping to the screen coordinates. We could simply modify maxSteps instead 
 | 
    float yMax = csZBufferSize.y - 0.5; 
 | 
    float yMin = 0.5; 
 | 
    float xMax = csZBufferSize.x - 0.5; 
 | 
    float xMin = 0.5; 
 | 
  
 | 
    // 2D interpolation parameter 
 | 
    float alpha = 0.0; 
 | 
    // P0 must be in bounds 
 | 
    if (P1.y > yMax || P1.y < yMin) { 
 | 
        float yClip = (P1.y > yMax) ? yMax : yMin; 
 | 
        float yAlpha = (P1.y - yClip) / (P1.y - P0.y); // Denominator is not zero, since P0 != P1 (or P0 would have been clipped!) 
 | 
        alpha = yAlpha; 
 | 
    } 
 | 
  
 | 
    // P0 must be in bounds 
 | 
    if (P1.x > xMax || P1.x < xMin) { 
 | 
        float xClip = (P1.x > xMax) ? xMax : xMin; 
 | 
        float xAlpha = (P1.x - xClip) / (P1.x - P0.x); // Denominator is not zero, since P0 != P1 (or P0 would have been clipped!) 
 | 
        alpha = max(alpha, xAlpha); 
 | 
    } 
 | 
  
 | 
    // These are all in homogeneous space, so they interpolate linearly 
 | 
    P1 = lerp(P1, P0, alpha); 
 | 
    k1 = lerp(k1, k0, alpha); 
 | 
    Q1 = lerp(Q1, Q0, alpha); 
 | 
#endif 
 | 
  
 | 
    // We're doing this to avoid divide by zero (rays exactly parallel to an eye ray) 
 | 
    P1 = (distanceSquared(P0, P1) < 0.0001) ? P0 + float2(0.01, 0.01) : P1; 
 | 
  
 | 
    float2 delta = P1 - P0; 
 | 
  
 | 
    // Assume horizontal 
 | 
    bool permute = false; 
 | 
    if (abs(delta.x) < abs(delta.y)) { 
 | 
        // More-vertical line. Create a permutation that swaps x and y in the output 
 | 
        permute = true; 
 | 
  
 | 
        // Directly swizzle the inputs 
 | 
        delta = delta.yx; 
 | 
        P1 = P1.yx; 
 | 
        P0 = P0.yx; 
 | 
    } 
 | 
  
 | 
    // From now on, "x" is the primary iteration direction and "y" is the secondary one 
 | 
  
 | 
    float stepDirection = sign(delta.x); 
 | 
    float invdx = stepDirection / delta.x; 
 | 
    float2 dP = float2(stepDirection, invdx * delta.y); 
 | 
  
 | 
    // Track the derivatives of Q and k 
 | 
    float3 dQ = (Q1 - Q0) * invdx; 
 | 
    float   dk = (k1 - k0) * invdx; 
 | 
  
 | 
    dP *= stepRate; 
 | 
    dQ *= stepRate; 
 | 
    dk *= stepRate; 
 | 
  
 | 
    P0 += dP * jitterFraction; 
 | 
    Q0 += dQ * jitterFraction; 
 | 
    k0 += dk * jitterFraction; 
 | 
  
 | 
    // Slide P from P0 to P1, (now-homogeneous) Q from Q0 to Q1, and k from k0 to k1 
 | 
    float3 Q = Q0; 
 | 
    float  k = k0; 
 | 
  
 | 
    // We track the ray depth at +/- 1/2 pixel to treat pixels as clip-space solid 
 | 
    // voxels. Because the depth at -1/2 for a given pixel will be the same as at 
 | 
    // +1/2 for the previous iteration, we actually only have to compute one value 
 | 
    // per iteration. 
 | 
    float prevZMaxEstimate = csOrigin.z; 
 | 
    stepCount = 0.0; 
 | 
    float rayZMax = prevZMaxEstimate, rayZMin = prevZMaxEstimate; 
 | 
    float sceneZ = 100000; 
 | 
  
 | 
    // P1.x is never modified after this point, so pre-scale it by 
 | 
    // the step direction for a signed comparison 
 | 
    float end = P1.x * stepDirection; 
 | 
  
 | 
    bool intersecting = isIntersecting(rayZMin, rayZMax, sceneZ, layerThickness); 
 | 
    // We only advance the z field of Q in the inner loop, since 
 | 
    // Q.xy is never used until after the loop terminates 
 | 
  
 | 
    //int rayIterations = min(maxSteps, stepsToGetOffscreen); 
 | 
  
 | 
  
 | 
    float2 P = P0; 
 | 
  
 | 
    int originalStepCount = 0; 
 | 
    rayIterations(traceBehindObjects, P, stepDirection, end,  originalStepCount,  maxSteps, intersecting, 
 | 
         sceneZ, dP, Q, dQ,  k,  dk, 
 | 
         rayZMin,  rayZMax,  prevZMaxEstimate, permute, hitPixel, 
 | 
         invSize,  layerThickness); 
 | 
  
 | 
  
 | 
    stepCount = originalStepCount; 
 | 
  
 | 
    // Loop only advanced the Z component. Now that we know where we are going 
 | 
    // update xy 
 | 
    Q.xy += dQ.xy * stepCount; 
 | 
    // Q is a vector, so we are trying to get by with 1 division instead of 3. 
 | 
    csHitPoint = Q * (1.0 / k); 
 | 
  
 | 
    return intersecting; 
 | 
} 
 | 
  
 | 
#endif // __SCREEN_SPACE_RAYTRACE__ 
 |