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