| /** | 
| \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__ |