static bool sfc_hit_planar(bool is_triangle, point3_t* A, point3_t* B, point3_t* C, ray3_t* ray, float t0, float t1, hit_record_t* hit) { // Use the notation of Shirley & Marschner, Section 4.4.2. float a = A->x - B->x ; float b = A->y - B->y ; float c = A->z - B->z ; float d = A->x - C->x ; float e = A->y - C->y ; float f = A->z - C->z ; float g = ray->dir.x ; float h = ray->dir.y ; float i = ray->dir.z ; float j = A->x - ray->base.x ; float k = A->y - ray->base.y ; float l = A->z - ray->base.z ; float ei = e*i, hf = h*f, gf = g*f, di = d*i, dh = d*h, eg = e*g ; float ak = a*k, jb = j*b, jc = j*c, al = a*l, bl = b*l, kc = k*c ; float ei_hf = ei-hf, gf_di = gf-di, dh_eg = dh-eg ; float ak_jb = ak-jb, jc_al = jc-al, bl_kc = bl-kc ; float M = a*ei_hf + b*gf_di + c*dh_eg ; float t = -(f*ak_jb + e*jc_al + d*bl_kc)/M ; if (t <= t0 || t > t1) return false ; float beta = (j*ei_hf + k*gf_di + l*dh_eg)/M ; if (is_triangle && (beta < 0 || beta > 1)) return false ; float gamma = (i*ak_jb + h*jc_al + g*bl_kc)/M ; if (!is_triangle || (0 <= gamma && beta+gamma <= 1)) { hit->t = t ; vector3_t b_minus_a, c_minus_a ; pv_subtract(B, A, &b_minus_a) ; pv_subtract(C, A, &c_minus_a) ; cross(&b_minus_a, &c_minus_a, &(hit->normal)) ; normalize(&(hit->normal)) ; multiply(&b_minus_a, beta, &b_minus_a) ; multiply(&c_minus_a, gamma, &c_minus_a) ; pv_add(A, &b_minus_a, &(hit->hit_pt)) ; pv_add(&(hit->hit_pt), &c_minus_a, &(hit->hit_pt)) ; return true ; } else return false ; }
static bool sfc_hit_sphere(void* data, ray3_t* ray, float t0, float t1, hit_record_t* hit) { sphere_data_t* sdata = (sphere_data_t*)data ; point3_t ctr = sdata->center ; float radius = sdata->radius ; point3_t* e = &ray->base ; vector3_t* d = &ray->dir ; // First see if there is any chance we hit the sphere. We do this // by checking whether any part of the sphere is inside the sphere // of radius t1 from the base of the ray. With even just one sphere // in the scene, I found that this check actually slowed down rendering, // probably because of all the square-root computations that turn // out to give no helpful information (i.e., this test says to // continue checking, but then the sphere isn't hit anyway). // if (t1 < (dist(e, &ctr) - radius)) return false ; vector3_t e_minus_ctr ; pv_subtract(e, &ctr, &e_minus_ctr) ; float e_minus_ctr2 = dot(&e_minus_ctr, &e_minus_ctr) ; float d2 = dot(d, d) ; // Compute the discriminant first. float b = dot(d, &e_minus_ctr) ; float discr = b*b - d2*(e_minus_ctr2 - radius*radius) ; // Compute hit position if discr. is >= 0, and also compute // the surface normal. if (discr < 0) return false ; else { // Hit position. float num = min(-b - sqrt(discr), -b + sqrt(discr)) ; float t = num/d2 ; if (t < t0 || t > t1) return false ; hit->t = t ; vector3_t ray_vec = *d ; multiply(&ray_vec, hit->t, &ray_vec) ; pv_add(e, &ray_vec, &(hit->hit_pt)) ; // Surface normal. pv_subtract(&(hit->hit_pt), &ctr, &(hit->normal)) ; normalize(&(hit->normal)) ; return true ; } }
enum pv_boolean pv_is_array_ref (pv_t addr, CORE_ADDR size, pv_t array_addr, CORE_ADDR array_len, CORE_ADDR elt_size, int *i) { /* Note that, since .k is a CORE_ADDR, and CORE_ADDR is unsigned, if addr is *before* the start of the array, then this isn't going to be negative... */ pv_t offset = pv_subtract (addr, array_addr); if (offset.kind == pvk_constant) { /* This is a rather odd test. We want to know if the SIZE bytes at ADDR don't overlap the array at all, so you'd expect it to be an || expression: "if we're completely before || we're completely after". But with unsigned arithmetic, things are different: since it's a number circle, not a number line, the right values for offset.k are actually one contiguous range. */ if (offset.k <= -size && offset.k >= array_len * elt_size) return pv_definite_no; else if (offset.k % elt_size != 0 || size != elt_size) return pv_maybe; else { *i = offset.k / elt_size; return pv_definite_yes; } } else return pv_maybe; }
/** Compute the eye frame basis from the eye point, look-at point, * and up direction. The basic algorithm: * -# w <- normalized (eye - look_at) * -# u <- normalized (up x w) * -# v <- w x u. */ void compute_eye_frame_basis() { pv_subtract(&eye, &look_at, &eye_frame_w); normalize(&eye_frame_w); cross(&up_dir, &eye_frame_w, &eye_frame_u); normalize(&eye_frame_u); cross(&eye_frame_w, &eye_frame_u, &eye_frame_v); debug("compute_eye_frame_basis(): eye_frame_u = (%f, %f, %f)", eye_frame_u.x, eye_frame_u.y, eye_frame_u.z); debug("compute_eye_frame_basis(): eye_frame_v = (%f, %f, %f)", eye_frame_v.x, eye_frame_v.y, eye_frame_v.z); debug("compute_eye_frame_basis(): eye_frame_w = (%f, %f, %f)", eye_frame_w.x, eye_frame_w.y, eye_frame_w.z); }
/** Get the shade determined by a given ray. * * @param ray the ray to trace. * @param t0 the start of the interval for which to get a shade. * @param t1 the end of the interval for which to get a shade. * @param depth the maximum number of times a reflect ray will be * cast for objects with non-NULL reflective color. * @param in_trans whether the ray is inside a transparent surface or not. * * * @return the color corresponding to the closest object hit by * <code>r</code> in the interval <code>[t0, t1]</code>. */ color_t ray_trace(ray3_t ray, float t0, float t1, int depth, bool in_trans) { assert(depth >= 0); color_t color = {0.0, 0.0, 0.0}; if (depth == 0) return color; hit_record_t hit_rec, closest_hit_rec; // Get a hit record for the closest object that is hit. bool hit_something = false; list356_itr_t* s = lst_iterator(surfaces); while (lst_has_next(s)) { surface_t* sfc = lst_next(s); if (sfc_hit(sfc, &ray, t0, t1, &hit_rec)) { if (hit_rec.t < t1) { hit_something = true; memcpy(&closest_hit_rec, &hit_rec, sizeof(hit_record_t)); t1 = hit_rec.t; } else assert("wrong"); } } lst_iterator_free(s); // If we hit something, color the pixel. if (hit_something) { surface_t* sfc = closest_hit_rec.sfc; // Specular reflection. if (spec_reflection) { if (sfc->refl_color != NULL) { color_t refl_color = get_specular_refl(&ray, &closest_hit_rec, depth, in_trans); add_scaled_color(&color, sfc->refl_color, &refl_color, 1.0f); } } // Tranparency if (transparency) { if (sfc->refr_index != -1) { color_t trans_color = get_transparency(&ray, &closest_hit_rec, depth, !in_trans); // Only add returned color, don't multiply by a surface_color. color.red += (trans_color.red); color.green += (trans_color.green); color.blue += (trans_color.blue); } } // Ambient shading. if (ambient_shading) { add_scaled_color(&color, sfc->ambient_color, &ambient_light, 1.0f); } // Lighting. if (lighting) { list356_itr_t* light_itr = lst_iterator(lights); while (lst_has_next(light_itr)) { light_t* light = lst_next(light_itr); vector3_t light_dir; pv_subtract(light->position, &(closest_hit_rec.hit_pt), &light_dir); normalize(&light_dir); // Check for global shadows. bool do_lighting = true; // Bool for if the shadow is caused by transparent surface. bool trans_shadow = false; ray3_t light_ray = {closest_hit_rec.hit_pt, light_dir}; float light_dist = dist(&closest_hit_rec.hit_pt, light->position); s = lst_iterator(surfaces); while (lst_has_next(s)) { surface_t* sfc = lst_next(s); if (sfc_hit(sfc, &light_ray, EPSILON, light_dist, &hit_rec)) { do_lighting = false; // If shadow is caused by transparent surface, we add // Lambertian shading only so our shadows are not opaque. if (hit_rec.sfc->refr_index != -1) trans_shadow = true; break; } } lst_iterator_free(s); if (!do_lighting) { continue; } // Lambertian shading. if (lambertian_shading) { if (!trans_shadow) { float scale = get_lambert_scale(&light_dir, &closest_hit_rec); add_scaled_color(&color, sfc->diffuse_color, light->color, scale); } } // Blin-Phong shading (if shadow is not caused by transparent // surface). if (blin_phong_shading) { if (!trans_shadow) { float phong_scale = get_blinn_phong_scale(&ray, &light_dir, &closest_hit_rec); add_scaled_color(&color, sfc->spec_color, light->color, phong_scale); } } } lst_iterator_free(light_itr); } } // if (hit_something) return color; }
/** Get the shade determined by a given ray. * * @param ray the ray to trace. * @param t0 the start of the interval for which to get a shade. * @param t1 the end of the interval for which to get a shade. * @param depth the maximum number of times a reflect ray will be * cast for objects with non-NULL reflective color. * * @return the color corresponding to the closest object hit by * <code>r</code> in the interval <code>[t0, t1]</code>. */ color_t ray_trace(ray3_t ray, float t0, float t1, int depth) { assert(depth >= 0) ; color_t color = {0.0, 0.0, 0.0} ; if (depth ==0) return color ; hit_record_t hit_rec, closest_hit_rec ; // Get a hit record for the closest object that is hit. bool hit_something = false ; list356_itr_t* s = lst_iterator(surfaces) ; while (lst_has_next(s)) { surface_t* sfc = lst_next(s) ; if (sfc_hit(sfc, &ray, t0, t1, &hit_rec)) { if (hit_rec.t < t1) { hit_something = true ; memcpy(&closest_hit_rec, &hit_rec, sizeof(hit_record_t)) ; t1 = hit_rec.t ; } } } lst_iterator_free(s) ; // If we hit something, color the pixel. if (hit_something) { surface_t* sfc = closest_hit_rec.sfc ; // Specular reflection. if (sfc->refl_color != NULL) { color_t refl_color = get_specular_refl(&ray, &closest_hit_rec, depth) ; add_scaled_color(&color, sfc->refl_color, &refl_color, 1.0f) ; } // Ambient shading. add_scaled_color(&color, sfc->ambient_color, &ambient_light, 1.0f) ; // Lighting. list356_itr_t* light_itr = lst_iterator(lights) ; while (lst_has_next(light_itr)) { light_t* light = lst_next(light_itr) ; vector3_t light_dir ; pv_subtract(light->position, &(closest_hit_rec.hit_pt), &light_dir) ; normalize(&light_dir) ; // Check for global shadows. bool do_lighting = true ; ray3_t light_ray = {closest_hit_rec.hit_pt, light_dir} ; float light_dist = dist(&closest_hit_rec.hit_pt, light->position) ; s = lst_iterator(surfaces) ; while (lst_has_next(s)) { surface_t* sfc = lst_next(s) ; if (sfc_hit(sfc, &light_ray, EPSILON, light_dist, &hit_rec)) { do_lighting = false ; break ; } } lst_iterator_free(s) ; if (!do_lighting) { continue ; } // Lambertian shading. float scale = get_lambert_scale(&light_dir, &closest_hit_rec) ; add_scaled_color(&color, sfc->diffuse_color, light->color, scale) ; // Blin-Phong shading. float phong_scale = get_blinn_phong_scale(&ray, &light_dir, &closest_hit_rec) ; add_scaled_color(&color, sfc->spec_color, light->color, phong_scale) ; } // while(lst_has_next(light_itr)) lst_iterator_free(light_itr) ; } // if (hit_something) return color ; }