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)))); }