Skip to content

Instantly share code, notes, and snippets.

# chriswhocodes/JavaCard.java Last active Dec 25, 2015

Andrew Kensler's business card raytracer converted to Java
 package com.chrisnewland.raytrace; import java.io.FileOutputStream; /* * Standing on the shoulders of giants. * I didn't invent this raytracer, just converted it from C to Java with the help of this web page by Fabien Sanglard: * http://fabiensanglard.net/rayTracing_back_of_business_card/index.php * The original code is by Andrew Kensler. Comments copied from Fabien Sanglard's notes. */ public class JavaCard { // Define a vector class class V3f { float x, y, z; // Vector has three float attributes. // Empty constructor V3f() { } // Constructor V3f(float a, float b, float c) { x = a; y = b; z = c; } // Vector add V3f add(V3f r) { return new V3f(x + r.x, y + r.y, z + r.z); } // Vector scaling V3f scale(float r) { return new V3f(x * r, y * r, z * r); } // Vector dot product float dot(V3f r) { return x * r.x + y * r.y + z * r.z; } // Cross-product V3f cross(V3f r) { return new V3f(y * r.z - z * r.y, z * r.x - x * r.z, x * r.y - y * r.x); } // Used later for normalizing the vector V3f normalise() { float factor = (float) (1f / (float) Math.sqrt((float) dot(this))); return scale(factor); } public String toString() { return x + " " + y + " " + z; } }; int G[] = { 247570, 280596, 280600, 249748, 18578, 18577, 231184, 16, 16 }; // Random generator, return a float within range [0-1] float randomFloat() { return (float) Math.random(); } // The intersection test for line [o,v]. // Return 2 if a hit was found (and also return distance t and bouncing ray // n). // Return 0 if no hit was found but ray goes upward // Return 1 if no hit was found but ray goes downward // Returns object[] 0 = int (m), 1 = float (t), 2 = Vector3f n Object[] test(V3f o, V3f d, V3f n) { float t = 1e9f; int m = 0; float p2 = -o.z / d.z; if (.01 < p2) { t = p2; n = new V3f(0, 0, 1); m = 1; } for (int k = 18; k >= 0; k--) { for (int j = 8; j >= 0; j--) { if ((G[j] & (1 << k)) > 0) { // There is a sphere but does the ray hit it ? V3f p = o.add(new V3f(-k, 0, -j - 4)); float b = p.dot(d); float c = p.dot(p) - 1; float q = b * b - c; // Does the ray hit the sphere ? if (q > 0) { float s = -b - (float) Math.sqrt(q); if (s < t && s > .01) { // So far this is the minimum distance, save // it. And // also // compute the bouncing ray // vector into 'n' t = s; n = (p.add(d.scale(t))).normalise(); m = 2; } } } } } return new Object[] { m, t, n }; } // sample the world and return the pixel color for // a ray passing by point o (Origin) and d (Direction) V3f sample(V3f origin, V3f direction) { V3f n = new V3f(); // Search for an intersection ray Vs World. Object[] result = test(origin, direction, n); int m = (int) result; float t = (float) result; n = (V3f) result; if (m == 0) { // No sphere found and the ray goes upward: Generate a sky color return new V3f(.7f, .6f, 1f).scale((float) Math.pow(1 - direction.z, 4)); } // A sphere was maybe hit. // h = intersection coordinate V3f h = origin.add(direction.scale(t)); // 'l' = direction to light (with random delta for soft-shadows). V3f l = new V3f(9 + randomFloat(), 9 + randomFloat(), 16); l = l.add(h.scale(-1)); l = l.normalise(); // r = The half-vector V3f r = direction.add(n.scale(n.dot(direction.scale(-2f)))); // Calculated the lambertian factor float b = l.dot(n); // Calculate illumination factor (lambertian coefficient > 0 or in // shadow)? if (b < 0) { b = 0; } else { result = test(h, l, n); int res = (int) result; t = (float) result; n = (V3f) result; if (res > 0) { b = 0; } } // Calculate the color 'p' with diffuse and specular component V3f rdash = r.scale(b > 0 ? 1 : 0); float p = (float) Math.pow(l.dot(rdash), 99); if ((m & 1) == 1) { // No sphere was hit and the ray was going downward: h = h.scale(0.2f); // Generate a floor color int ceil = (int) (Math.ceil(h.x) + Math.ceil(h.y)); if ((ceil & 1) == 1) { return new V3f(3, 1, 1).scale(b * .2f + .1f); } else { return new V3f(3, 3, 3).scale(b * .2f + .1f); } } // m == 2 A sphere was hit. // Cast an ray bouncing from the sphere surface. // Attenuate color by 50% since it is bouncing (* .5) return new V3f(p, p, p).add(sample(h, r).scale(0.5f)); } public JavaCard() throws Exception { // Camera direction V3f g = new V3f(-6, -16, 0).normalise(); // Camera up vector...Seem Z is pointing up :/ WTF ! V3f a = new V3f(0, 0, 1).cross(g).normalise().scale(.002f); // The right vector, obtained via traditional cross-product V3f b = g.cross(a).normalise().scale(.002f); // WTF ? See https://news.ycombinator.com/item?id=6425965 for more. V3f c = a.add(b).scale(-256).add(g); FileOutputStream fos = new FileOutputStream("java.ppm"); fos.write(new String("P6 512 512 255 ").getBytes()); for (int y = 511; y >= 0; y--) { // For each column for (int x = 511; x >= 0; x--) { // For each pixel in a line // Reuse the vector class to store not XYZ but a RGB pixel color // Default pixel color is almost pitch black V3f p = new V3f(13, 13, 13); // Cast 64 rays per pixel (For blur (stochastic sampling) and // soft-shadows. for (int r = 63; r >= 0; r--) { // The delta to apply to the origin of the view (For Depth // of View blur). // v t = a * (R() - .5) * 99 + b * (R() - .5) * 99; // A little bit of delta up/down and left/right V3f t = a.scale(randomFloat() - 0.5f); t = t.scale(99); V3f t2 = b.scale(randomFloat() - 0.5f); t2 = t2.scale(99); t = t.add(t2); // Set the camera focal point v(17,16,8) and Cast the ray // Accumulate the color returned in the p variable // Ray Direction with random deltas for stochastic sampling V3f dirA = a.scale(randomFloat() + x); V3f dirB = b.scale(randomFloat() + y); V3f dirC = dirA.add(dirB).add(c); V3f dir = t.scale(-1f).add(dirC.scale(16f)).normalise(); // Ray Origin +p for color accumulation p = sample(new V3f(17f, 16f, 8f).add(t), dir).scale(3.5f).add(p); } fos.write((int) p.x); fos.write((int) p.y); fos.write((int) p.z); } } fos.close(); } public static void main(String[] args) throws Exception { new JavaCard(); } }
Owner Author

### chriswhocodes commented Oct 16, 2013

 Looks like Andrew Kensler's raytracer was inspired by one from Paul Heckbert http://www.cs.cmu.edu/~ph/
Owner Author

### chriswhocodes commented Oct 17, 2013

 NB - This is my first attempt at conversion. Completely unoptimised and runs about 3x slower than the -O3 C version. Generates way too many new V3f objects so GC pauses may explain the slowness.
to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
{"ft_lang_label":"__label__en","ft_lang_prob":0.5745836,"math_prob":0.9967606,"size":6693,"snap":"2019-35-2019-39","text_gpt3_token_len":2374,"char_repetition_ratio":0.13634326,"word_repetition_ratio":0.024671054,"special_character_ratio":0.41745105,"punctuation_ratio":0.2100551,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9983945,"pos_list":[0,1,2],"im_url_duplicate_count":[null,null,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2019-08-25T20:55:17Z\",\"WARC-Record-ID\":\"<urn:uuid:a6b359eb-6eed-4645-a196-156b1509500e>\",\"Content-Length\":\"139808\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:ae9b7537-2c4a-4d4c-8828-8dcf58219726>\",\"WARC-Concurrent-To\":\"<urn:uuid:5df28f91-353a-4f4d-a493-dd59d3f99eb8>\",\"WARC-IP-Address\":\"192.30.253.118\",\"WARC-Target-URI\":\"https://gist.github.com/chriswhocodes/7009568\",\"WARC-Payload-Digest\":\"sha1:4H4FHBAPB77ZIOAX2UO6QG2W4HZ4IV3N\",\"WARC-Block-Digest\":\"sha1:XAEGXQE4WTHWSM4N7I4CJZV3NN4P2MBZ\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2019/CC-MAIN-2019-35/CC-MAIN-2019-35_segments_1566027330800.17_warc_CC-MAIN-20190825194252-20190825220252-00487.warc.gz\"}"}