// Check if the given sphere is intersected by the given ray. // See Shirley et.al., section 10.3.1 // Returns 1 if there is an intersection (and sets the appropriate // fields of ip), or 0 otherwise. static int ray_intersects_sphere(intersection_point* ip, sphere sph, vec3 ray_origin, vec3 ray_direction) { float A, B, C, D; vec3 diff; float t_hit; A = v3_dotprod(ray_direction, ray_direction); diff = v3_subtract(ray_origin, sph.center); B = 2.0 * v3_dotprod(diff, ray_direction); C = v3_dotprod(diff, diff) - sph.radius * sph.radius; D = B*B - 4*A*C; if (D < 0.0) return 0; D = sqrt(D); // We're only interested in the first hit, i.e. the one with // the smallest t_hit, so we check -B-D first, followed by -B+D t_hit = (-B - D)/(2*A); if (t_hit < 0.0) { t_hit = (-B + D)/(2*A); if (t_hit < 0.0) return 0; } ip->t = t_hit; ip->p = v3_add(ray_origin, v3_multiply(ray_direction, t_hit)); ip->n = v3_normalize(v3_subtract(ip->p, sph.center)); ip->i = v3_normalize(v3_negate(ray_direction)); ip->material = sph.material; return 1; }
void ray_trace(void) { vec3 forward_vector, right_vector, up_vector; int i, j; float image_plane_width, image_plane_height; vec3 color; char buf[128]; struct timeval t0, t1; float time_taken; fprintf(stderr, "Ray tracing ..."); gettimeofday(&t0, NULL); num_rays_shot = num_shadow_rays_shot = num_triangles_tested = num_bboxes_tested = 0; // Compute camera coordinate system from camera position // and look-at point up_vector = v3_create(0, 0, 1); forward_vector = v3_normalize(v3_subtract(scene_camera_lookat, scene_camera_position)); right_vector = v3_normalize(v3_crossprod(forward_vector, up_vector)); up_vector = v3_crossprod(right_vector, forward_vector); // Compute size of image plane from the chosen field-of-view // and image aspect ratio. This is the size of the plane at distance // of one unit from the camera position. image_plane_height = 2.0 * tan(0.5*VFOV/180*M_PI); image_plane_width = image_plane_height * (1.0 * framebuffer_width / framebuffer_height); vec3 d, e, u, v, w, ud, vd; float l, r, b, t, nx, ny; // direction d, origin e // ONB u, v, w // image plane edges l, r, b, t // window size nx, ny e = scene_camera_position; u = right_vector, v = up_vector, w = v3_negate(forward_vector); l = -0.5 * image_plane_width, r = 0.5 * image_plane_width; b = 0.5 * image_plane_height, t = -0.5 * image_plane_height; nx = framebuffer_width, ny = framebuffer_height; // Loop over all pixels in the framebuffer for (j = 0; j < ny; j++) { for (i = 0; i < nx; i++) { ud = v3_multiply(u, (l + (r - l) * ((i + 0.5) / nx))); vd = v3_multiply(v, (b + (t - b) * ((j + 0.5) / ny))); d = v3_add(v3_add(ud, vd), v3_negate(w)); color = ray_color(0, e, d); put_pixel(i, j, color.x, color.y, color.z); } sprintf(buf, "Ray-tracing ::: %.0f%% done", 100.0*j/framebuffer_height); glutSetWindowTitle(buf); } // Done! gettimeofday(&t1, NULL); glutSetWindowTitle("Ray-tracing ::: done"); // Output some statistics time_taken = 1.0 * (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1000000.0; fprintf(stderr, " done in %.1f seconds\n", time_taken); fprintf(stderr, "... %lld total rays shot, of which %d camera rays and " "%lld shadow rays\n", num_rays_shot, do_antialiasing ? 4*framebuffer_width*framebuffer_height : framebuffer_width*framebuffer_height, num_shadow_rays_shot); fprintf(stderr, "... %lld triangles intersection tested " "(avg %.1f tri/ray)\n", num_triangles_tested, 1.0*num_triangles_tested/num_rays_shot); fprintf(stderr, "... %lld bboxes intersection tested (avg %.1f bbox/ray)\n", num_bboxes_tested, 1.0*num_bboxes_tested/num_rays_shot); }
static int ray_intersects_triangle(intersection_point* ip, triangle tri, vec3 ray_origin, vec3 ray_direction) { vec3 edge1, edge2; vec3 tvec, pvec, qvec; double det, inv_det; double t, u, v; // u, v are barycentric coordinates // t is ray parameter num_triangles_tested++; edge1 = v3_subtract(scene_vertices[tri.v[1]], scene_vertices[tri.v[0]]); edge2 = v3_subtract(scene_vertices[tri.v[2]], scene_vertices[tri.v[0]]); pvec = v3_crossprod(ray_direction, edge2); det = v3_dotprod(edge1, pvec); if (det < 1.0e-6) return 0; tvec = v3_subtract(ray_origin, scene_vertices[tri.v[0]]); u = v3_dotprod(tvec, pvec); if (u < 0.0 || u > det) return 0; qvec = v3_crossprod(tvec, edge1); v = v3_dotprod(ray_direction, qvec); if (v < 0.0 || u+v > det) return 0; t = v3_dotprod(edge2, qvec); if (t < 0.0) return 0; inv_det = 1.0 / det; t *= inv_det; u *= inv_det; v *= inv_det; // We have a triangle intersection! // Return the relevant intersection values. // Compute the actual intersection point ip->t = t; ip->p = v3_add(ray_origin, v3_multiply(ray_direction, t)); // Compute an interpolated normal for this intersection point, i.e. // we use the barycentric coordinates as weights for the vertex normals ip->n = v3_normalize(v3_add( v3_add( v3_multiply(tri.vn[0], 1.0-u-v), v3_multiply(tri.vn[1], u) ), v3_multiply(tri.vn[2], v))); ip->i = v3_normalize(v3_negate(ray_direction)); ip->material = tri.material; return 1; }