229 lines
7.8 KiB
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;
|
|
}
|