[Shootout-list] Ray tracer developments

Jon Harrop jon@ffconsultancy.com
Sun, 1 May 2005 17:36:11 +0100


--Boundary-00=_7VQdCIbHtM9e9H1
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On Sunday 01 May 2005 15:50, Jon Harrop wrote:
> On Sunday 01 May 2005 15:39, Sebastien Loisel wrote:
> > > The inheritance hierarchy is certainly one of the most verbose parts.
> > > But what would you use instead? You could use a tagged union, but that
> > > is much more common in C than in C++.
> >
> > I've got a lot of work so I haven't looked in detail, but I suspect
> > you can replace everything by a single "node" struct, with a list of
> > children. If the list is empty, it's a sphere, otherwise it's a group.
>
> An excellent idea! ...

The new C++ version is about 10 lines shorter, weighing in at 85 LOC, and ~10% 
slower on x86.

-- 
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
Objective CAML for Scientists
http://www.ffconsultancy.com/products/ocaml_for_scientists

--Boundary-00=_7VQdCIbHtM9e9H1
Content-Type: text/x-c++src;
  charset="iso-8859-1";
  name="ray3.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="ray3.cpp"

#include <vector>
#include <iostream>
#include <limits>
#include <cmath>
using namespace std;
numeric_limits<double> dbl;
double delta = sqrt(dbl.epsilon()), infinity = dbl.infinity(), pi = M_PI;
struct Vec { // 3D vector
  double x, y, z;
  Vec(double x2, double y2, double z2) : x(x2), y(y2), z(z2) {}
};
Vec operator+(Vec a, Vec b) { return Vec(a.x + b.x, a.y + b.y, a.z + b.z); }
Vec operator-(Vec a, Vec b) { return Vec(a.x - b.x, a.y - b.y, a.z - b.z); }
Vec operator*(double a, Vec b) { return Vec(a * b.x, a * b.y, a * b.z); }
double dot(Vec a, Vec b) { return a.x*b.x + a.y*b.y + a.z*b.z; }
Vec unitise(Vec a) { return (1 / sqrt(dot(a, a))) * a; }
struct Ray { Vec orig, dir; Ray(Vec o, Vec d) : orig(o), dir(d) {} };
struct Tree {
  vector<Tree> child;
  Vec center;
  double radius;
  Tree(Vec c, double r) : child(), center(c), radius(r) {}
};
double ray_sphere(const Ray ray, const Tree &tree) {
  Vec v = tree.center - ray.orig;
  double b = dot(v, ray.dir),
    disc = b*b - dot(v, v) + tree.radius * tree.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);
}
void intersect(double &lambda, Vec &normal, const Ray ray, const Tree &tree) {
  double l = ray_sphere(ray, tree);
  if (l >= lambda) return;
  if (tree.child.size() == 0) {
    lambda = l;
    normal = unitise(ray.orig + l * ray.dir - tree.center);
  } else
    for (vector<Tree>::const_iterator it=tree.child.begin();
	 it!=tree.child.end(); ++it)
      intersect(lambda, normal, ray, *it);
}
double ray_trace(const double weight, const Vec light, const Ray ray,
		 const Tree &tree) {
  double lambda = infinity;
  Vec normal(0, 0, 0);
  intersect(lambda, normal, ray, tree);
  if (lambda == infinity) return 0;
  Vec o = ray.orig + lambda * ray.dir + delta * normal;
  double g = -dot(normal, light), l = infinity;
  if (g <= 0) return 0.;
  intersect(l, normal, Ray(o, Vec(0, 0, 0) - light), tree);
  return (l == infinity ? g : 0);
}
Tree create(int level, double r, double x, double y, double z) {
  if (level == 1) return Tree(Vec(x, y, z), r);
  Tree group = Tree(Vec(x, y, z), 3*r);
  group.child.push_back(Tree(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;
}
int main(int argc, char *argv[]) {
  int level = (argc==2 ? atoi(argv[1]) : 6), w = 512, h = 512, ss = 4;
  Tree tree = create(level, 1, 0, -1, 0);
  cout << "P2\n" << w << " " << h << "\n255\n";
  for (int y=h-1; y>=0; --y) {
    for (int x=0; x<w; ++x) {
      double g=0;
      for (int dx=0; dx<ss; ++dx)
	for (int dy=0; dy<ss; ++dy) {
	  Vec d(x+double(dx)/ss-w/2, y+double(dy)/ss-h/2, max(w, h));
	  g += ray_trace(1, unitise(Vec(-1, -3, 2)), Ray(Vec(0, 0, -4),
							 unitise(d)), tree);
	}
      cout << min(255, int(256. * g / (ss*ss))) << " ";
    }
    cout << endl;
  }
  return 0;
}

--Boundary-00=_7VQdCIbHtM9e9H1--