From: Lukas Jiriste Date: Fri, 3 Jan 2025 23:08:21 +0000 (+0100) Subject: Fix "light acne" inside sphere X-Git-Url: https://git.ljiriste.work/?a=commitdiff_plain;h=313a764d3a43564d5dd84d412f84ef896469d9a2;p=42%2FminiRT.git Fix "light acne" inside sphere 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. --- diff --git a/src/scene.c b/src/scene.c index b8d3ed4..c73ec34 100644 --- a/src/scene.c +++ b/src/scene.c @@ -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 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