Sphere s, light; ICamera c; PImage render; void setup() { size(700, 700, P3D); colorMode(RGB); s = new Sphere(100, 0, 0, 0, 0.95, 0); light = new Sphere(15, 0, 80, 0, 1, 255); c = new ICamera(-200, 0, 0, s, 17.5, 3, 15, 17500, 3000); } void draw() { background(0); PVector cam = pointFromAng(new PVector(mouseX/100.0, mouseY/100.0), s.getRadius()+350.0); camera(cam.x, cam.y, cam.z, 0, 0, 0, 0, 0, -1); light.show(); s.show(); c.show(s, light); strokeWeight(3); stroke(255, 0, 0); line(0, 0, 0, 50, 0, 0);//x axis stroke(0, 255, 0); line(0, 0, 0, 0, 50, 0);//y axis stroke(0, 0, 255); line(0, 0, 0, 0, 0, 50);//z axis } void mouseClicked() { println("Rendering......................."); int time = millis(); render = c.render(s, light); render.save("render.png"); time = millis() - time; println("Render Time: " + (int)(time/60000.0) + " minutes, " + (((int)(time/1000.0))%60) + " seconds, " + (time%1000) + " millis"); exit(); } void sphereAt(PVector pos, float r) { pushMatrix(); translate(pos.x, pos.y, pos.z); sphere(r); popMatrix(); } void lineFromTo(PVector a, PVector b) { line(a.x, a.y, a.z, b.x, b.y, b.z); } PVector angleOf(PVector p) { return new PVector(atan2(p.y, p.x), atan2(p.z, pythag(p.x, p.y))); } PVector angleFrom(PVector a, PVector b) { return angleOf(PVector.sub(b, a)); } PVector pointFromAng(PVector a, float r) { float xyrad = r*cos(a.y); return new PVector(xyrad*cos(a.x), xyrad*sin(a.x), r*sin(a.y)); } float pythag(float a, float b, float c) { return sqrt(sqr(a)+sqr(b)+sqr(c)); } float pythag(float a, float b) { return sqrt(sqr(a)+sqr(b)); } float sqr(float x) {return x*x;} class Ray { int maxBounces; PVector pos, dir; Ray(float x, float y, float z, float dx, float dy, float dz, int b) { pos = new PVector(x, y, z); dir = new PVector(dx, dy, dz); maxBounces = b; } Ray(PVector p, PVector d, int b) { pos = p.copy(); dir = PVector.sub(d, p); maxBounces = b; } PVector getPos() {return pos;} color trace(Sphere s, Sphere light, boolean show) { for(int i = 1; i<=maxBounces; i++) { PVector[] points = intersect(light, false); if(points != null) { float dist0 = sqrt(sq(points[0].x-pos.x) + sq(points[0].y-pos.y) + sq(points[0].z-pos.z)); float dist1 = sqrt(sq(points[1].x-pos.x) + sq(points[1].y-pos.y) + sq(points[1].z-pos.z)); int nearest = 1; if(dist1 > dist0) {nearest = 0;} PVector bouncePoint = points[nearest]; if(show) { stroke(100); lineFromTo(pos, bouncePoint); } float finalBrightness = light.getBrightness() * pow(s.getTransmission(), i); return color(finalBrightness/4, finalBrightness, finalBrightness, 255); } boolean result = bounce(s, 255, show); if(!result) { return color(0, 0, 0, 0); } } return color(0, 0, 0, 255); } boolean bounce(Sphere s, int shade, boolean show) { PVector[] points = intersect(s, false); if(points == null) { return false; } float dist0 = sqrt(sq(points[0].x-pos.x) + sq(points[0].y-pos.y) + sq(points[0].z-pos.z)); float dist1 = sqrt(sq(points[1].x-pos.x) + sq(points[1].y-pos.y) + sq(points[1].z-pos.z)); int farthest = 0; if(dist0 == 0) {farthest = 1;} else if(dist1 > dist0) {farthest = 1;} PVector bouncePoint = points[farthest]; //println(bouncePoint); if(show) { stroke(shade); strokeWeight(3); lineFromTo(pos, bouncePoint); } PVector bounceNormal = PVector.sub(s.getPos(), bouncePoint); bounceNormal = PVector.div(bounceNormal, bounceNormal.mag());//normalizes it PVector bounceAngle = PVector.sub(dir, PVector.mult(bounceNormal, 2*PVector.dot(dir, bounceNormal))); pos.set(bouncePoint); dir.set(bounceAngle); return true; } PVector[] intersect(Sphere s, Boolean show) { PVector p1 = pos; PVector p2 = PVector.add(pos, dir); //p2 = PVector.add(p1, p2); PVector p3 = s.getPos(); float r = s.getRadius(); float a = sq(p2.x-p1.x) + sq(p2.y-p1.y) + sq(p2.z-p1.z); float b = 2*((p2.x-p1.x)*(p1.x-p3.x) + (p2.y-p1.y)*(p1.y-p3.y) + (p2.z-p1.z)*(p1.z-p3.z)); float c = sq(p1.x) + sq(p1.y) + sq(p1.z) + sq(p3.x) + sq(p3.y) + sq(p3.z) - 2*(p1.x*p3.x + p1.y*p3.y + p1.z*p3.z) - sq(r); float disc = sq(b) - 4*a*c; if(disc < 0) { return null; } else { float u1 = (b*-1 + sqrt(disc)) / (2*a); float u2 = (b*-1 - sqrt(disc)) / (2*a); PVector[] points = new PVector[2]; points[0] = new PVector(p1.x + u1*(p2.x - p1.x), p1.y + u1*(p2.y - p1.y), p1.z + u1*(p2.z - p1.z)); points[1] = new PVector(p1.x + u2*(p2.x - p1.x), p1.y + u2*(p2.y - p1.y), p1.z + u2*(p2.z - p1.z)); if(show) { stroke(255); fill(255); sphereAt(points[0], 3); sphereAt(points[1], 3); } return points; } } void showOrigin(float shade) { stroke(shade); fill(shade); sphereAt(pos, 5); sphereAt(PVector.add(pos, dir), 5); } int sgn(float x) { if(x<0) {return -1;} else {return 1;} } } class Sphere { private float radius; private PVector pos; private float transmission; // 0-1 private float brightness; Sphere(float r, float x, float y, float z, float trans, float bri) { radius = r; pos = new PVector(x, y, z); transmission = trans; brightness = bri; } float getTransmission() {return transmission;} float getBrightness() {return brightness;} float getRadius() {return radius;} PVector getPos() {return pos;} void show() { noFill(); stroke(255); strokeWeight(1); sphereAt(pos, radius); } } class ICamera { PVector pos, sensor, resolution, rot; Sphere target; PVector tl, tr, bl, br; ICamera(float x, float y, float z, Sphere t, float sx, float sy, float fd, int rx, int ry) { pos = new PVector(x, y, z); sensor = new PVector(sx, sy, fd); resolution = new PVector(rx, ry); target = t; } void calculateCorners() { updateRot(); PVector a = rot; float sr = pythag(sensor.z, sensor.y/2); float cornerPitch = atan2(sensor.y/2, sensor.z); PVector right = new PVector(pos.x + (sensor.x/2)*cos(a.x + HALF_PI), pos.y + (sensor.x/2)*sin(a.x + HALF_PI), pos.z); PVector left = new PVector(pos.x - (sensor.x/2)*cos(a.x + HALF_PI), pos.y - (sensor.x/2)*sin(a.x + HALF_PI), pos.z); fill(255, 0, 0); tl = new PVector(left.x + sr*cos(a.y + cornerPitch)*cos(a.x), left.y + sr*cos(a.y + cornerPitch)*sin(a.x), left.z + sr*sin(a.y + cornerPitch)); bl = new PVector(left.x + sr*cos(a.y - cornerPitch)*cos(a.x), left.y + sr*cos(a.y - cornerPitch)*sin(a.x), left.z + sr*sin(a.y - cornerPitch)); tr = new PVector(right.x + sr*cos(a.y + cornerPitch)*cos(a.x), right.y + sr*cos(a.y + cornerPitch)*sin(a.x), right.z + sr*sin(a.y + cornerPitch)); br = new PVector(right.x + sr*cos(a.y - cornerPitch)*cos(a.x), right.y + sr*cos(a.y - cornerPitch)*sin(a.x), right.z + sr*sin(a.y - cornerPitch)); } void updateRot() { PVector sensorCenter = PVector.sub(target.getPos(), pos); rot = angleOf(sensorCenter); } PImage render(Sphere s, Sphere light) { calculateCorners(); PImage image = createImage((int)resolution.x, (int)resolution.y, ARGB); image.loadPixels(); for(int j = 0; j