/* * 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; }