This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl

163 lines
4.6 KiB
GLSL

/* Based on work from Morgan McGuire and Michael Mara at Williams College 2014
* Released as open source under the BSD 2-Clause License
* http://opensource.org/licenses/BSD-2-Clause
* http://casual-effects.blogspot.fr/2014/08/screen-space-ray-tracing.html */
#define MAX_STEP 256
uniform mat4 PixelProjMatrix; /* View > NDC > Texel : maps view coords to texel coord */
void swapIfBigger(inout float a, inout float b)
{
if (a > b) {
float temp = a;
a = b;
b = temp;
}
}
/* Return the length of the ray if there is a hit, and -1.0 if not hit occured */
float raycast(sampler2D depth_texture, vec3 ray_origin, vec3 ray_dir)
{
float near = get_view_z_from_depth(0.0); /* TODO optimize */
/* Clip ray to a near plane in 3D */
float ray_length = 1e16;
if ((ray_origin.z + ray_dir.z * ray_length) > near)
ray_length = (near - ray_origin.z) / ray_dir.z;
vec3 ray_end = ray_dir * ray_length + ray_origin;
/* Project into screen space */
vec4 H0 = PixelProjMatrix * vec4(ray_origin, 1.0);
vec4 H1 = PixelProjMatrix * vec4(ray_end, 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. */
float k0 = 1.0 / H0.w;
float k1 = 1.0 / H1.w;
/* Switch the original points to values that interpolate linearly in 2D */
vec3 Q0 = ray_origin * k0;
vec3 Q1 = ray_end * k1;
/* Screen-space endpoints */
vec2 P0 = H0.xy * k0;
vec2 P1 = H1.xy * k1;
/* [Optional clipping to frustum sides here] */
/* Initialize to off screen */
vec2 hitpixel = vec2(-1.0, -1.0);
/* If the line is degenerate, make it cover at least one pixel
* to not have to handle zero-pixel extent as a special case later */
P1 += vec2((distance_squared(P0, P1) < 0.0001) ? 0.01 : 0.0);
vec2 delta = P1 - P0;
/* Permute so that the primary iteration is in x to reduce large branches later.
* After this, "x" is the primary iteration direction and "y" is the secondary one
* If it is a more-vertical line, create a permutation that swaps x and y in the output
* and directly swizzle the inputs. */
bool permute = false;
if (abs(delta.x) < abs(delta.y)) {
permute = true;
delta = delta.yx;
P1 = P1.yx;
P0 = P0.yx;
}
/* Track the derivatives */
float step_sign = sign(delta.x);
float invdx = step_sign / delta.x;
vec2 dP = vec2(step_sign, invdx * delta.y);
vec3 dQ = (Q1 - Q0) * invdx;
float dk = (k1 - k0) * invdx;
/* Slide each value from the start of the ray to the end */
vec4 pqk = vec4(P0, Q0.z, k0);
/* Scale derivatives by the desired pixel stride */
vec4 dPQK = vec4(dP, dQ.z, dk) * 8.0;
/* 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 prev_zmax = ray_origin.z;
float zmax, zmin;
/* P1.x is never modified after this point, so pre-scale it by
* the step direction for a signed comparison */
float end = P1.x * step_sign;
bool hit = false;
float hitstep, raw_depth, view_depth;
for (hitstep = 0.0; hitstep < MAX_STEP && !hit; hitstep++) {
/* Ray finished & no hit*/
if ((pqk.x * step_sign) > end) break;
/* step through current cell */
pqk += dPQK;
hitpixel = permute ? pqk.yx : pqk.xy;
zmin = prev_zmax;
zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
prev_zmax = zmax;
swapIfBigger(zmin, zmax);
raw_depth = texelFetch(depth_texture, ivec2(hitpixel), 0).r;
view_depth = get_view_z_from_depth(raw_depth);
if (zmax < view_depth) {
/* Below surface, cannot trace further */
hit = true;
}
}
if (hit) {
/* Rewind back a step */
pqk -= dPQK;
/* And do a finer trace over this segment */
dPQK /= 16.0;
for (float refinestep = 0.0; refinestep < 16.0; refinestep++) {
/* step through current cell */
pqk += dPQK;
hitpixel = permute ? pqk.yx : pqk.xy;
zmin = prev_zmax;
zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
prev_zmax = zmax;
swapIfBigger(zmin, zmax);
raw_depth = texelFetch(depth_texture, ivec2(hitpixel), 0).r;
view_depth = get_view_z_from_depth(raw_depth);
if (zmax < view_depth) {
/* Below surface, cannot trace further */
break;
}
}
}
/* Check if we are somewhere near the surface. */
/* TODO user threshold */
float threshold = 0.1; /* In clip space */
if (zmax < view_depth - threshold) {
hit = false;
}
/* Check failure cases (out of screen, hit background) */
if (hit && (raw_depth != 1.0) && (raw_depth != 0.0)) {
/* Return length */
return (zmax - ray_origin.z) / ray_dir.z;
}
/* Failure, return no hit */
return -1.0;
}