Implement Lambertian diffuse lighting (kind of)
authorLukas Jiriste <ljiriste@student.42prague.com>
Tue, 26 Nov 2024 18:55:06 +0000 (19:55 +0100)
committerLukas Jiriste <ljiriste@student.42prague.com>
Tue, 26 Nov 2024 18:55:06 +0000 (19:55 +0100)
This implementation has its problems. The one I can think of now is that
planes are only reflecting, when their normal points in the direction of
the light.

src/scene.c

index 841af39a2a0d8b33fdf7af74464400a6a056921f..02105b55adfd6ef0208d4f2775f0f591289b5a48 100644 (file)
@@ -42,6 +42,12 @@ double       dist_point_from_line(const t_ray *ray, t_vec3 point)
        return (vec_norm(vec_diff(start, projection)));
 }
 
+double dist_point_from_plane(const t_plane *plane, t_vec3 point)
+{
+       return (fabs(vec_scalar_mul(plane->normal,
+                                       vec_diff(point, plane->point))));
+}
+
 int    is_behind_ray(const t_ray *ray, t_vec3 point)
 {
        return (0 > vec_scalar_mul(ray->direction, vec_diff(point, ray->start)));
@@ -231,28 +237,80 @@ t_color   get_ambient_color(const t_object *object, const t_ambient_light *amb)
                                amb->brightness));
 }
 
+t_vec3 get_cylinder_normal(t_vec3 point, const t_cylinder *cylinder)
+{
+       t_vec3  radial_vec;
+       double  dist_from_round;
+       double  dist_from_top;
+       double  dist_from_bottom;
+       t_plane base;
+
+       radial_vec = vec_vec_mul(cylinder->rot_axis,
+                       vec_diff(point, cylinder->center));
+       dist_from_round = fabs(vec_norm(radial_vec) - cylinder->radius);
+       base.normal = cylinder->rot_axis;
+       base.point = vec_add(cylinder->center,
+                       vec_real_mul(cylinder->rot_axis, cylinder->height / 2));
+       dist_from_top = dist_point_from_plane(&base, point);
+       base.point = vec_diff(cylinder->center,
+                       vec_real_mul(cylinder->rot_axis, cylinder->height / 2));
+       dist_from_bottom = dist_point_from_plane(&base, point);
+       if (dist_from_round < dist_from_top && dist_from_round < dist_from_bottom)
+               return (radial_vec);
+       if (dist_from_top < dist_from_bottom)
+               return (cylinder->rot_axis);
+       return (vec_real_mul(cylinder->rot_axis, -1));
+}
+
+t_vec3 get_object_normal(const t_object *object, t_vec3 point)
+{
+       if (object->type == PLANE)
+               return (object->object.plane.normal);
+       if (object->type == SPHERE)
+               return (vec_diff(point, object->object.sphere.center));
+       if (object->type == CYLINDER)
+               return (get_cylinder_normal(point, &object->object.cylinder));
+       return ((t_vec3){.x = 0, .y = 0, .z = 0});
+}
+
+t_color        get_light_contribution(t_vec3 point, const t_object *object, const t_light *light, const t_scene *scene)
+{
+       t_ray                   new_ray;
+       const t_object  *obstruction;
+       t_vec3                  normal;
+       double                  angle_multiplier;
+
+       new_ray.start = point;
+       new_ray.direction = vec_diff(light->position, new_ray.start);
+       obstruction = find_nearest_object(&new_ray, &scene->objects);
+       normal = get_object_normal(object, point);
+       angle_multiplier = vec_scalar_mul(normal, new_ray.direction)
+               / (vec_norm(normal) * vec_norm(new_ray.direction));
+       if (angle_multiplier > 0 && (!obstruction
+                       || 1 < get_intersection_arg(&new_ray, obstruction)
+                       || get_intersection_arg(&new_ray, obstruction) < 0))
+               return (vec_real_mul(
+                                       vec_elwise_mul(light->color, object->object.plane.color),
+                                       light->brightness * angle_multiplier));
+       else
+               return ((t_color){.x = 0, .y = 0, .z = 0});
+}
+
 t_color        get_object_color(const t_ray *ray, const t_object *object,
                        const t_scene *scene)
 {
        t_color                 result;
        const t_light   *light;
        size_t                  i;
-       t_ray                   new_ray;
-       const t_object  *obstruction;
 
        result = (t_color){.x = 0, .y = 0, .z = 0};
        i = 0;
        while (i < scene->lights.size)
        {
                light = ft_vec_caccess(&scene->lights, i);
-               new_ray.start = vec_add(ray->start, vec_real_mul(ray->direction,
-                                       get_intersection_arg(ray, object)));
-               new_ray.direction = vec_diff(light->position, new_ray.start);
-               obstruction = find_nearest_object(&new_ray, &scene->objects);
-               if (!obstruction || 1 < get_intersection_arg(&new_ray, obstruction)
-                               || get_intersection_arg(&new_ray, obstruction) < 0)
-                       result = vec_add(result, vec_real_mul(vec_elwise_mul(light->color,
-                                               object->object.plane.color), light->brightness));
+               result = vec_add(result, get_light_contribution(
+                                       ray_point(ray, get_intersection_arg(ray, object)),
+                                       object, light, scene));
                ++i;
        }
        result = vec_add(result, get_ambient_color(object, &scene->ambient_light));