diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2021-03-05 22:32:54 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2021-03-08 19:25:16 +0300 |
commit | 6842c549bb3f976fc4891d2e6f7469b3705655f5 (patch) | |
tree | b52c512d30878d1dfbc651ddc1b84d955c6860ac /source/blender/draw | |
parent | ba75ea8012084aa84ba8c9ac088b88a8dcf4fb21 (diff) |
EEVEE: SSRayTrace: Cleanup/Refactor
This is a major rewrite that improves the screen space raytracing
a little bit.
This also decouple ray preparation from raytracing to be reuse in other
part of the code.
This changes a few things:
- Reflections have lower grazing angle failure
- Reflections have less self intersection issues
- Contact shadows are now fully opaque (faster)
Unrelated but some self intersection / incorrect bad rays are caused by
the ray reconstruction technique used by the SSR. This is not fixed by
this commit but I added a TODO.
Diffstat (limited to 'source/blender/draw')
4 files changed, 237 insertions, 263 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index f2117d8ee67..66183e1bc02 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -63,14 +63,19 @@ void do_planar_ssr( pdfData = min(1024e32, pdf); /* Theoretical limit of 16bit float */ - /* Since viewspace hit position can land behind the camera in this case, - * we save the reflected view position (visualize it as the hit position - * below the reflection plane). This way it's garanted that the hit will - * be in front of the camera. That let us tag the bad rays with a negative - * sign in the Z component. */ - vec3 hit_pos = raycast(index, vP, R * 1e16, 1e16, rand.y, ssrQuality, a2, false); - - hitData = encode_hit_data(hit_pos.xy, (hit_pos.z > 0.0), true); + Ray ray; + ray.origin = vP; + ray.direction = R * 1e16; + + RayTraceParameters params; + params.jitter = rand.y; + params.trace_quality = ssrQuality; + params.roughness = a2; + + vec3 hit_pos; + bool hit = raytrace_planar(ray, params, index, hit_pos); + + hitData = encode_hit_data(hit_pos.xy, hit, true); } void do_ssr(vec3 V, vec3 N, vec3 T, vec3 B, vec3 vP, float a2, vec4 rand) @@ -104,27 +109,30 @@ void do_ssr(vec3 V, vec3 N, vec3 T, vec3 B, vec3 vP, float a2, vec4 rand) pdfData = min(1024e32, pdf_ggx_reflect(NH, a2)); /* Theoretical limit of 16bit float */ - /* TODO(fclem) This bias should use depth precision and the dot product between - * ray direction and geom normal. */ - float rays_bias = 0.005; - vP = vP + vNg * rays_bias; + vP = raytrace_offset(vP, vNg); + + Ray ray; + ray.origin = vP; + ray.direction = R * 1e16; - vec3 hit_pos = raycast(-1, vP, R * 1e16, ssrThickness, rand.y, ssrQuality, a2, true); + RayTraceParameters params; + params.thickness = ssrThickness; + params.jitter = rand.y; + params.trace_quality = ssrQuality; + params.roughness = a2; - hitData = encode_hit_data(hit_pos.xy, (hit_pos.z > 0.0), false); + vec3 hit_pos; + bool hit = raytrace(ray, params, true, hit_pos); + + hitData = encode_hit_data(hit_pos.xy, hit, false); } +in vec4 uvcoordsvar; + void main() { -# ifdef FULLRES - ivec2 fullres_texel = ivec2(gl_FragCoord.xy); - ivec2 halfres_texel = fullres_texel; -# else - ivec2 fullres_texel = ivec2(gl_FragCoord.xy) * 2 + halfresOffset; - ivec2 halfres_texel = ivec2(gl_FragCoord.xy); -# endif - - float depth = texelFetch(depthBuffer, fullres_texel, 0).r; + vec2 uvs = uvcoordsvar.xy; + float depth = textureLod(depthBuffer, uvs, 0.0).r; /* Default: not hits. */ hitData = encode_hit_data(vec2(0.5), false, false); @@ -136,18 +144,16 @@ void main() return; } - vec2 uvs = vec2(fullres_texel) / vec2(textureSize(depthBuffer, 0)); - /* Using view space */ vec3 vP = get_view_space_from_depth(uvs, depth); vec3 P = transform_point(ViewMatrixInverse, vP); vec3 vV = viewCameraVec(vP); vec3 V = cameraVec(P); - vec3 vN = normal_decode(texelFetch(normalBuffer, fullres_texel, 0).rg, vV); + vec3 vN = normal_decode(texture(normalBuffer, uvs, 0).rg, vV); vec3 N = transform_direction(ViewMatrixInverse, vN); /* Retrieve pixel data */ - vec4 speccol_roughness = texelFetch(specroughBuffer, fullres_texel, 0).rgba; + vec4 speccol_roughness = texture(specroughBuffer, uvs, 0).rgba; /* Early out */ if (dot(speccol_roughness.rgb, vec3(1.0)) == 0.0) { @@ -163,7 +169,7 @@ void main() return; } - vec4 rand = texelfetch_noise_tex(halfres_texel); + vec4 rand = texelfetch_noise_tex(vec2(gl_FragCoord.xy)); /* Gives *perfect* reflection for very small roughness */ if (roughness < 0.04) { @@ -194,11 +200,6 @@ void main() } } - /* 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) */ - vP.z = get_view_z_from_depth(depth - mix(2.4e-7, 4.8e-7, depth)); - do_ssr(vV, vN, vT, vB, vP, a2, rand); } @@ -333,7 +334,7 @@ vec3 get_scene_color(vec2 ref_uvs, float mip, float planar_index, bool is_planar return textureLod(probePlanars, vec3(ref_uvs, planar_index), mip).rgb; } else { - return textureLod(prevColorBuffer, ref_uvs * hizUvScaleBias.xy + hizUvScaleBias.zw, mip).rgb; + return textureLod(prevColorBuffer, ref_uvs * hizUvScale.xy, mip).rgb; } } @@ -356,6 +357,8 @@ vec4 get_ssr_samples(vec4 hit_pdf, hit_co[1].xy = decode_hit_data(hit_data[1].xy, has_hit.z, is_planar.z); hit_co[1].zw = decode_hit_data(hit_data[1].zw, has_hit.w, is_planar.w); + /* TODO/FIXME(fclem) This is giving precision issues due to refined intersection. This is most + * noticeable on rough surfaces. */ vec4 hit_depth; hit_depth.x = get_sample_depth(hit_co[0].xy, is_planar.x, planar_index); hit_depth.y = get_sample_depth(hit_co[0].zw, is_planar.y, planar_index); diff --git a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl index 04ad53eabb7..bd752d33819 100644 --- a/source/blender/draw/engines/eevee/shaders/lights_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lights_lib.glsl @@ -276,31 +276,30 @@ float light_contact_shadows( /* Only compute if not already in shadow. */ if (sd.sh_contact_dist > 0.0) { /* Contact Shadows. */ - vec3 ray_ori, ray_dir; - float trace_distance; + Ray ray; if (ld.l_type == SUN) { - trace_distance = sd.sh_contact_dist; - ray_dir = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * trace_distance; + ray.direction = shadows_cascade_data[int(sd.sh_data_index)].sh_shadow_vec * + sd.sh_contact_dist; } else { - ray_dir = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P; - float len = length(ray_dir); - trace_distance = min(sd.sh_contact_dist, len); - ray_dir *= trace_distance / len; + ray.direction = shadows_cube_data[int(sd.sh_data_index)].position.xyz - P; + ray.direction *= saturate(sd.sh_contact_dist * safe_rcp(length(ray.direction))); } - ray_dir = transform_direction(ViewMatrix, ray_dir); - ray_ori = vec3(vP.xy, tracing_depth) + vNg * sd.sh_contact_offset; + ray.direction = transform_direction(ViewMatrix, ray.direction); + ray.origin = vP + vNg * sd.sh_contact_offset; - vec3 hit_pos = raycast( - -1, ray_ori, ray_dir, sd.sh_contact_thickness, rand_x, 0.1, 0.001, false); + RayTraceParameters params; + params.thickness = sd.sh_contact_thickness; + params.jitter = rand_x; + params.trace_quality = 0.1; + params.roughness = 0.001; - if (hit_pos.z > 0.0) { - hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z); - float hit_dist = distance(vP, hit_pos); - float dist_ratio = hit_dist / trace_distance; - return saturate(dist_ratio * 3.0 - 2.0); + vec3 hit_position_unused; + + if (raytrace(ray, params, false, hit_position_unused)) { + return 0.0; } } } diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl index fbc13d832bd..0adfea9ca73 100644 --- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl @@ -3,253 +3,215 @@ #pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_uniforms_lib.glsl) +/** + * Screen-Space Raytracing functions. + */ + uniform sampler2D maxzBuffer; uniform sampler2DArray planarDepth; -#define MAX_STEP 256 +struct Ray { + vec3 origin; + vec3 direction; +}; -float sample_depth(vec2 uv, int index, float lod) +vec3 raytrace_offset(vec3 P, vec3 Ng) { -#ifdef PLANAR_PROBE_RAYTRACE - if (index > -1) { - return textureLod(planarDepth, vec3(uv, index), 0.0).r; - } - else { -#endif - return textureLod(maxzBuffer, uv * hizUvScale.xy, floor(lod)).r; -#ifdef PLANAR_PROBE_RAYTRACE - } -#endif + /* TODO(fclem) better offset */ + const float epsilon_f = 1e-4; + return P + epsilon_f * Ng; } -vec4 sample_depth_grouped(vec4 uv1, vec4 uv2, int index, float lod) +/* Inputs expected to be in viewspace. */ +void raytrace_clip_ray_to_near_plane(inout Ray ray) { - vec4 depths; -#ifdef PLANAR_PROBE_RAYTRACE - if (index > -1) { - depths.x = textureLod(planarDepth, vec3(uv1.xy, index), 0.0).r; - depths.y = textureLod(planarDepth, vec3(uv1.zw, index), 0.0).r; - depths.z = textureLod(planarDepth, vec3(uv2.xy, index), 0.0).r; - depths.w = textureLod(planarDepth, vec3(uv2.zw, index), 0.0).r; + 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); } - else { -#endif - depths.x = textureLod(maxzBuffer, uv1.xy * hizUvScale.xy, lod).r; - depths.y = textureLod(maxzBuffer, uv1.zw * hizUvScale.xy, lod).r; - depths.z = textureLod(maxzBuffer, uv2.xy * hizUvScale.xy, lod).r; - depths.w = textureLod(maxzBuffer, uv2.zw * hizUvScale.xy, lod).r; -#ifdef PLANAR_PROBE_RAYTRACE - } -#endif - return depths; } -float refine_isect(float prev_delta, float curr_delta) -{ - /** - * Simplification of 2D intersection : - * r0 = (0.0, prev_ss_ray.z); - * r1 = (1.0, curr_ss_ray.z); - * d0 = (0.0, prev_hit_depth_sample); - * d1 = (1.0, curr_hit_depth_sample); - * vec2 r = r1 - r0; - * vec2 d = d1 - d0; - * vec2 isect = ((d * cross(r1, r0)) - (r * cross(d1, d0))) / cross(r,d); - * - * We only want isect.x to know how much stride we need. So it simplifies : - * - * isect_x = (cross(r1, r0) - cross(d1, d0)) / cross(r,d); - * isect_x = (prev_ss_ray.z - prev_hit_depth_sample.z) / cross(r,d); - */ - return saturate(prev_delta / (prev_delta - curr_delta)); -} +/* 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 prepare_raycast(vec3 ray_origin, - vec3 ray_dir, - float thickness, - int index, - out vec4 ss_step, - out vec4 ss_ray, - out float max_time) +void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray) { - /* Negate the ray direction if it goes towards the camera. - * This way we don't need to care if the projected point - * is behind the near plane. */ - float z_sign = -sign(ray_dir.z); - vec3 ray_end = ray_origin + z_sign * ray_dir; - - /* Project into screen space. */ - vec4 ss_start, ss_end; - ss_start.xyz = project_point(ProjectionMatrix, ray_origin); - ss_end.xyz = project_point(ProjectionMatrix, ray_end); - - /* We interpolate the ray Z + thickness values to check if depth is within threshold. */ - ray_origin.z -= thickness; - ray_end.z -= thickness; - ss_start.w = project_point(ProjectionMatrix, ray_origin).z; - ss_end.w = project_point(ProjectionMatrix, ray_end).z; - - /* XXX This is a hack. A better method is welcome! */ - /* We take the delta between the offsetted depth and the depth and subtract it from the ray - * depth. This will change the world space thickness appearance a bit but we can have negative - * values without worries. We cannot do this in viewspace because of the perspective division. */ - ss_start.w = 2.0 * ss_start.z - ss_start.w; - ss_end.w = 2.0 * ss_end.z - ss_end.w; - - ss_step = ss_end - ss_start; - max_time = length(ss_step.xyz); - ss_step = z_sign * ss_step / length(ss_step.xyz); - + /* 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; + float ray_len_sqr = len_squared(ray.direction.xyz); /* 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 (dot(ss_step.xy, ss_step.xy) < 0.00001) { - ss_step.xy = vec2(0.0, 0.0001); + if (ray_len_sqr < 0.00001) { + ray.direction.xy = vec2(0.0, 0.0001); } - - /* Make ss_step cover one pixel. */ - ss_step /= max(abs(ss_step.x), abs(ss_step.y)); - ss_step *= (abs(ss_step.x) > abs(ss_step.y)) ? ssrPixelSize.x : ssrPixelSize.y; - + /* 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. */ - max_time /= length(ss_step.xyz); + ray.max_time = sqrt(ray_len_sqr * safe_rcp(len_squared(ray.direction.xyz))); /* Clipping to frustum sides. */ - max_time = min(max_time, line_unit_box_intersect_dist(ss_start.xyz, ss_step.xyz)); + float clip_dist = line_unit_box_intersect_dist(ray.origin.xyz, ray.direction.xyz); + ray.max_time = min(ray.max_time, clip_dist); /* Avoid no iteration. */ - max_time = max(max_time, 1.0); + ray.max_time = max(ray.max_time, 1.1); + /* Convert to texture coords [0..1] range. */ + ray.origin = ray.origin * 0.5 + 0.5; + ray.direction *= 0.5; +} - /* Convert to texture coords. Z component included - * since this is how it's stored in the depth buffer. - * 4th component how far we are on the ray */ -#ifdef PLANAR_PROBE_RAYTRACE - /* Planar Reflections have X mirrored. */ - vec2 m = (index > -1) ? vec2(-0.5, 0.5) : vec2(0.5); -#else - const vec2 m = vec2(0.5); -#endif - ss_ray = ss_start * m.xyyy + 0.5; - ss_step *= m.xyyy; +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; } -/* See times_and_deltas. */ -#define curr_time times_and_deltas.x -#define prev_time times_and_deltas.y -#define curr_delta times_and_deltas.z -#define prev_delta times_and_deltas.w +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; +} -// #define GROUPED_FETCHES /* is still slower, need to see where is the bottleneck. */ -/* Return the hit position, and negate the z component (making it positive) if not hit occurred. */ +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; +}; + +/* Return true on hit. */ /* __ray_dir__ is the ray direction premultiplied by its maximum length */ -vec3 raycast(int index, - vec3 ray_origin, - vec3 ray_dir, - float thickness, - float ray_jitter, - float trace_quality, - float roughness, - const bool discard_backface) +/* TODO fclem remove the backface check and do it the SSR resolve code. */ +bool raytrace(Ray ray, + RayTraceParameters params, + const bool discard_backface, + out vec3 hit_position) { - vec4 ss_step, ss_start; - float max_time; - prepare_raycast(ray_origin, ray_dir, thickness, index, ss_step, ss_start, max_time); - - float max_trace_time = max(0.01, max_time - 0.01); + /* 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); + } -#ifdef GROUPED_FETCHES - ray_jitter *= 0.25; -#endif + ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray, params.thickness); - /* x : current_time, y: previous_time, z: current_delta, w: previous_delta */ - vec4 times_and_deltas = vec4(0.0); + 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 ray_time = 0.0; - float depth_sample = sample_depth(ss_start.xy, index, 0.0); - curr_delta = depth_sample - ss_start.z; + float lod_fac = saturate(fast_sqrt(params.roughness) * 2.0 - 0.4); - float lod_fac = saturate(fast_sqrt(roughness) * 2.0 - 0.4); + /* Cross at least one pixel. */ + float t = 1.001, time = 1.001; bool hit = false; - float iter; - for (iter = 1.0; !hit && (ray_time < max_time) && (iter < MAX_STEP); iter++) { - float stride = max(1.01, iter * trace_quality); - float lod = log2(stride * 0.5 * trace_quality) * lod_fac; - ray_time += stride; - - /* Save previous values. */ - times_and_deltas.xyzw = times_and_deltas.yxwz; - -#ifdef GROUPED_FETCHES - stride *= 4.0; - vec4 jit_stride = mix(vec4(2.0), vec4(stride), vec4(0.0, 0.25, 0.5, 0.75) + ray_jitter); - - vec4 times = min(vec4(ray_time) + jit_stride, vec4(max_trace_time)); - - vec4 uv1 = ss_start.xyxy + ss_step.xyxy * times.xxyy; - vec4 uv2 = ss_start.xyxy + ss_step.xyxy * times.zzww; - - vec4 depth_samples = sample_depth_grouped(uv1, uv2, index, lod); - - vec4 ray_z = ss_start.zzzz + ss_step.zzzz * times.xyzw; - vec4 ray_w = ss_start.wwww + ss_step.wwww * vec4(prev_time, times.xyz); - - vec4 deltas = depth_samples - ray_z; - /* Same as component wise (curr_delta <= 0.0) && (prev_w <= depth_sample). */ - bvec4 test = equal(step(deltas, vec4(0.0)) * step(ray_w, depth_samples), vec4(1.0)); - hit = any(test); - - if (hit) { - vec2 m = vec2(1.0, 0.0); /* Mask */ - - vec4 ret_times_and_deltas = times.wzzz * m.xxyy + deltas.wwwz * m.yyxx; - ret_times_and_deltas = (test.z) ? times.zyyy * m.xxyy + deltas.zzzy * m.yyxx : - ret_times_and_deltas; - ret_times_and_deltas = (test.y) ? times.yxxx * m.xxyy + deltas.yyyx * m.yyxx : - ret_times_and_deltas; - times_and_deltas = (test.x) ? times.xxxx * m.xyyy + deltas.xxxx * m.yyxy + - times_and_deltas.yyww * m.yxyx : - ret_times_and_deltas; - - depth_sample = depth_samples.w; - depth_sample = (test.z) ? depth_samples.z : depth_sample; - depth_sample = (test.y) ? depth_samples.y : depth_sample; - depth_sample = (test.x) ? depth_samples.x : depth_sample; - } - else { - curr_time = times.w; - curr_delta = deltas.w; - } -#else - float jit_stride = mix(2.0, stride, ray_jitter); - - curr_time = min(ray_time + jit_stride, max_trace_time); - vec4 ss_ray = ss_start + ss_step * curr_time; - - depth_sample = sample_depth(ss_ray.xy, index, lod); - - float prev_w = ss_start.w + ss_step.w * prev_time; - curr_delta = depth_sample - ss_ray.z; - hit = (curr_delta <= 0.0) && (prev_w <= depth_sample); -#endif - } + 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; - /* Discard backface hits. Only do this if the ray traveled enough to avoid losing intricate - * contact reflections. This is only used for SSReflections. */ - if (discard_backface && prev_delta < 0.0 && curr_time > 4.1) { - hit = false; - } + 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); + } + /* Discard backface 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; +} - curr_time = (hit) ? mix(prev_time, curr_time, refine_isect(prev_delta, curr_delta)) : curr_time; - ray_time = (hit) ? curr_time : ray_time; +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; - /* Clip to frustum. */ - ray_time = max(0.001, min(ray_time, max_time - 1.5)); + 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; + + /* 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; + + 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))); - vec4 ss_ray = ss_start + ss_step * ray_time; + hit_position = ssray.origin.xyz + ssray.direction.xyz * time; - /* Tag Z if ray failed. */ - ss_ray.z *= (hit) ? 1.0 : -1.0; - return ss_ray.xyz; + return hit; } float screen_border_mask(vec2 hit_co) diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl index 907a9cf17f8..5a09120916a 100644 --- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl @@ -39,10 +39,20 @@ vec4 screen_space_refraction(vec3 vP, vec3 N, vec3 V, float ior, float roughness R = transform_direction(ViewMatrix, R); - vec3 hit_pos = raycast( - -1, vP, R * 1e16, ssrThickness, rand.y, ssrQuality, roughnessSquared, false); + Ray ray; + ray.origin = vP; + ray.direction = R * 1e16; - if ((hit_pos.z > 0.0) && (F_eta(ior, dot(H, V)) < 1.0)) { + RayTraceParameters params; + params.thickness = ssrThickness; + params.jitter = rand.y; + params.trace_quality = ssrQuality; + params.roughness = roughnessSquared; + + vec3 hit_pos; + bool hit = raytrace(ray, params, false, hit_pos); + + if (hit && (F_eta(ior, dot(H, V)) < 1.0)) { hit_pos = get_view_space_from_depth(hit_pos.xy, hit_pos.z); float hit_dist = distance(hit_pos, vP); |