Skip to content

Instantly share code, notes, and snippets.

@glaforge
Created April 10, 2012 12:51
Show Gist options
  • Save glaforge/2351150 to your computer and use it in GitHub Desktop.
Save glaforge/2351150 to your computer and use it in GitHub Desktop.
Ray tracer example using @CompileStatic
// translated from http://www.ffconsultancy.com/languages/ray_tracer/code/1/ray.java
// by Derek Young - Sep 6, 07
import groovy.transform.CompileStatic
@CompileStatic
class Vec {
public double x, y, z;
public Vec(double x2, double y2, double z2) { x = x2; y = y2; z = z2; }
static Vec add(Vec a, Vec b) { return new Vec(a.x + b.x, a.y + b.y, a.z + b.z); }
static Vec sub(Vec a, Vec b) { return new Vec(a.x - b.x, a.y - b.y, a.z - b.z); }
static Vec scale(double s, Vec a) { return new Vec(s * a.x, s * a.y, s * a.z); }
static double dot(Vec a, Vec b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
static Vec unitise(Vec a) { return scale(1 / Math.sqrt(Vec.dot(a, a)), a); }
}
@CompileStatic
class Ray {
public Vec orig, dir;
public Ray(Vec o, Vec d) { orig = o; dir = d; }
}
@CompileStatic
class Hit {
public double lambda;
public Vec normal;
public Hit(double l, Vec n) { lambda = l; normal = n; }
}
@CompileStatic
abstract class Scene {
abstract public Hit intersect(Hit i, Ray ray);
}
@CompileStatic
class Sphere extends Scene {
static double infinity = Float.POSITIVE_INFINITY;
public Vec center;
public double radius;
public Sphere(Vec c, double r) { center = c; radius = r; }
public double ray_sphere(Ray ray) {
Vec v = Vec.sub(center, ray.orig);
double b = Vec.dot(v, ray.dir),
disc = b * b - Vec.dot(v, v) + radius * radius;
if (disc < 0) return infinity;
double d = Math.sqrt(disc), t2 = b + d;
if (t2 < 0) return infinity;
double t1 = b - d;
return (t1 > 0 ? t1 : t2);
}
public Hit intersect(Hit i, Ray ray) {
double l = ray_sphere(ray);
if (l >= i.lambda) return i;
Vec n = Vec.add(ray.orig, Vec.sub(Vec.scale(l, ray.dir), center));
return new Hit(l, Vec.unitise(n));
}
}
@CompileStatic
class Group extends Scene {
public Sphere bound;
public ArrayList objs;
public Group(Sphere b) {
bound = b;
objs = new ArrayList();
}
public Hit intersect(Hit i, Ray ray) {
double l = bound.ray_sphere(ray);
if (l >= i.lambda) return i;
ListIterator it = objs.listIterator(0);
while (it.hasNext()) {
Scene scene = (Scene) it.next();
i = scene.intersect(i, ray);
}
return i;
}
}
@CompileStatic
class rayMain {
double delta = Math.sqrt(Math.ulp(1.0d)), infinity = Float.POSITIVE_INFINITY;
double ray_trace(Vec light, Ray ray, Scene scene) {
Hit i = scene.intersect(new Hit(infinity, new Vec(0, 0, 0)), ray);
if (i.lambda == infinity) return 0;
Vec o = Vec.add(ray.orig, Vec.add(Vec.scale(i.lambda, ray.dir),
Vec.scale(delta, i.normal)));
double g = Vec.dot(i.normal, light);
if (g >= 0) return 0;
Ray sray = new Ray(o, Vec.scale(-1, light));
Hit si = scene.intersect(new Hit(infinity, new Vec(0, 0, 0)), sray);
return (si.lambda == infinity ? -g : 0);
}
Scene create(int level, Vec c, double r) {
Sphere sphere = new Sphere(c, r);
if (level == 1) return sphere;
Group group = new Group(new Sphere(c, 3 * r));
group.objs.add(sphere);
double rn = 3 * r / Math.sqrt(12);
for (int dz in [-1, 1]) {
for (int dx in [-1, 1]) {
//for (int dx=-1; dx<=1; dx+=2) {
Vec c2 = new Vec(c.x + dx * rn, c.y + rn, c.z + dz * rn);
group.objs.add(create(level - 1, c2, r / 2));
}
}
return group;
}
void run(int n, int level, int ss) {
Scene scene = create(level, new Vec(0, -1, 0), 1);
FileOutputStream out = new FileOutputStream("groovyimage.pgm");
out.write(("P5\n" + n + " " + n + "\n255\n").getBytes());
for (int yi = 0; yi < n; yi++) {
int y = (n - 1) - yi
for (int x = 0; x < n; x++) {
//print('.');
double g = 0;
for (int dx = 0; dx < ss; dx++) {
for (int dy = 0; dy < ss; dy++) {
Vec d = new Vec(x + dx / ss - n / 2, y + dy / ss - n / 2, (double)n);
Ray ray = new Ray(new Vec(0, 0, -4), Vec.unitise(d));
g += ray_trace(Vec.unitise(new Vec(-1, -3, 2)),
ray, scene);
}
}
out.write((int) (0.5d + 255 * g / (ss * ss)));
}
}
out.close();
}
}
long start = System.currentTimeMillis();
(new rayMain()).run(Integer.parseInt(args[1]), Integer.parseInt(args[0]), 4);
long end = System.currentTimeMillis();
System.out.println(end - start);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment