public static Vec radiance(Ray r, int depth, int[] Xi) { double t[] = {0.0}; // distance to intersection int id[] = {0}; // id of intersected object if (!intersect(r, t, id)) return new Vec(); // if miss, return black Sphere obj = spheres[id[0]]; // the hit object Vec x = r.o.add(r.d.multiply(t[0])); Vec n = (x.substract(obj.p)).norm(); Vec nl = n.dot(r.d) < 0 ? n : n.multiply(-1); Vec f = obj.c; double p = f.x > f.y && f.x > f.z ? f.x : f.y > f.z ? f.y : f.z; // max refl if (++depth > 5) if (erand48(Xi) < p) f = f.multiply(1 / p); else return obj.e; // R.R. if (obj.refl == Refl_t.DIFF) { // Ideal DIFFUSE reflection double r1 = 2 * Math.PI * erand48(Xi), r2 = erand48(Xi), r2s = Math.sqrt(r2); Vec w = nl; Vec u = ((Math.abs(w.x) > .1 ? new Vec(0, 1) : new Vec(1)).modulo(w)).norm(); Vec v = w.modulo(u); Vec d = (u.multiply(Math.cos(r1)) .multiply(r2s) .add(v.multiply(Math.sin(r1)).multiply(r2s)) .add(w.multiply(Math.sqrt(1 - r2)))) .norm(); return obj.e.add(f.mult(radiance(new Ray(x, d), depth, Xi))); } else if (obj.refl == Refl_t.SPEC) { // Ideal SPECULAR reflection return obj.e.add( f.mult(radiance(new Ray(x, r.d.substract(n.multiply(2 * n.dot(r.d)))), depth, Xi))); } Ray reflRay = new Ray(x, r.d.substract(n.multiply(2 * n.dot(r.d)))); // Ideal dielectric REFRACTION boolean into = n.dot(nl) > 0; // Ray from outside going in? double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.dot(nl), cos2t; if ((cos2t = 1 - nnt * nnt * (1 - ddn * ddn)) < 0) // Total internal reflection return obj.e.add(f.mult(radiance(reflRay, depth, Xi))); Vec tdir = (r.d.multiply(nnt).substract(n.multiply((into ? 1 : -1) * (ddn * nnt + Math.sqrt(cos2t))))) .norm(); double a = nt - nc, b = nt + nc, R0 = a * a / (b * b), c = 1 - (into ? -ddn : tdir.dot(n)); double Re = R0 + (1 - R0) * c * c * c * c * c, Tr = 1 - Re, P = .25 + .5 * Re, RP = Re / P, TP = Tr / (1 - P); return obj.e.add( f.mult( depth > 2 ? (erand48(Xi) < P ? // Russian roulette radiance(reflRay, depth, Xi).multiply(RP) : radiance(new Ray(x, tdir), depth, Xi).multiply(TP)) : radiance(reflRay, depth, Xi) .multiply(Re) .add(radiance(new Ray(x, tdir), depth, Xi).multiply(Tr)))); }
public static void main(String[] args) { final int samps = args.length == 1 ? Integer.parseInt(args[0]) / 4 : 1; // # samples final Ray cam = new Ray(new Vec(50, 52, 295.6), new Vec(0, -0.042612, -1).norm()); // cam pos, dir final Vec cx = new Vec(width * .5135 / height); final Vec cy = (cx.modulo(cam.d)).norm().multiply(.5135); for (int p = 0; p < scene.length; p++) { scene[p] = new Vec(); } for (int y = 0; y < height; y++) { // Loop over image rows final int cury = y; Thread t = new Thread( jtg, new Runnable() { public void run() { int Xi[] = {0, 0, cury * cury * cury}; for (int x = 0; x < width; x++) { // Loop cols for (int sy = 0, i = (height - cury - 1) * width + x; sy < 2; sy++) { // 2x2 subpixel rows Vec r = new Vec(); for (int sx = 0; sx < 2; sx++, r = new Vec()) { // 2x2 subpixel cols for (int s = 0; s < samps; s++) { double r1 = 2 * erand48(Xi), dx = r1 < 1 ? Math.sqrt(r1) - 1 : 1 - Math.sqrt(2 - r1); double r2 = 2 * erand48(Xi), dy = r2 < 1 ? Math.sqrt(r2) - 1 : 1 - Math.sqrt(2 - r2); Vec d = cx.multiply(((sx + .5 + dx) / 2 + x) / width - .5) .add( cy.multiply(((sy + .5 + dy) / 2 + cury) / height - .5) .add(cam.d)); r = r.add( radiance(new Ray(cam.o.add(d.multiply(140)), d.norm()), 0, Xi) .multiply(1. / samps)); } // Camera rays are pushed ^^^^^ forward to start in interior acquireScene()[i] = acquireScene()[i].add( new Vec(clamp(r.x), clamp(r.y), clamp(r.z)).multiply(.25)); } } } synchronized (signal) { System.out.println( String.format( "\rRendered %5.2f%% | %d threads on duty | %d rows remaining", 100. * progress++ / (height), threadCount--, height - progress)); signal .notify(); // let the waitee know that we have finished to eventually spawn // another thread } } }); t.start(); synchronized (signal) { /* * Make sure that we are not spawning more threads than * available processors unless we will generate unnecessary overhead. */ if (++threadCount >= Runtime.getRuntime().availableProcessors()) { try { signal.wait(); } catch (InterruptedException iex) { // don't care } } } } // wait for all threads to get the job done! jtg.join(); File f = new File("image.ppm"); try { FileWriter fw = new FileWriter(f); fw.write(String.format("P3\n%d %d\n%d\n", width, height, 255)); for (int i = 0; i < width * height; i++) { fw.write( String.format("%d %d %d ", toInt(scene[i].x), toInt(scene[i].y), toInt(scene[i].z))); } } catch (Exception ex) { } }