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
Clément Foucault 4fd70c99a5 Eevee: SSR: Add support for planar probes.
This add the possibility to use planar probe informations to create SSR.
This has 2 advantages:
- Tracing is less expensive since the hit is found much quicker.
- We have much less artifact due to missing information.

There is still area for improvement.
2017-07-25 22:07:35 +02:00

197 lines
5.7 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
#define MAX_REFINE_STEP 32 /* Should be max allowed stride */
uniform mat4 PixelProjMatrix; /* View > NDC > Texel : maps view coords to texel coord */
uniform vec2 ssrParameters;
uniform sampler2D depthBuffer;
uniform sampler2DArray planarDepth;
#define ssrStride ssrParameters.x
#define ssrThickness ssrParameters.y
float sample_depth(ivec2 hitpixel, int index)
{
if (index > -1) {
return texelFetch(planarDepth, ivec3(hitpixel, index), 0).r;
}
else {
return texelFetch(depthBuffer, hitpixel, 0).r;
}
}
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 negate it if not hit occured */
float raycast(int index, vec3 ray_origin, vec3 ray_dir, float ray_jitter)
{
float near = get_view_z_from_depth(0.0); /* TODO optimize */
float far = get_view_z_from_depth(1.0); /* TODO optimize */
/* Clip ray to a near/far 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;
}
else {
ray_length = (ray_origin.z - far) / -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] */
/* 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.001) ? 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) * ssrStride;
/* 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;
/* 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;
/* Initial offset */
if (index > -1) {
pqk -= dPQK * ray_jitter;
}
else {
pqk += dPQK * (0.01 + ray_jitter);
}
bool hit = false;
float raw_depth;
float thickness = (index == -1) ? ssrThickness : 1e16;
for (float 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;
ivec2 hitpixel = ivec2(permute ? pqk.yx : pqk.xy);
raw_depth = sample_depth(hitpixel, index);
float zmin = prev_zmax;
zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
prev_zmax = zmax;
swapIfBigger(zmin, zmax);
float vmax = get_view_z_from_depth(raw_depth);
float vmin = vmax - thickness;
/* Check if we are somewhere near the surface. */
/* Note: we consider hitting the screen borders (raw_depth == 0.0)
* as valid to check for occluder in the refine pass */
if (!((zmin > vmax) || (zmax < vmin)) || (raw_depth == 0.0)) {
/* Below surface, cannot trace further */
hit = true;
}
}
if (hit) {
/* Rewind back a step. */
pqk -= dPQK;
/* And do a finer trace over this segment. */
dPQK /= ssrStride;
prev_zmax = (dPQK.z * -0.5 + pqk.z) / (dPQK.w * -0.5 + pqk.w);
for (float refinestep = 0.0; refinestep < (ssrStride * 2.0) && refinestep < (MAX_REFINE_STEP * 2.0); refinestep++) {
/* step through current cell */
pqk += dPQK;
ivec2 hitpixel = ivec2(permute ? pqk.yx : pqk.xy);
raw_depth = sample_depth(hitpixel, index);
float zmin = prev_zmax;
zmax = (dPQK.z * 0.5 + pqk.z) / (dPQK.w * 0.5 + pqk.w);
prev_zmax = zmax;
swapIfBigger(zmin, zmax);
float vmax = get_view_z_from_depth(raw_depth);
float vmin = vmax - thickness;
/* Check if we are somewhere near the surface. */
if (!((zmin > vmax) || (zmax < vmin)) || (raw_depth == 0.0)) {
/* Below surface, cannot trace further */
break;
}
}
}
/* If we did hit the background, get exact ray. */
if (raw_depth == 1.0) {
zmax = get_view_z_from_depth(1.0); /* TODO optimize */
}
hit = hit && (raw_depth != 0.0);
/* Return length */
float result = (zmax - ray_origin.z) / ray_dir.z;
return (hit) ? result : -result;
}