diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2017-07-18 17:48:24 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2017-07-24 16:28:27 +0300 |
commit | 3be8ab881ed4a085778593ccae59a6f0e1b4bffa (patch) | |
tree | 50d7ee3cf173e280746fb2faca49c477b426e357 /source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl | |
parent | 19323a0ff550deea2c6ec8c945059c8927a8cedb (diff) |
Eevee: SSR: Add simple raytracing.
Still imprecise.
Diffstat (limited to 'source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl')
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl new file mode 100644 index 00000000000..6f6ced3c668 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl @@ -0,0 +1,162 @@ +/* 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) * 4.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, refinestep, 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 /= 4.0; + + for (refinestep = 0.0; refinestep < 4.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.05 / pqk.w; /* In view 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; +} |