Fix "light acne" inside sphere
authorLukas Jiriste <ljiriste@student.42prague.com>
Fri, 3 Jan 2025 23:08:21 +0000 (00:08 +0100)
committerLukas Jiriste <ljiriste@student.42prague.com>
Fri, 3 Jan 2025 23:08:21 +0000 (00:08 +0100)
Consider how the color tracing part works. Until now the ray toward
light was ignoring the object from which it is originating. Then the
closest intersect was found for every other object. Then the closest one
was picked and compared to the distance of the object to light source.

Not ignoring the current object causes acne, because the ray origin may
be on either side of the surface. But it would be quite exhausting to
built the detection to every singe detection function. So instead the
functions return a vector of all intersections of an object with a ray
and the later functions can choose which one is the best.

So now the obstruction finding function may chose to ignore the closest
intersection if it is suspiciously close and of the same object.

src/scene.c

index b8d3ed4424fa2134cbaf9d8bc78d32416335194a..c73ec34a2ef5164b33fc1ded92d9ac75b3e62856 100644 (file)
@@ -92,18 +92,26 @@ int intersects_circumsphere(const t_ray *ray, const t_object *object)
 double get_intersection_arg_plane(const t_ray *ray, const t_plane *plane)
 {
        t_vec3  start;
+       t_vec   res;
 
+       ft_vec_init(&res, sizeof(double));
        start = vec_diff(plane->point, ray->start);
        return (vec_scalar_mul(plane->normal, start)
                        / vec_scalar_mul(plane->normal, ray->direction));
 }
 
-double get_intersection_arg_nonbounded(const t_ray *ray, const t_object *object)
+t_vec  get_intersection_arg_nonbounded(const t_ray *ray, const t_object *object)
 {
+       t_vec   res;
+       double  tmp;
+
+       ft_vec_init(&res, sizeof(double));
        if (object->type == PLANE)
-               return (get_intersection_arg_plane(ray, &object->object.plane));
-       else
-               return (NAN);
+       {
+               tmp = get_intersection_arg_plane(ray, &object->object.plane);
+               ft_vec_append(&res, &tmp);
+       }
+       return (res);
 }
 
 t_vec3 ray_point(const t_ray *ray, double arg)
@@ -111,24 +119,26 @@ t_vec3    ray_point(const t_ray *ray, double arg)
        return (vec_add(ray->start, vec_real_mul(ray->direction, arg)));
 }
 
-double get_intersection_arg_cylinder_base
+t_vec  get_intersection_arg_cylinder_base
                        (const t_ray *ray, const t_cylinder *cylinder)
 {
-       double  res;
+       t_vec   res;
+       double  arg;
        t_plane base;
 
+       ft_vec_init(&res, sizeof(double));
        base.point = vec_add(cylinder->center, vec_real_mul(cylinder->rot_axis, cylinder->height / 2));
        base.normal = cylinder->rot_axis;
-       res = get_intersection_arg_plane(ray, &base);
-       if (vec_norm(vec_diff(base.point, ray_point(ray, res))) <= cylinder->radius
+       arg = get_intersection_arg_plane(ray, &base);
+       if (vec_norm(vec_diff(base.point, ray_point(ray, arg))) <= cylinder->radius
                        && vec_scalar_mul(ray->direction, vec_diff(cylinder->center, base.point)) > 0)
-               return (res);
+               ft_vec_append(&res, &arg);
        base.point = vec_diff(cylinder->center, vec_real_mul(cylinder->rot_axis, cylinder->height / 2));
-       res = get_intersection_arg_plane(ray, &base);
-       if (vec_norm(vec_diff(base.point, ray_point(ray, res))) <= cylinder->radius
+       arg = get_intersection_arg_plane(ray, &base);
+       if (vec_norm(vec_diff(base.point, ray_point(ray, arg))) <= cylinder->radius
                        && vec_scalar_mul(ray->direction, vec_diff(cylinder->center, base.point)) > 0)
-               return (res);
-       return (NAN);
+               ft_vec_append(&res, &arg);
+       return (res);
 }
 
 t_pair solve_quadratic(double a, double b, double c)
@@ -149,14 +159,16 @@ t_pair    solve_quadratic(double a, double b, double c)
        return (res);
 }
 
-double get_intersection_arg_cylinder_around
+t_vec  get_intersection_arg_cylinder_around
                        (const t_ray *ray, const t_cylinder *cylinder)
 {
+       t_vec   res;
        t_pair  args;
        t_vec3  v;
        t_vec3  u;
        t_vec3  intersect;
 
+       ft_vec_init(&res, sizeof(double));
        v = vec_vec_mul(cylinder->rot_axis, ray->direction);
        u = vec_vec_mul(cylinder->rot_axis, vec_diff(ray->start, cylinder->center));
        args = solve_quadratic(vec_scalar_mul(v, v), 2 * vec_scalar_mul(v, u),
@@ -164,88 +176,146 @@ double   get_intersection_arg_cylinder_around
        intersect = ray_point(ray, args.first);
        if (fabs(vec_scalar_mul(cylinder->rot_axis, vec_diff(intersect, cylinder->center))) < cylinder->height / 2
                && vec_scalar_mul(vec_vec_mul(cylinder->rot_axis, ray->direction), vec_vec_mul(cylinder->rot_axis, vec_diff(cylinder->center, intersect))) > 0)
-               return (args.first);
+               ft_vec_append(&res, &args.first);
        intersect = ray_point(ray, args.second);
        if (fabs(vec_scalar_mul(cylinder->rot_axis, vec_diff(intersect, cylinder->center))) < cylinder->height / 2
                && vec_scalar_mul(vec_vec_mul(cylinder->rot_axis, ray->direction), vec_vec_mul(cylinder->rot_axis, vec_diff(cylinder->center, intersect))) > 0)
-               return (args.second);
-       return (NAN);
+               ft_vec_append(&res, &args.second);
+       return (res);
 }
 
-double get_intersection_arg_cylinder
+t_vec  get_intersection_arg_cylinder
                        (const t_ray *ray, const t_cylinder *cylinder)
 {
-       double  res;
+       size_t                  i;
+       t_vec                   res;
+       t_vec                   res2;
+       const double    *el;
 
        res = get_intersection_arg_cylinder_base(ray, cylinder);
-       if (isnan(res))
-               res = get_intersection_arg_cylinder_around(ray, cylinder);
+       res2 = get_intersection_arg_cylinder_around(ray, cylinder);
+       i = 0;
+       while (i <res2.size)
+       {
+               el = ft_vec_caccess(&res2, i);
+               ft_vec_append(&res, el);
+               ++i;
+       }
+       ft_vec_free(&res2, NULL);
        return (res);
 }
 
-double get_intersection_arg_sphere(const t_ray *ray, const t_sphere *sphere)
+t_vec  get_intersection_arg_sphere(const t_ray *ray, const t_sphere *sphere)
 {
        t_vec3  start;
        t_pair  intersection_args;
+       t_vec   res;
 
+       ft_vec_init(&res, sizeof(double));
        start = vec_diff(ray->start, sphere->center);
        intersection_args = solve_quadratic(
                vec_scalar_mul(ray->direction, ray->direction),
                2 * vec_scalar_mul(ray->direction, start),
                vec_scalar_mul(start, start) - sphere->radius * sphere->radius);
-       if (intersection_args.first > 0)
-               return (intersection_args.first);
-       else if (intersection_args.second > 0)
-               return (intersection_args.second);
-       else
-               return (NAN);
+       if (!isnan(intersection_args.first))
+               ft_vec_append(&res, &intersection_args.first);
+       if (!isnan(intersection_args.second))
+               ft_vec_append(&res, &intersection_args.second);
+       return (res);
 }
 
-double get_intersection_arg_bounded(const t_ray *ray, const t_object *object)
+t_vec  get_intersection_arg_bounded(const t_ray *ray, const t_object *object)
 {
+       t_vec   res;
+
        if (object->type == SPHERE)
-               return (get_intersection_arg_sphere(ray, &object->object.sphere));
+               res = get_intersection_arg_sphere(ray, &object->object.sphere);
        else if (object->type == CYLINDER)
-               return (get_intersection_arg_cylinder(ray, &object->object.cylinder));
+               res = get_intersection_arg_cylinder(ray, &object->object.cylinder);
        else
-               return (NAN);
+               ft_vec_init(&res, sizeof(double));
+       return (res);
 }
 
-double get_intersection_arg(const t_ray *ray, const t_object *object)
+t_vec  get_intersection_args(const t_ray *ray, const t_object *object)
 {
+       t_vec   res;
+
        if (!is_bounded(object))
-               return (get_intersection_arg_nonbounded(ray, object));
-       else if (!intersects_circumsphere(ray, object))
-               return (NAN);
+               res = get_intersection_arg_nonbounded(ray, object);
+       else if (intersects_circumsphere(ray, object))
+               res = get_intersection_arg_bounded(ray, object);
        else
-               return (get_intersection_arg_bounded(ray, object));
+               ft_vec_init(&res, sizeof(double));
+       return (res);
+}
+
+double get_intersection_arg_min(const t_ray *ray, const t_object *object)
+{
+       t_vec   args;
+       double  arg;
+       double  res;
+       size_t  i;
+
+       args = get_intersection_args(ray, object);
+       res = INFINITY;
+       i = 0;
+       while (i < args.size)
+       {
+               arg = *(const double *)ft_vec_caccess(&args, i++);
+               if (arg > 0 && arg < res)
+                       res = arg;
+       }
+       ft_vec_free(&args, NULL);
+       return (res);
 }
 
-const t_object *find_nearest_object(const t_ray *ray, const t_vec *objects,
-                                       const t_object *ignored, double ignored_dist)
+double get_self_obstruction_limit(const t_object *object)
+{
+       return (get_circumsphere(object).radius * SELF_OBSTRUCTION_MULTIPLIER);
+}
+
+typedef struct s_obstruction
+{
+       const t_object  *object;
+       double                  distance;
+}                                      t_obstruction;
+
+t_obstruction  find_nearest_obstruction(const t_ray *ray, const t_vec *objects,
+                                       const t_object *ignored)
 {
        size_t                  i;
-       const t_object  *object_found;
+       size_t                  j;
+       t_obstruction   obstruction_found;
        const t_object  *object;
-       double                  distance_found;
        double                  distance;
+       double                  ignored_dist;
+       t_vec                   distances;
 
-       object_found = NULL;
-       distance_found = INFINITY;
+       if (ignored)
+               ignored_dist = get_self_obstruction_limit(ignored);
+       obstruction_found.object = NULL;
+       obstruction_found.distance = INFINITY;
        i = 0;
        while (i < objects->size)
        {
                object = ft_vec_caccess(objects, i++);
-               distance = get_intersection_arg(ray, object);
-               if (object == ignored && distance > 0 && distance < ignored_dist)
-                       continue ;
-               if (0 < distance && distance < distance_found)
+               distances = get_intersection_args(ray, object);
+               j = 0;
+               while (j < distances.size)
                {
-                       distance_found = distance;
-                       object_found = object;
+                       distance = *(const double *)ft_vec_caccess(&distances, j++);
+                       if (object == ignored && distance > 0 && distance < ignored_dist)
+                               continue ;
+                       if (0 < distance && distance < obstruction_found.distance)
+                       {
+                               obstruction_found.distance = distance;
+                               obstruction_found.object = object;
+                       }
                }
+               ft_vec_free(&distances, NULL);
        }
-       return (object_found);
+       return (obstruction_found);
 }
 
 t_color        get_ambient_color(const t_object *object, const t_ambient_light *amb)
@@ -292,15 +362,10 @@ t_vec3    get_object_normal(const t_object *object, t_vec3 point)
        return ((t_vec3){.x = 0, .y = 0, .z = 0});
 }
 
-double get_self_obstruction_limit(const t_object *object)
-{
-       return (get_circumsphere(object).radius * SELF_OBSTRUCTION_MULTIPLIER);
-}
-
 t_color        get_light_contribution(t_ray normal, const t_object *object, const t_light *light, const t_scene *scene)
 {
        t_ray                   new_ray;
-       const t_object  *obstruction;
+       t_obstruction   obstruction;
        double                  angle_multiplier;
        double                  distance;
 
@@ -308,14 +373,14 @@ t_color   get_light_contribution(t_ray normal, const t_object *object, const t_lig
        new_ray.direction = vec_diff(light->position, new_ray.start);
        distance = vec_norm(new_ray.direction);
        new_ray.direction = vec_real_mul(new_ray.direction, 1 / distance);
-       obstruction = find_nearest_object(&new_ray, &scene->objects, object, get_self_obstruction_limit(object));
+       obstruction = find_nearest_obstruction(&new_ray, &scene->objects, object);
        angle_multiplier = vec_scalar_mul(normal.direction, new_ray.direction)
                / (vec_norm(normal.direction) * vec_norm(new_ray.direction));
        if (object->type == PLANE)
                angle_multiplier = fabs(angle_multiplier);
-       if (angle_multiplier > 0 && (!obstruction
-                       || distance < get_intersection_arg(&new_ray, obstruction)
-                       || get_intersection_arg(&new_ray, obstruction) < 0))
+       if (angle_multiplier > 0 && (!obstruction.object
+                       || distance < obstruction.distance
+                       ||obstruction.distance < 0))
                return (vec_real_mul(
                                        vec_elwise_mul(light->color, object->object.plane.color),
                                        light->brightness * angle_multiplier / distance / distance));
@@ -332,7 +397,7 @@ t_color     get_object_color(const t_ray *ray, const t_object *object,
        size_t                  i;
 
        result = (t_color){.x = 0, .y = 0, .z = 0};
-       normal_at_intersect.start = ray_point(ray, get_intersection_arg(ray, object));
+       normal_at_intersect.start = ray_point(ray, get_intersection_arg_min(ray, object));
        normal_at_intersect.direction = get_object_normal(object, normal_at_intersect.start);
        if (vec_scalar_mul(normal_at_intersect.direction, ray->direction) > 0)
                normal_at_intersect.direction = vec_real_mul(normal_at_intersect.direction, -1);
@@ -354,7 +419,7 @@ t_color     trace_ray(const t_ray *ray, const t_scene *scene)
 {
        const t_object  *object_found;
 
-       object_found = find_nearest_object(ray, &scene->objects, NULL, 0);
+       object_found = find_nearest_obstruction(ray, &scene->objects, NULL).object;
        if (object_found)
                return (get_object_color(ray, object_found, scene));
        else