Cycles: remove ray offsetting

Remove small ray offsets that were used to avoid self intersection, and leave
that to the newly added primitive object/prim comparison. These changes together
significantly reduce artifacts on small, large or far away objects.

The balance here is that overlapping primitives are not handled well and should
be avoided (though this was already an issue). The upside is that this is
something a user has control over, whereas the other artifacts had no good
manual solution in many cases.

There is a known issue where the Blender particle system generates overlapping
objects and in turn leads to render differences between CPU and GPU. This will
be addressed separately.

Differential Revision: https://developer.blender.org/D12954
This commit is contained in:
William Leeson 2022-01-13 17:20:50 +01:00 committed by Brecht Van Lommel
parent ae44070341
commit 74afc86d4b
Notes: blender-bot 2023-07-25 06:33:05 +02:00
Referenced by issue #99254, Blender artifacts when rendering overlapping objects with CUDA
Referenced by issue #98764, Regression: Hair Rendering Tranparency Issue with Cycles
Referenced by issue #97736, Transparent objects are visible inside volumetric materials (Cycles)
Referenced by issue #97027, Regression: Triangle edges visible (ray/triangle intersection issue?)
Referenced by issue #96537, Regression: Alpha/transparency not working correctly with intersecting mesh in Cycles
Referenced by issue #96475, Overlapping geometry shows black faces on Cycles Blender 3.1 (Ray Tracing Precision)
Referenced by issue #96381, Overlapping Volumes broken in Cycles, GPU only (possible regression)
Referenced by issue #95969, Bake to vertex colors completely broken in 3.1 & 3.2
Referenced by issue #43835, ray offsetting giving precission problems / render artifacts / cracks on far away geometry
Referenced by issue #110323, Regression: Mesh render artifacts
11 changed files with 32 additions and 71 deletions

View File

@ -21,54 +21,22 @@ CCL_NAMESPACE_BEGIN
/* Ray offset to avoid self intersection.
*
* This function should be used to compute a modified ray start position for
* rays leaving from a surface. */
* rays leaving from a surface. This is from "A Fast and Robust Method for Avoiding
* Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and
*/
ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
/* ideally this should match epsilon_f, but instancing and motion blur
* precision makes it problematic */
const float epsilon_test = 1.0f;
const int epsilon_i = 32;
const float int_scale = 256.0f;
int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z));
float3 res;
/* x component */
if (fabsf(P.x) < epsilon_test) {
res.x = P.x + Ng.x * epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if (fabsf(P.y) < epsilon_test) {
res.y = P.y + Ng.y * epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if (fabsf(P.z) < epsilon_test) {
res.z = P.z + Ng.z * epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f * Ng;
#endif
float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)),
__int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)),
__int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z)));
const float origin = 1.0f / 32.0f;
const float float_scale = 1.0f / 65536.0f;
return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x,
fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y,
fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z);
}
#if defined(__KERNEL_CPU__)

View File

@ -71,7 +71,7 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg,
volume_stack_enter_exit(kg, state, stack_sd);
/* Move ray forward. */
volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
volume_ray.P = stack_sd->P;
if (volume_ray.t != FLT_MAX) {
volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t);
}
@ -210,7 +210,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s
}
/* Move ray forward. */
volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng);
volume_ray.P = stack_sd->P;
++step;
}
#endif

View File

@ -37,8 +37,9 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
/* Advance ray beyond light. */
/* TODO: can we make this more numerically robust to avoid reintersecting the
* same light in some cases? */
const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D);
* same light in some cases? Ray should not intersect surface anymore as the
* object and prim ids will prevent self intersection. */
const float3 new_ray_P = ray_P + ray_D * isect.t;
INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P;
INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t;
@ -46,7 +47,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t);
ray_P -= ray_D * mis_ray_t;
isect.t += mis_ray_t;
INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t;
INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t;
LightSample ls ccl_optional_struct_init;
const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls);

View File

@ -152,7 +152,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg,
const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t);
const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P);
const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D);
INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D);
INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D;
INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t;
}

View File

@ -271,13 +271,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
}
/* Setup ray. Note that clipping works through transparent bounces. */
INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P,
(label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng);
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in);
INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ?
INTEGRATOR_STATE(state, ray, t) - sd->ray_length :
FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in);
@ -321,7 +319,7 @@ ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState
}
/* Setup ray position, direction stays unchanged. */
INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng);
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
/* Clipping works through transparent. */
INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length;
@ -365,7 +363,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
}
Ray ray ccl_optional_struct_init;
ray.P = ray_offset(sd->P, sd->Ng);
ray.P = sd->P;
ray.D = ao_D;
ray.t = kernel_data.integrator.ao_bounces_distance;
ray.time = sd->time;

View File

@ -877,7 +877,6 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in);
INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX;
# ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in);

View File

@ -164,10 +164,8 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat
if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) {
float3 P = INTEGRATOR_STATE(state, ray, P);
const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng);
const float3 offset_P = ray_offset(P, -Ng);
integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P);
integrator_volume_stack_update_for_subsurface(kg, state, P, ray.P);
}
}
# endif /* __VOLUME__ */

View File

@ -206,7 +206,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
}
/* Setup ray. */
ray.P = ray_offset(P, -Ng);
ray.P = P;
ray.D = D;
ray.t = FLT_MAX;
ray.time = time;
@ -421,13 +421,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
if (hit) {
t = ray.t;
}
else if (bounce == 0) {
/* Restore original position if nothing was hit after the first bounce,
* without the ray_offset() that was added to avoid self-intersection.
* Otherwise if that offset is relatively large compared to the scattering
* radius, we never go back up high enough to exit the surface. */
ray.P = P;
}
/* Advance to new scatter location. */
ray.P += t * ray.D;

View File

@ -198,7 +198,7 @@ ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg,
float NL = dot(sd->N, L);
bool transmit = (NL < 0.0f);
float3 Ng = (transmit ? -sd->Ng : sd->Ng);
float3 P = ray_offset(sd->P, Ng);
float3 P = sd->P;
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
const float offset_cutoff =
@ -243,7 +243,7 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri
}
else {
/* other lights, avoid self-intersection */
ray->D = ray_offset(ls->P, ls->Ng) - P;
ray->D = ls->P - P;
ray->D = normalize_len(ray->D, &ray->t);
}
}

View File

@ -70,7 +70,7 @@ ccl_device float svm_ao(
/* Create ray. */
Ray ray;
ray.P = ray_offset(sd->P, N);
ray.P = sd->P;
ray.D = D.x * T + D.y * B + D.z * N;
ray.t = max_dist;
ray.time = sd->time;

View File

@ -36,8 +36,12 @@ BLACKLIST_GPU = [
'hair_instancer_uv.blend',
'hair_length_info.blend',
'hair_particle_random.blend',
"hair_transmission.blend",
'principled_hair_.*.blend',
'transparent_shadow_hair.*.blend',
# Inconsistent handling of overlapping objects.
"T41143.blend",
"visibility_particles.blend",
]