Cycles: Deduplicate sphere intersection test #112031

Open
Alaska wants to merge 10 commits from Alaska/blender:cleanup-sphere-intersect into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 38 additions and 61 deletions

View File

@ -10,53 +10,6 @@ CCL_NAMESPACE_BEGIN
#ifdef __POINTCLOUD__
ccl_device_forceinline bool point_intersect_test(const float4 point,
const float3 ray_P,
const float3 ray_D,
const float ray_tmin,
const float ray_tmax,
ccl_private float *t)
{
const float3 center = float4_to_float3(point);
const float radius = point.w;
const float rd2 = 1.0f / dot(ray_D, ray_D);
const float3 c0 = center - ray_P;
const float projC0 = dot(c0, ray_D) * rd2;
const float3 perp = c0 - projC0 * ray_D;
const float l2 = dot(perp, perp);
const float r2 = radius * radius;
if (!(l2 <= r2)) {
return false;
}
const float td = sqrt((r2 - l2) * rd2);
const float t_front = projC0 - td;
const bool valid_front = (ray_tmin <= t_front) & (t_front <= ray_tmax);
/* Always back-face culling for now. */
# if 0
const float t_back = projC0 + td;
const bool valid_back = (ray_tmin <= t_back) & (t_back <= ray_tmax);
/* check if there is a first hit */
const bool valid_first = valid_front | valid_back;
if (!valid_first) {
return false;
}
*t = (valid_front) ? t_front : t_back;
return true;
# else
if (!valid_front) {
return false;
}
*t = t_front;
return true;
# endif
}
ccl_device_forceinline bool point_intersect(KernelGlobals kg,
ccl_private Intersection *isect,
const float3 ray_P,
@ -71,7 +24,17 @@ ccl_device_forceinline bool point_intersect(KernelGlobals kg,
const float4 point = (type & PRIMITIVE_MOTION) ? motion_point(kg, object, prim, time) :
kernel_data_fetch(points, prim);
if (!point_intersect_test(point, ray_P, ray_D, ray_tmin, ray_tmax, &isect->t)) {
float3 discard;
if (!ray_sphere_intersect(ray_P,
ray_D,
ray_tmin,
ray_tmax,
float4_to_float3(point),
point.w,
&discard,
&isect->t,
true))
{
return false;
}

View File

@ -124,7 +124,8 @@ ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *kligh
}
float3 P;
return ray_sphere_intersect(ray->P, ray->D, ray->tmin, ray->tmax, klight->co, radius, &P, t);
return ray_sphere_intersect(
ray->P, ray->D, ray->tmin, ray->tmax, klight->co, radius, &P, t, false);
}
ccl_device_inline bool point_light_sample_from_intersection(

View File

@ -68,7 +68,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight,
ls->D = sample_uniform_cone(
-klight->spot.dir, one_minus_cos_half_spot_spread, rand, &cos_theta, &ls->pdf);
if (!ray_sphere_intersect(P, ls->D, 0.0f, FLT_MAX, center, radius, &ls->P, &ls->t)) {
if (!ray_sphere_intersect(P, ls->D, 0.0f, FLT_MAX, center, radius, &ls->P, &ls->t, false)) {
/* Sampled direction does not intersect with the light. */
return false;
}

View File

@ -16,27 +16,40 @@ ccl_device bool ray_sphere_intersect(float3 ray_P,
float3 sphere_P,
float sphere_radius,
ccl_private float3 *isect_P,
ccl_private float *isect_t)
ccl_private float *isect_t,
const bool cull_backface)
{
const float3 d_vec = sphere_P - ray_P;
const float3 c0 = sphere_P - ray_P;
const float r_sq = sphere_radius * sphere_radius;
const float d_sq = dot(d_vec, d_vec);
const float d_cos_theta = dot(d_vec, ray_D);
if (d_sq > r_sq && d_cos_theta < 0.0f) {
/* Ray origin outside sphere and points away from sphere. */
/* to_surface_d denotes where the ray origin is located relative to the surface of the sphere.
* Negative values denote the ray origin is inside the sphere.
* Positive values denote the ray origin is outside the sphere. */
const float to_surface_d = len_squared(c0) - r_sq;
if (cull_backface && to_surface_d < 0.0f) {
/* Ray origin is inside the sphere meaning it can only intersect backfaces. */
return false;
}
const float d_sin_theta_sq = len_squared(d_vec - d_cos_theta * ray_D);
/* If the object has been scaled, ray_D will not be a normalized vector
* and inv_rd_sq will not be equal to 1.0 */
const float inv_rd_sq = 1.0f / dot(ray_D, ray_D);
const float projC0 = dot(c0, ray_D) * inv_rd_sq;
if (d_sin_theta_sq > r_sq) {
/* Closest point on ray outside sphere. */
if (projC0 < 0.0f && to_surface_d >= 0.0f) {
/* Ray origin is outside the sphere and points away from the sphere. */
return false;
}
/* Law of cosines. */
const float t = d_cos_theta - copysignf(sqrtf(r_sq - d_sin_theta_sq), d_sq - r_sq);
const float d_sq = len_squared(c0 - (projC0 * ray_D));
if (d_sq > r_sq) {
/* Closest point on ray is outside the sphere. */
return false;
}
const float t = projC0 - copysignf(sqrt((r_sq - d_sq) * inv_rd_sq), to_surface_d);
if (t > ray_tmin && t < ray_tmax) {
*isect_t = t;