Newsgroups: comp.lang.functional
From: Jon Harrop <use...@jdh30.plus.com>
Date: Thu, 02 Jun 2005 09:02:22 +0100
Local: Thurs 2 Jun 2005 09:02
Subject: Re: Port my ray tracer
alex goldman wrote: C does a lot better on AMD64 but I believe the difference is due to the > Jon Harrop wrote: >> C++ 1.605s g++-3.4 -O3 -funroll-all-loops -ffast-math ray.cpp -o > What explains such big difference here? efficiency of inlined const reference passing of vectors in C++ compared to the naive approach used by the C code. Also, the C++ code includes some optimisations not in the C code because it > Where is the code actually used for benchmarking, BTW? Here's the OCaml: (* The Great Computer Language Shootout (* This implementation differs from the original in several ways: Uses an implicit scene, generated as a ray is traced rather than being Specialized shadow-ray intersection functions. *) let delta = sqrt epsilon_float and pi = 4. *. atan 1. (* 3D vector and associated functions *) (* A semi-infinite ray starting at "orig" and with direction "dir". *) (* Calculate the parametric intersection of the given ray with the given (* Calculate whether or not the given ray intersects the given sphere. *) (* Ratio of the radii of one level of spheres to the next. *) (* Find the first intersection point of the given ray with the scene. *) (* Find if the given ray intersects the scene. *) (* Trace a single ray by casting it into the scene and, if it intersects (* Ray trace the scene at the given resolution. *) Printf.printf "P5\n%d %d\n255\n" n n; Here's the C++: // The Great Computer Language Shootout #include <vector> using namespace std; numeric_limits<double> real; // 3D vector }; Vec operator+(const Vec &a, const Vec &b) { return Vec(a.x + b.x, a.y + b.y, a.z + b.z); } Vec operator-(const Vec &a, const Vec &b) { return Vec(a.x - b.x, a.y - b.y, a.z - b.z); } Vec operator*(double a, const Vec &b) { return Vec(a * b.x, a * b.y, a * b.z); } double dot(const Vec &a, const Vec &b) { return a.x*b.x + a.y*b.y + a.z*b.z; } Vec unitise(const Vec &a) { return (1 / sqrt(dot(a, a))) * a; } // Semi-infinite ray // Scene tree Scene(Vec c, double r) : child(), center(c), radius(r) {} }; // Find the first intersection of the given ray with this sphere double ray_sphere(const Ray &ray, const Scene &s) { Vec v = s.center - ray.orig; double b = dot(v, ray.dir), disc = b*b - dot(v, v) + s.radius*s.radius; if (disc < 0) return infinity; double d = sqrt(disc), t2 = b + d; if (t2 < 0) return infinity; double t1 = b - d; return (t1 > 0 ? t1 : t2); } // Accumulate the first intersection of the given ray with the given scene // The accumulated parameter (lambda) and normal vector (normal) are passed by // reference to avoid having to define a struct to represent the real return // type of this function. void intersect(double &lambda, Vec &normal, const Ray &ray, const Scene &s) { double l = ray_sphere(ray, s); // If there is no intersection with this node or if the intersection point is // farther than the current intersection then return as no closer // intersection is to be found here. if (l >= lambda) return; if (s.child.size() == 0) { // Intersect with a single sphere lambda = l; normal = unitise(ray.orig + l * ray.dir - s.center); } else // Intersect with a group for (std::vector<Scene>::const_iterator it=s.child.begin(); it!=s.child.end(); ++it) intersect(lambda, normal, ray, *it); } // Find any intersection of the given ray with the given scene // This function is significantly faster than the above function because it can // terminate as soon as any intersection is found. // This function is distinguished from the above function by its arguments // (function overloading). bool intersect(const Ray &ray, const Scene &s) { if (ray_sphere(ray, s) == infinity) return false; if (s.child.size() == 0) return true; else for (std::vector<Scene>::const_iterator it=s.child.begin(); it != s.child.end(); ++it) if (intersect(ray, *it)) return true; return false; } // Trace a single ray into the scene double ray_trace(const double weight, const Vec light, const Ray ray, const Scene &s) { // As the accumulator is passed to the "intersect" function by reference, // they cannot be given inline so they are declared as local variables here. double lambda = infinity; Vec normal(0, 0, 0); intersect(lambda, normal, ray, s); if (lambda == infinity) return 0; Vec o = ray.orig + lambda * ray.dir + delta * normal; double g = -dot(normal, light); // If we are on the shadowed side of a sphere then don't bother casting a // shadow ray as we know it will intersect the same sphere. if (g <= 0) return 0.; return (intersect(Ray(o, Vec(0, 0, 0) - light), s) ? 0 : g); } // Recursively build the scene tree Scene create(int level, double r, double x, double y, double z) { if (level == 1) return Scene(Vec(x, y, z), r); Scene group = Scene(Vec(x, y, z), 3*r); group.child.push_back(Scene(Vec(x, y, z), r)); double rn = 3*r/sqrt(12.); for (int dz=-1; dz<=1; dz+=2) for (int dx=-1; dx<=1; dx+=2) group.child.push_back(create(level-1, r/2, x-dx*rn, y+rn, z-dz*rn)); return group; } // Build a scene and trace many rays into it, outputting a PGM image int main(int argc, char *argv[]) { // Number of levels of spheres, resolution and oversampling int level = 6, n = (argc==2 ? atoi(argv[1]) : 256), ss = 4; // Direction of the light Vec light = unitise(Vec(-1, -3, 2)); // Build the scene Scene s = create(level, 1, 0, -1, 0); std::cout << "P5\n" << n << " " << n << "\n255\n"; // In this implementation the Scene destructor recursively deallocates the return 0; } -- Dr Jon D Harrop, Flying Frog Consultancy http://www.ffconsultancy.com You must Sign in before you can post messages.
To post a message, you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
| ||||||||||||||