Try to implement sphere self obstruction inside
authorLukas Jiriste <ljiriste@student.42prague.com>
Fri, 3 Jan 2025 15:18:48 +0000 (16:18 +0100)
committerLukas Jiriste <ljiriste@student.42prague.com>
Fri, 3 Jan 2025 15:18:48 +0000 (16:18 +0100)
The sphere was completely lit inside by the lights outside.
I cannot stop ignoring at least parts of the objects else planes would
render with shadow acne.
So I tried to ignore the object if it obstructs itself very close from
the rendered place - this should prevent shadow acne.
The sphere however ignores the distant intersection when the close one
needs to be ignored. This manifests as "light acne" - some part are
correctly in shadow, while others cause the object to be ignored and
light passes through.

src/scene.c

index 475002dc09257f17b39cdad7e354215e337f90a7..b8d3ed4424fa2134cbaf9d8bc78d32416335194a 100644 (file)
@@ -11,7 +11,9 @@
 # error "This platform does not support NAN macro!"
 #endif // NAN
 
-static const t_color DEFAULT_COLOR = {0, 0, 0};
+static const t_color   DEFAULT_COLOR = {0, 0, 0};
+
+static const double            SELF_OBSTRUCTION_MULTIPLIER = 1e-8;
 
 static int     is_bounded(const t_object *object)
 {
@@ -53,26 +55,38 @@ int is_behind_ray(const t_ray *ray, t_vec3 point)
        return (0 > vec_scalar_mul(ray->direction, vec_diff(point, ray->start)));
 }
 
-int    intersects_circumsphere(const t_ray *ray, const t_object *object)
+t_sphere       get_circumsphere(const t_object *object)
 {
-       double  radius;
-       t_vec3  center;
-       double  distance;
+       t_sphere        circumsphere;
 
        if (object->type == SPHERE)
        {
-               radius = object->object.sphere.radius;
-               center = object->object.sphere.center;
+               circumsphere.radius = object->object.sphere.radius;
+               circumsphere.center = object->object.sphere.center;
        }
        else if (object->type == CYLINDER)
        {
-               radius = get_cylinder_circumsphere_radius(&object->object.cylinder);
-               center = object->object.cylinder.center;
+               circumsphere.radius = get_cylinder_circumsphere_radius(&object->object.cylinder);
+               circumsphere.center = object->object.cylinder.center;
+       }
+       else if(object->type == PLANE)
+       {
+               circumsphere.radius = INFINITY;
+               circumsphere.center = object->object.plane.point;
        }
-       if (is_behind_ray(ray, center))
+       return (circumsphere);
+}
+
+int    intersects_circumsphere(const t_ray *ray, const t_object *object)
+{
+       t_sphere        circumsphere;
+       double          distance;
+
+       circumsphere = get_circumsphere(object);
+       if (is_behind_ray(ray, circumsphere.center))
                return (0);
-       distance = dist_point_from_line(ray, center);
-       return (distance < radius);
+       distance = dist_point_from_line(ray, circumsphere.center);
+       return (distance < circumsphere.radius);
 }
 
 double get_intersection_arg_plane(const t_ray *ray, const t_plane *plane)
@@ -208,7 +222,7 @@ double      get_intersection_arg(const t_ray *ray, const t_object *object)
 }
 
 const t_object *find_nearest_object(const t_ray *ray, const t_vec *objects,
-                                       const t_object *ignored)
+                                       const t_object *ignored, double ignored_dist)
 {
        size_t                  i;
        const t_object  *object_found;
@@ -222,9 +236,9 @@ const t_object      *find_nearest_object(const t_ray *ray, const t_vec *objects,
        while (i < objects->size)
        {
                object = ft_vec_caccess(objects, i++);
-               if (object == ignored)
-                       continue ;
                distance = get_intersection_arg(ray, object);
+               if (object == ignored && distance > 0 && distance < ignored_dist)
+                       continue ;
                if (0 < distance && distance < distance_found)
                {
                        distance_found = distance;
@@ -278,6 +292,11 @@ 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;
@@ -289,7 +308,7 @@ 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);
+       obstruction = find_nearest_object(&new_ray, &scene->objects, object, get_self_obstruction_limit(object));
        angle_multiplier = vec_scalar_mul(normal.direction, new_ray.direction)
                / (vec_norm(normal.direction) * vec_norm(new_ray.direction));
        if (object->type == PLANE)
@@ -335,7 +354,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);
+       object_found = find_nearest_object(ray, &scene->objects, NULL, 0);
        if (object_found)
                return (get_object_color(ray, object_found, scene));
        else