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

229 lines
7.8 KiB
GLSL

#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
/**
* Screen-Space Raytracing functions.
*/
uniform sampler2D maxzBuffer;
uniform sampler2DArray planarDepth;
struct Ray {
vec3 origin;
/* Ray direction premultiplied by its maximum length. */
vec3 direction;
};
/* Inputs expected to be in viewspace. */
void raytrace_clip_ray_to_near_plane(inout Ray ray)
{
float near_dist = get_view_z_from_depth(0.0);
if ((ray.origin.z + ray.direction.z) > near_dist) {
ray.direction *= abs((near_dist - ray.origin.z) / ray.direction.z);
}
}
/* Screenspace ray ([0..1] "uv" range) where direction is normalize to be as small as one
* full-resolution pixel. The ray is also clipped to all frustum sides.
*/
struct ScreenSpaceRay {
vec4 origin;
vec4 direction;
float max_time;
};
void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray)
{
/* Constant bias (due to depth buffer precision). Helps with self intersection. */
/* Magic numbers for 24bits of precision.
* From http://terathon.com/gdc07_lengyel.pdf (slide 26) */
const float bias = -2.4e-7 * 2.0;
ray.origin.zw += bias;
ray.direction.zw += bias;
ray.direction -= ray.origin;
/* 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 */
if (len_squared(ray.direction.xy) < 0.00001) {
ray.direction.xy = vec2(0.0, 0.00001);
}
float ray_len_sqr = len_squared(ray.direction.xyz);
/* Make ray.direction cover one pixel. */
bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y);
ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x);
ray.direction *= (is_more_vertical) ? ssrPixelSize.y : ssrPixelSize.x;
/* Clip to segment's end. */
ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz)));
/* Clipping to frustum sides. */
float clip_dist = line_unit_box_intersect_dist_safe(ray.origin.xyz, ray.direction.xyz);
ray.max_time = min(ray.max_time, clip_dist);
/* Convert to texture coords [0..1] range. */
ray.origin = ray.origin * 0.5 + 0.5;
ray.direction *= 0.5;
}
ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray)
{
ScreenSpaceRay ssray;
ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
raytrace_screenspace_ray_finalize(ssray);
return ssray;
}
ScreenSpaceRay raytrace_screenspace_ray_create(Ray ray, float thickness)
{
ScreenSpaceRay ssray;
ssray.origin.xyz = project_point(ProjectionMatrix, ray.origin);
ssray.direction.xyz = project_point(ProjectionMatrix, ray.origin + ray.direction);
/* Interpolate thickness in screen space.
* Calculate thickness further away to avoid near plane clipping issues. */
ssray.origin.w = get_depth_from_view_z(ray.origin.z - thickness) * 2.0 - 1.0;
ssray.direction.w = get_depth_from_view_z(ray.origin.z + ray.direction.z - thickness) * 2.0 -
1.0;
raytrace_screenspace_ray_finalize(ssray);
return ssray;
}
struct RayTraceParameters {
/** ViewSpace thickness the objects. */
float thickness;
/** Jitter along the ray to avoid banding artifact when steps are too large. */
float jitter;
/** Determine how fast the sample steps are getting bigger. */
float trace_quality;
/** Determine how we can use lower depth mipmaps to make the tracing faster. */
float roughness;
};
/* Returns true on hit. */
/* TODO(fclem): remove the back-face check and do it the SSR resolve code. */
bool raytrace(Ray ray,
RayTraceParameters params,
const bool discard_backface,
const bool allow_self_intersection,
out vec3 hit_position)
{
/* Clip to near plane for perspective view where there is a singularity at the camera origin. */
if (ProjectionMatrix[3][3] == 0.0) {
raytrace_clip_ray_to_near_plane(ray);
}
ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray, params.thickness);
/* Avoid no iteration. */
if (!allow_self_intersection && ssray.max_time < 1.1) {
hit_position = ssray.origin.xyz + ssray.direction.xyz;
return false;
}
ssray.max_time = max(1.1, ssray.max_time);
float prev_delta = 0.0, prev_time = 0.0;
float depth_sample = get_depth_from_view_z(ray.origin.z);
float delta = depth_sample - ssray.origin.z;
float lod_fac = saturate(fast_sqrt(params.roughness) * 2.0 - 0.4);
/* Cross at least one pixel. */
float t = 1.001, time = 1.001;
bool hit = false;
const float max_steps = 255.0;
for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) {
float stride = 1.0 + iter * params.trace_quality;
float lod = log2(stride) * lod_fac;
prev_time = time;
prev_delta = delta;
time = min(t + stride * params.jitter, ssray.max_time);
t += stride;
vec4 ss_p = ssray.origin + ssray.direction * time;
depth_sample = textureLod(maxzBuffer, ss_p.xy * hizUvScale.xy, floor(lod)).r;
delta = depth_sample - ss_p.z;
/* Check if the ray is below the surface ... */
hit = (delta < 0.0);
/* ... and above it with the added thickness. */
hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride * 2.0));
}
/* Discard back-face hits. */
hit = hit && !(discard_backface && prev_delta < 0.0);
/* Reject hit if background. */
hit = hit && (depth_sample != 1.0);
/* Refine hit using intersection between the sampled heightfield and the ray.
* This simplifies nicely to this single line. */
time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta)));
hit_position = ssray.origin.xyz + ssray.direction.xyz * time;
return hit;
}
bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out vec3 hit_position)
{
/* Clip to near plane for perspective view where there is a singularity at the camera origin. */
if (ProjectionMatrix[3][3] == 0.0) {
raytrace_clip_ray_to_near_plane(ray);
}
ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray);
/* Planar Reflections have X mirrored. */
ssray.origin.x = 1.0 - ssray.origin.x;
ssray.direction.x = -ssray.direction.x;
float prev_delta = 0.0, prev_time = 0.0;
float depth_sample = texture(planarDepth, vec3(ssray.origin.xy, planar_ref_id)).r;
float delta = depth_sample - ssray.origin.z;
float t = 0.0, time = 0.0;
/* On very sharp reflections, the ray can be perfectly aligned with the view direction
* making the tracing useless. Bypass tracing in this case. */
bool hit = false;
const float max_steps = 255.0;
for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) {
float stride = 1.0 + iter * params.trace_quality;
prev_time = time;
prev_delta = delta;
time = min(t + stride * params.jitter, ssray.max_time);
t += stride;
vec4 ss_ray = ssray.origin + ssray.direction * time;
depth_sample = texture(planarDepth, vec3(ss_ray.xy, planar_ref_id)).r;
delta = depth_sample - ss_ray.z;
/* Check if the ray is below the surface. */
hit = (delta < 0.0);
}
/* Reject hit if background. */
hit = hit && (depth_sample != 1.0);
/* Refine hit using intersection between the sampled heightfield and the ray.
* This simplifies nicely to this single line. */
time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta)));
hit_position = ssray.origin.xyz + ssray.direction.xyz * time;
/* Planar Reflections have X mirrored. */
hit_position.x = 1.0 - hit_position.x;
return hit;
}
float screen_border_mask(vec2 hit_co)
{
const float margin = 0.003;
float atten = ssrBorderFac + margin; /* Screen percentage */
hit_co = smoothstep(0.0, atten, hit_co) * (1.0 - smoothstep(1.0 - atten, 1.0, hit_co));
float screenfade = hit_co.x * hit_co.y;
return screenfade;
}