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:
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
|
@ -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__)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue