RNRgb R3DirectionalLight:: SpecularReflection(const R3Brdf& brdf, const R3Point& eye, const R3Point& point, const R3Vector& normal) const { // Check if light is active if (!IsActive()) return RNblack_rgb; // Get material properties const RNRgb& Sc = brdf.Specular(); RNScalar s = brdf.Shininess(); // Get light properties const RNRgb& Ic = Color(); RNScalar I = Intensity(); R3Vector L = -(Direction()); // Compute geometric stuff RNScalar NL = normal.Dot(L); if (RNIsNegativeOrZero(NL)) return RNblack_rgb; R3Vector R = (2.0 * NL) * normal - L; R3Vector V = eye - point; V.Normalize(); RNScalar VR = V.Dot(R); if (RNIsNegativeOrZero(VR)) return RNblack_rgb; // Return specular component of reflection return (I * pow(VR,s)) * Sc * Ic; }
RNRgb R3PointLight:: Reflection(const R3Brdf& brdf, const R3Point& eye, const R3Point& point, const R3Vector& normal) const { // Check if light is active if (!IsActive()) return RNblack_rgb; // Get material properties const RNRgb& Dc = brdf.Diffuse(); const RNRgb& Sc = brdf.Specular(); RNScalar s = brdf.Shininess(); // Get light properties RNScalar I = IntensityAtPoint(point); R3Vector L = DirectionFromPoint(point); const RNRgb& Ic = Color(); // Compute geometric stuff RNScalar NL = normal.Dot(L); if (RNIsNegativeOrZero(NL)) return RNblack_rgb; R3Vector R = (2.0 * NL) * normal - L; R3Vector V = eye - point; V.Normalize(); RNScalar VR = V.Dot(R); // Compute diffuse reflection RNRgb rgb = (I * NL) * Dc * Ic; // Compute specular reflection if (RNIsPositive(VR)) rgb += (I * pow(VR,s)) * Sc * Ic; // Return total reflection return rgb; }
// compute intersection between a sphere and a ray R3Intersection ComputeIntersection(R3Sphere *sphere, R3Ray &ray) { R3Intersection i; bool in_front_of_center = false; bool internal = false; R3Vector l = sphere->Center() - ray.Start(); double tca = l.Dot(ray.Vector()); if (tca < 0) // case where ray originates within sphere and points away from its center { tca = -tca; in_front_of_center = true; internal = true; } double d2 = l.Dot(l) - tca * tca; double r2 = sphere->Radius() * sphere->Radius(); if (d2 > r2) // case where ray misses sphere entirely { i.hit = false; return i; } double thc = sqrt(r2 - d2); double t; if (!in_front_of_center) { t = tca - thc; if (t < 0) { t = tca + thc; internal = true; } } else { t = thc - tca; } if (t < 0) { i.hit = false; return i; } R3Point p = ray.Point(t); R3Vector n = p - sphere->Center(); n.Normalize(); if (internal) n = -n; // populate intersection data i.hit = true; i.normal = n; i.position = p; i.t = t; return i; }
// compute the ray reflected from the incoming ray r at intersection i R3Ray GetReflectedRay(R3Intersection i, R3Ray r) { // add offset to point to ensure the same intersection is not found again due to floating point error R3Point offset = i.position + i.normal * EPSILON; R3Vector v = -r.Vector(); return R3Ray(offset, 2 * v.Dot(i.normal) * i.normal - v); }
double R3Distance(const R3Line& line1, const R3Line& line2) { // Return distance from line to line (Riddle p. 905) R3Vector v = line1.Vector(); v.Cross(line2.Vector()); return v.Dot(line1.Point() - line2.Point()); }
double R3Distance(const R3Point& point, const R3Ray& ray) { // Check if start point is closest R3Vector v = point - ray.Start(); double dir = v.Dot(ray.Vector()); if (dir < 0) return v.Length(); // Return distance from point to ray line return R3Distance(point, ray.Line()); }
// compute ray refracted from incoming ray r using Snell's Law // NOTE: I assume that refracting objects will always be non-intersecting and that all intersections passed into this function // will be from empty space with an IOR of 1 to the interior of a shape R3Ray GetRefractedRay(R3Intersection i, R3Ray r, double ior, R3Scene *scene) { // this time push the point inside the object R3Point internal_offset = i.position - i.normal * EPSILON; // calculate the ray that will be cast through the object R3Vector v = -r.Vector(); double cos_vn = v.Dot(i.normal); R3Vector internal_vec = i.normal * (cos_vn / ior - sqrt(1 - (1 - cos_vn * cos_vn) / (ior * ior))) - v / ior; R3Ray trans_ray(internal_offset, internal_vec); // cast this ray and determine it's exit point from the refracting object R3Intersection exit_i = ComputeIntersection(scene, scene->Root(), trans_ray, numeric_limits<double>::infinity()); assert (exit_i.hit); // calculate the refracted exit ray and R3Point external_offset = exit_i.position - exit_i.normal * EPSILON; v = -trans_ray.Vector(); cos_vn = v.Dot(exit_i.normal); R3Vector external_vec = exit_i.normal * (cos_vn * ior - sqrt(1 - (1 - cos_vn * cos_vn) * (ior * ior))) - v * ior; return R3Ray(external_offset, external_vec); }
int IntersectSphere(R3Sphere *s, R3Ray r, R3Point *position, R3Vector *normal, double *t) { R3Vector l = s->Center() - r.Start(); double tca = l.Dot(r.Vector()); if(tca < 0) return 0; double d2 = l.Dot(l) - tca*tca; if(d2 > s->Radius() * s->Radius()) return 0; double thc = sqrt(s->Radius() * s->Radius() - d2); *t = thc > 0 ? tca - thc : tca + thc; if(*t < 0) { *t = thc > 0 ? tca + thc : tca - thc; } if(*t < 0) { return 0; } *position = r.Start() + *t * r.Vector(); *normal = *position - s->Center(); normal->Normalize(); return 1; }
void R3Matrix::Rotate(const R3Vector& from, const R3Vector& to) { // rotate matrix that takes direction of vector "from" -> "to" // This is a quickie hack -- there's got to be a better way double d1 = from.Length(); if (d1 == 0) return; double d2 = to.Length(); if (d2 == 0) return; double cosine = from.Dot(to) / (d1 * d2); double radians = acos(cosine); R3Vector axis = from % to; axis.Normalize(); Rotate(axis, radians); }
double R3Distance(const R3Ray& ray, const R3Segment& segment) { // There's got to be a better way ??? // Get vectors in more convenient form const R3Vector v1 = ray.Vector(); const R3Vector v2 = segment.Vector(); // Compute useful intermediate values const double v1v1 = 1.0; // v1.Dot(v1); const double v2v2 = 1.0; // v2.Dot(v2); double v1v2 = v1.Dot(v2); double denom = v1v2*v1v2 - v1v1*v2v2; // Check if ray and segment are parallel if (denom == 0) { // Not right ??? // Look at directions of vectors, then check relative starts and stops return R3Distance(segment.Line(), ray.Line()); } else { // Find closest points const R3Vector p1 = ray.Start().Vector(); const R3Vector p2 = segment.Start().Vector(); double p1v1 = v1.Dot(p1); double p2v2 = v2.Dot(p2); double p1v2 = v2.Dot(p1); double p2v1 = v1.Dot(p2); double ray_t = (v1v2*p2v2 + v2v2*p1v1 - v1v2*p1v2 - v2v2*p2v1) / denom; double segment_t = (v1v2*p1v1 + v1v1*p2v2 - v1v2*p2v1 - v1v1*p1v2) / denom; R3Point ray_point = (ray_t <= 0.0) ? ray.Start() : ray.Point(ray_t); R3Point segment_point = (segment_t <= 0.0) ? segment.Start() : (segment_t >= segment.Length()) ? segment.End() : segment.Ray().Point(segment_t); double distance = R3Distance(ray_point, segment_point); return distance; } }
// Compute the luminance for a light at a specific point R3Rgb ComputeLuminance(R3Light *light, R3Point &point) { R3Rgb il(0, 0, 0, 1); switch (light->type) { // quadratic attenuation factor case R3_POINT_LIGHT: { double d = (light->position - point).Length(); double att = light->constant_attenuation + light->linear_attenuation * d + light->quadratic_attenuation * d * d; il = light->color / att; break; } // constant luminance case R3_DIRECTIONAL_LIGHT: { il = light->color; break; } // quadratic attenuation with directional falloff case R3_SPOT_LIGHT: { R3Vector l = (point - light->position); l.Normalize(); double cost = l.Dot(light->direction); if (acos(cost) <= light->angle_cutoff) { double d = (light->position - point).Length(); double att = light->constant_attenuation + light->linear_attenuation * d + light->quadratic_attenuation * d * d; il = light->color * pow(cost, light->angle_attenuation) / att; } break; } // not implemented case R3_AREA_LIGHT: break; case R3_NUM_LIGHT_TYPES: break; } return il; }
RNRgb R3PointLight:: DiffuseReflection(const R3Brdf& brdf, const R3Point& point, const R3Vector& normal) const { // Check if light is active if (!IsActive()) return RNblack_rgb; // Get material properties const RNRgb& Dc = brdf.Diffuse(); // Get light properties const RNRgb& Ic = Color(); RNScalar I = IntensityAtPoint(point); R3Vector L = DirectionFromPoint(point); // Compute geometric stuff RNScalar NL = normal.Dot(L); if (RNIsNegativeOrZero(NL)) return RNblack_rgb; // Return diffuse component of reflection return (I * NL) * Dc * Ic; }
RNBoolean R3Perpendicular(const R3Vector& vector1, const R3Vector& vector2) { // Normalized vectors ??? // Return whether vector1 and vector2 are perpendicular return RNIsZero(vector1.Dot(vector2)); }
// compute intersection between a triangle face and a ray R3Intersection ComputeIntersection(R3MeshFace *tri, R3Ray &ray, double min_t) { assert (tri->vertices.size() == 3); R3Intersection i; R3Point t1 = tri->vertices[0]->position; R3Point t2 = tri->vertices[1]->position; R3Point t3 = tri->vertices[2]->position; // intersect ray with triangle's plane R3Plane plane = tri->plane; double t = -(ray.Start().Vector().Dot(plane.Normal()) + plane.D()) / (ray.Vector().Dot(plane.Normal())); // early return if not closer than minimum intersection for the mesh if (t > min_t || t < 0) { i.hit = false; return i; } // check if intersection is within triangle using barycentric coordinate method R3Point p = ray.Point(t); R3Vector v; v = t2 - t1; v.Cross(t3 - t1); double area = v.Length() / 2; v = t2 - t1; v.Cross(p - t1); if (v.Dot(plane.Normal()) < 0) { i.hit = false; return i; } double a = v.Length() / (2 * area); v = p - t1; v.Cross(t3 - t1); if (v.Dot(plane.Normal()) < 0) { i.hit = false; return i; } double b = v.Length() / (2 * area); if (a <= 1 && a >= 0 && b <= 1 && b >= 0 && a + b <= 1) { i.hit = true; if (ray.Vector().Dot(plane.Normal()) < 0) { i.normal = plane.Normal(); } else { i.normal = -plane.Normal(); } i.position = p; i.t = t; return i; } else { i.hit = false; return i; } }
// Compute the radiance emitted back along a given ray from a given intersection R3Rgb ComputeRadiance(R3Scene *scene, R3Ray &ray, R3Intersection &intersection, int depth, int max_depth, int distrib) { R3Rgb il(0, 0, 0, 1); R3Material *mat = intersection.node->material; // add a small epsilon normal to the intersection point so light and secondary rays don't intersect with the same surface R3Point offset_intersection_pos = intersection.position + intersection.normal * EPSILON; // v = a unit vector pointing from the intersection to the ray origin R3Vector v = -ray.Vector(); // if we are not using distributed ray tracing... if (distrib == 0) { // include lighting from all scene lights for (int i = 0; i < scene->NLights(); i++) { R3Light *light = scene->Light(i); // l = a unit vector pointing from the intersection to a light // d = the distance between the intersection and a light R3Vector l; double d; if (light->type == R3_DIRECTIONAL_LIGHT) { l = -light->direction; d = numeric_limits<double>::infinity(); } else { l = (light->position - intersection.position); l.Normalize(); d = (light->position - intersection.position).Length(); } // construct a ray from the intersection toward the light R3Ray light_ray(offset_intersection_pos, l); // compute a shadow intersection and if one exists (that is between the intersection and the light), ignore the light R3Intersection shadow_intersection = ComputeIntersection(scene, scene->Root(), light_ray, d); if (shadow_intersection.hit && shadow_intersection.t < d) continue; // compute the luminance of the light at the intersection position R3Rgb light_il = ComputeLuminance(light, intersection.position); // evaluate the Phong BRDF equations for diffuse and specular reflection double cos_nl = l.Dot(intersection.normal); // r = l reflected across the intersection normal R3Vector r = 2 * cos_nl * intersection.normal - l; double cos_vr = v.Dot(r); // if the light is shining on the front of the surface, include diffuse lighting // if the light is also less than 90 degrees from the camera vector, include specular lighting if (cos_nl > 0) { il += light_il * mat->kd * cos_nl; if (cos_vr > 0) il += light_il * mat->ks * pow(cos_vr, mat->shininess); } } } else // if we ARE using distributed... { if (depth < max_depth) { int nrays = 0; R3Rgb dist_il(0, 0, 0, 1); while (nrays < distrib) { // randomly sample outgoing directions using random variables with normal distributions double u1 = (double)rand()/RAND_MAX; double u2 = (double)rand()/RAND_MAX; double u3 = (double)rand()/RAND_MAX; double u4 = (double)rand()/RAND_MAX; double s1 = sqrt(-2 * log(u1)); double rx = s1 * cos(2 * 3.14159 * u2); double ry = s1 * sin(2 * 3.14159 * u2); double s2 = sqrt(-2 * log(u3)); double rz = s2 * cos(2 * 3.14159 * u4); R3Vector l(rx, ry, rz); l.Normalize(); // check if vector is in the right hemisphere if (l.Dot(intersection.normal) < 0) continue; // generate a random ray and recursively compute the radiance it contributes R3Ray rray(offset_intersection_pos, l); R3Rgb rgb = ComputeRadiance(scene, rray, depth + 1, max_depth, distrib); // evaluate the Phong BRDF equations for diffuse and specular reflection double cos_nl = l.Dot(intersection.normal); // r = l reflected across the intersection normal R3Vector r = 2 * cos_nl * intersection.normal - l; double cos_vr = v.Dot(r); // if the light is shining on the front of the surface, include diffuse lighting // if the light is also less than 90 degrees from the camera vector, include specular lighting if (cos_nl > 0) { dist_il += rgb * mat->kd * cos_nl; if (cos_vr > 0) dist_il += rgb * mat->ks * pow(cos_vr, mat->shininess); } nrays++; } il += dist_il / nrays; } } // if the material has a specular component and the maximum depth hasn't been reached, reflect the camera ray from the // intersection surface and recusively evaluate its radiance if (!mat->ks.IsBlack() && depth < max_depth) { R3Ray spec_ray = GetReflectedRay(intersection, ray); R3Rgb spec = ComputeRadiance(scene, spec_ray, depth + 1, max_depth, distrib); il += spec * mat->ks; } // add refraction component for both distributed and non-distributed techniques if (!mat->kt.IsBlack() && depth < max_depth) { R3Ray trans_ray = GetRefractedRay(intersection, ray, mat->indexofrefraction, scene); R3Rgb trans = ComputeRadiance(scene, trans_ray, depth + 1, max_depth, distrib); il += trans * mat->kt; } // add ambient and emission components il += mat->emission; il += mat->ka * scene->ambient; return il; }
double R3SquaredDistance(const R3Point& point1, const R3Point& point2) { // Return length of vector between points R3Vector v = point1 - point2; return v.Dot(v); }