Example #1
0
/*
 * Adds the effect of the given light over the given intersection.
 *
 * light: Light that is being applied.
 * inter: Intersection over which the light is being applied.
 * normal_vec:  Normal vector of the intersection point. It is passed by as a
 *              parameter for optimization purposes.
 * rev_dir_vec: Reverse vector of the direction of the ray that comes from the
 *              eye. It is passed by as a parameter for optimization purposes.
 * all_lights_color: Accumulated amount of light sources effect. The effect of
 *              the given light is added to this total.
 * all_lights_color: Accumulated amount of the specular light effect. The
 *              specular effect of the given light is added to this total.
 * conf: Configuration of the scene.
 */
void apply_light_source(Light light,
                        Intersection inter,
                        Vector normal_vec,
                        Vector rev_dir_vec,
                        Color *all_lights_color,
                        long double *all_spec_light,
                        SceneConfig conf)
{
    Vector light_vec;
    long double light_distance, illum_cos, att_factor, spec_cos;
    Color light_filter;
    Intersection* shadow_inter;
    int shadow_i, shadow_inter_length;
    Object shadow_obj;

    // Find the vector that points from the intersection point to the light
    // source, and normalize it
    light_vec = subtract_vectors(light.anchor, inter.posn);
    light_distance = normalize_vector(&light_vec);
    // We check for any object making a shadow from that light
    light_filter = (Color){ .red = 1.0, .green = 1.0, .blue = 1.0 };;
    shadow_inter = get_intersections(inter.posn, light_vec, &shadow_inter_length, conf);
    // If the intersection is beyond the light source, we ignore it
    if(shadow_inter)
    {   // Object(s) is/are actually behind the light
        for(shadow_i = 0; shadow_i < shadow_inter_length && !is_color_empty(light_filter); shadow_i++)
        {
            if(shadow_inter[shadow_i].distance < light_distance)
            {
                shadow_obj = shadow_inter[shadow_i].obj;
                if(shadow_obj.translucency_material)
                {
                    light_filter = multiply_color(shadow_obj.translucency_material, multiply_colors(light_filter, shadow_obj.color));
                }
                else
                {
                    light_filter = get_empty_color();
                }
            }

        }
        if(!is_color_empty(light_filter))
        {
            free(shadow_inter);
            shadow_inter = NULL;
        }
    }
    // If there aren't any shadows
    if(!shadow_inter)
    {
        illum_cos = do_dot_product(normal_vec, light_vec);
        // We only take it into account if the angle is lower than 90 degrees
        if(illum_cos > 0)
        {
            Vector light_mirror_vec = subtract_vectors(multiply_vector(2 * illum_cos, normal_vec), light_vec);
            // Attenuation factor, reduces the light energy depending on the distance
            att_factor = get_attenuation_factor(light, light_distance);
            spec_cos = do_dot_product(rev_dir_vec, light_mirror_vec);
            // We add the light source effect
            light_filter = multiply_color(illum_cos * inter.obj.light_material * att_factor, light_filter);
            *all_lights_color = add_colors(*all_lights_color, multiply_colors(light_filter, light.color));
            // The specular light, is the white stain on the objects
            if(spec_cos > 0)
            {
                *all_spec_light += pow(spec_cos * inter.obj.specular_material * att_factor, inter.obj.specular_pow);
            }
        }
    }
    else free(shadow_inter);
}

/*
 * Gets the color of the intersection by calculating light intensity,
 * (which includes specular light, shadows, transparency, etc)
 *
 * light: Light for which the attenuation factor is calculated.
 * distance: Distance between the light and an illuminated spot.
 * mirror_level: Current level of reflection.
 * conf: Configuration of the scene.
 */
Color get_intersection_color(Vector eye,
                             Vector dir_vec,
                             Intersection *inter_list,
                             int inter_length,
                             int mirror_level,
                             int transparency_level,
                             SceneConfig conf)
{
    Intersection inter;
    int light_index;
    long double spec_light_factor, mirror_factor, transparency_factor;
    Vector normal_vec, rev_dir_vec, reflection_vec;
    Light light;
    Color all_lights_color, color_found, reflection_color,
          transparency_color, final_color;

    inter = inter_list[transparency_level];
    // Light intensity
    all_lights_color = (Color){ .red = 0.0, .green = 0.0, .blue = 0.0 };
    // Specular light intensity
    spec_light_factor = 0.0;
    normal_vec = get_normal_vector(&inter);
    // Pick up the normal vector that is pointing to the eye
    if(do_dot_product(normal_vec, dir_vec) > 0)
        normal_vec = multiply_vector(-1, normal_vec);
    // Initialize reverse direction vector for mirrors and specular light
    rev_dir_vec = multiply_vector(-1, dir_vec);
    for(light_index = 0; light_index < conf.lights_length; light_index++)
    {
        light = conf.lights[light_index];
        apply_light_source(light, inter, normal_vec, rev_dir_vec, &all_lights_color, &spec_light_factor, conf);
    }
    // We add the environmental light of the scene
    all_lights_color = add_colors(all_lights_color, multiply_color(inter.obj.light_ambiental, conf.environment_light));
    if(spec_light_factor > 1.0)
        spec_light_factor = 1.0;
    color_found = multiply_colors(all_lights_color, inter.obj.color);
    // Specular light gives a color between enlightened color and the light color.
    color_found.red += (1 - color_found.red) * spec_light_factor;
    color_found.green += (1 - color_found.green) * spec_light_factor;
    color_found.blue += (1 - color_found.blue) * spec_light_factor;
    // Get transparency color
    transparency_factor = inter.obj.transparency_material;
    if (transparency_level < conf.max_transparency_level &&
        transparency_factor > 0.0)
    {
        if(transparency_level + 1 < inter_length)
        {
            transparency_color = get_intersection_color(eye,dir_vec,inter_list,inter_length,0,transparency_level+1, conf);
        }
        else
        {
            transparency_color = conf.background;
        }
    }
    else
    {
        transparency_factor = 0.0;
        transparency_color = get_empty_color();
    }
    // Get reflection color
    mirror_factor = inter.obj.mirror_material;
    if (mirror_level < conf.max_mirror_level &&
        mirror_factor > 0.0)
    {
        reflection_vec = subtract_vectors(multiply_vector(2 * do_dot_product(normal_vec, rev_dir_vec), normal_vec), rev_dir_vec);
        reflection_color = get_color(inter.posn, reflection_vec, mirror_level + 1, conf);
    }
    else
    {
        mirror_factor = 0;
        reflection_color = get_empty_color();
    }
    // Calculate final color
    transparency_color = multiply_color(transparency_factor, transparency_color);
    reflection_color = multiply_color((1.0-transparency_factor) * mirror_factor, reflection_color);
    color_found = multiply_color((1.0-transparency_factor) * (1.0-mirror_factor), color_found);
    final_color = add_colors(transparency_color, add_colors(reflection_color, color_found));
    return final_color;
}

/*
 * Returns the color that is seen from the position 'eye' when looking at the
 * tridimensional scene towards the direction 'dir_vec'.
 *
 * eye: Position from which the scene is seen.
 * dir_vec: Direction at which the eye is looking. This vector must be normalized.
 * mirror_level: Current level of reflection.
 * conf: Configuration of the scene.
 */
Color get_color(Vector eye, Vector dir_vec, int mirror_level, SceneConfig conf)
{
	Intersection *inter_list;
	Color color;
	// Get intersections on the given direction. Intersections are ordered from the nearest to the farthest.
	int inter_list_length;
	inter_list = get_intersections(eye, dir_vec, &inter_list_length, conf);
	// If we don't find an intersection we return the background, otherwise we check for the intersections's color.
	if (!inter_list) return conf.background;
	color = get_intersection_color(eye, dir_vec, inter_list, inter_list_length, mirror_level, 0, conf);
	free(inter_list);
	return color;
}
color scene_3D::cast_ray(line_3D line, double threshold, unsigned int recursion_depth)
  {
    unsigned int k, l, m;
    triangle_3D triangle;
    color final_color, helper_color, add_color;
    double depth, t;
    double *texture_coords_a, *texture_coords_b, *texture_coords_c;
    double barycentric_a, barycentric_b, barycentric_c;
    point_3D starting_point;
    point_3D normal,normal_a,normal_b,normal_c;
    point_3D reflection_vector, incoming_vector_reverse;
    material mat;
    int color_sum[3];

    line.get_point(0,starting_point);

    depth = 99999999;

    final_color.red = this->background_color.red;
    final_color.green = this->background_color.green;
    final_color.blue = this->background_color.blue;

    for (k = 0; k < this->meshes.size(); k++)
      {
        if (!line.intersects_sphere(this->meshes[k]->bounding_sphere_center,this->meshes[k]->bounding_sphere_radius))
          continue;

        for (l = 0; l < this->meshes[k]->triangle_indices.size(); l += 3)
          {
            triangle.a = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l]].position;
            triangle.b = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 1]].position;
            triangle.c = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 2]].position;

            texture_coords_a = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l]].texture_coords;
            texture_coords_b = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 1]].texture_coords;
            texture_coords_c = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 2]].texture_coords;

            if (line.intersects_triangle(triangle,barycentric_a,barycentric_b,barycentric_c,t))
              {
                point_3D intersection;
                line.get_point(t,intersection);
                double distance = point_distance(starting_point,intersection);

                mat = this->meshes[k]->get_material();

                if (distance < depth && distance > threshold)  // depth test
                  {
                    depth = distance;

                    normal_a = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l]].normal;
                    normal_b = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 1]].normal;
                    normal_c = this->meshes[k]->vertices[this->meshes[k]->triangle_indices[l + 2]].normal;

                    normal.x = 0;
                    normal.y = 0;
                    normal.z = 0;

                    normal.x = barycentric_a * normal_a.x + barycentric_b * normal_b.x + barycentric_c * normal_c.x;
                    normal.y = barycentric_a * normal_a.y + barycentric_b * normal_b.y + barycentric_c * normal_c.y;
                    normal.z = barycentric_a * normal_a.z + barycentric_b * normal_b.z + barycentric_c * normal_c.z;
                    normalize(normal);  // interpolation breaks normalization

                    if (!this->meshes[k]->use_3D_texture && this->meshes[k]->get_texture() != 0)        // 2d texture
                      {
                        double u,v;

                        u = barycentric_a * texture_coords_a[0] + barycentric_b * texture_coords_b[0] + barycentric_c * texture_coords_c[0];
                        v = barycentric_a * texture_coords_a[1] + barycentric_b * texture_coords_b[1] + barycentric_c * texture_coords_c[1];

                        color_buffer_get_pixel(this->meshes[k]->get_texture(),u * this->meshes[k]->get_texture()->width,v * this->meshes[k]->get_texture()->height,&final_color.red,&final_color.green,&final_color.blue);
                      }
                    else if (this->meshes[k]->use_3D_texture && this->meshes[k]->get_texture_3D() != 0) // 3d texture
                      {
                        final_color = this->meshes[k]->get_texture_3D()->get_color(intersection.x,intersection.y,intersection.z);
                      }
                    else                                                                                // mesh color
                      {
                        final_color.red = 255;
                        final_color.green = 255;
                        final_color.blue = 255;
                      }

                    helper_color = compute_lighting(intersection,mat,normal);
                    final_color = multiply_colors(helper_color,final_color);

                    if (recursion_depth != 0)
                      {
                        incoming_vector_reverse = line.get_vector_to_origin();

                        if (mat.reflection > 0)                         // reflection
                          {
                            color_sum[0] = 0;
                            color_sum[1] = 0;
                            color_sum[2] = 0;

                            for (m = 0; m < this->reflection_rays; m++)
                              {
                                point_3D helper_point;
                                reflection_vector = make_reflection_vector(normal,incoming_vector_reverse);

                                reflection_vector.x *= -1;
                                reflection_vector.y *= -1;
                                reflection_vector.z *= -1;

                                if (m > 0) // alter the ray slightly
                                  alter_vector(reflection_vector,this->reflection_range);

                                helper_point.x = intersection.x + reflection_vector.x;
                                helper_point.y = intersection.y + reflection_vector.y;
                                helper_point.z = intersection.z + reflection_vector.z;

                                line_3D reflection_line(intersection,helper_point);

                                add_color = cast_ray(reflection_line,ERROR_OFFSET,recursion_depth - 1);

                                color_sum[0] += add_color.red;
                                color_sum[1] += add_color.green;
                                color_sum[2] += add_color.blue;
                              }

                            add_color.red = color_sum[0] / this->reflection_rays;
                            add_color.green = color_sum[1] / this->reflection_rays;
                            add_color.blue = color_sum[2] / this->reflection_rays;

                            final_color = interpolate_colors(final_color,add_color,mat.reflection);
                          }

                        if (mat.transparency > 0)                       // refraction
                          {
                            color_sum[0] = 0;
                            color_sum[1] = 0;
                            color_sum[2] = 0;

                            for (m = 0; m < this->refraction_rays; m++)
                              {
                                point_3D helper_point;
                                point_3D refraction_vector;
                                refraction_vector = make_refraction_vector(normal,incoming_vector_reverse,mat.refractive_index);

                                if (m > 0) // alter the ray slightly
                                  alter_vector(refraction_vector,this->refraction_range);

                                helper_point.x = intersection.x + refraction_vector.x;
                                helper_point.y = intersection.y + refraction_vector.y;
                                helper_point.z = intersection.z + refraction_vector.z;

                                line_3D refraction_line(intersection,helper_point);
                                add_color = cast_ray(refraction_line,ERROR_OFFSET,recursion_depth - 1);

                                color_sum[0] += add_color.red;
                                color_sum[1] += add_color.green;
                                color_sum[2] += add_color.blue;
                              }

                            add_color.red = color_sum[0] / this->refraction_rays;
                            add_color.green = color_sum[1] / this->refraction_rays;
                            add_color.blue = color_sum[2] / this->refraction_rays;

                            final_color = interpolate_colors(final_color,add_color,mat.transparency);
                          }
                      }
                  }
                }
              }
            }

    return final_color;
  }