Ray tracing with Groovy and Java
July 08, 2015
While I was reading the excellent book by Kevin Suffern "Ray tracing from the ground up", I implemented a ray tracer in Java and wrote a DSL in Groovy, to describe scenes more easily and dynamically.
- migrated the code from C++ to Java
- made the code "more" object oriented
- made the code thread-safe for parallel execution
- wrote a DSL for easy scene creation and manipulation
The following image ...
... is defined by the following DSL:
import net.dinkla.raytracer.colors.RGBColor
import net.dinkla.raytracer.utilities.Resolution
builder.world(id: "World48") {
viewPlane(resolution: Resolution.RESOLUTION_1080, maxDepth: 2)
camera(d: 1250, eye: p(0, 0.1, 10), lookAt: p(0, -1, 0))
ambientLight(color: RGBColor.WHITE, ls: 0.5f)
lights {
pointLight(location: p(0, 5, 0), color: c(1, 1, 1), ls: 1)
}
materials {
phong(id: "grey", ks: 1.0, cd: c(0.1, 0.1, 0.1), ka: 0.5, kd: 1.0, exp: 10)
phong(id: "sky", cd: c(0.1, 0.7, 1.0), ka: 0.75, kd: 1.0)
reflective(id: "white", ks: 0.7, cd: c(1.0, 1.0, 1.0), ka: 0.5, kd: 0.75, exp: 2)
phong(id: "red", ks: 0.9, cd: c(0.9, 0.4, 0.1), ka: 0.5, kd: 0.75, exp: 10)
phong(id: "orange", ks: 0.9, cd: c(0.9, 0.7, 0.1), ka: 0.5, kd: 0.75, exp: 10)
}
objects {
plane(point: p(0,-1.1,0), normal: n(0, 1, 0), material: "white")
sphere(center: p(2.5, 0.5, 0.5), radius: 0.5, material: "orange")
triangle(a: p(-3, 0, -1), b: p(-3, -1, 1), c: p(-1, 0, 1), material: "orange")
smoothTriangle(a: p(-5, 0, -1), b: p(-5, -1, 1), c: p(-3, 0, 1), material: "orange")
ply(file: "resources/TwoTriangles.ply", material: "red")
grid {
triangle(a: p(3, 0, -1), b: p(3, -1, 1), c: p(5, 0, 1), material: "orange")
sphere(center: p(1.5, 1.5, 1.5), radius: 0.5, material: "sky")
}
}
}
There is a white plane, an orange sphere, some triangles, etc. grid
is used to
store objects in a grid-like data structure to speed up look ups.
Behind the scenes the DSL is translated into Java calls.
A grid is used also in the following image:
I also implemented ambient occuclusion:
In this image there are three light sources in red, green and blue and the white ambient light. The DSL is:
package lights.ambient
import net.dinkla.raytracer.math.Point3D
import net.dinkla.raytracer.colors.RGBColor
import net.dinkla.raytracer.math.Normal
import net.dinkla.raytracer.objects.acceleration.Grid
import net.dinkla.raytracer.samplers.Sampler
import net.dinkla.raytracer.samplers.MultiJittered
import net.dinkla.raytracer.utilities.Resolution;
def NUM_AMBIENT_SAMPLES = 64
String path = '/opt/rendering/ply'
Grid bunny = builder.ply(file: "\${path}/bunny/bunny16K.ply", multiplier: 2.0, smooth: true)
def sampler = new Sampler(new MultiJittered(), 2500, 100)
sampler.mapSamplesToHemiSphere(1.0)
builder.world(id: "World54") {
viewPlane(resolution: Resolution.RESOLUTION_1440)
camera(d: 2000, eye: p(0, 1, 10), lookAt: p(0, 0.75, 0), numThreads: 30 )
ambientOccluder(minAmount: RGBColor.WHITE, sampler: sampler, numSamples: NUM_AMBIENT_SAMPLES)
lights {
pointLight(location: p(-5, 5, 0), color: c(1, 0, 0), ls: 1)
pointLight(location: p(5, 5, 0), color: c(0, 0, 1), ls: 1)
pointLight(location: p(5, 5, -15), color: c(0, 1, 0), ls: 1)
}
materials {
phong(id: "yellow", cd: c(1, 1, 0), ka: 0.5, kd: 0.5, ks: 0.25, exp: 4)
matte(id: "gray", cd: c(1), ka: 0.5, kd: 0.5)
phong(id: "orange", cd: c(1, 0.5, 0), ka: 0.5, kd: 0.25, ks: 0.25, exp: 20)
phong(id: "chocolate", cd: c(0.5647, 0.1294, 0), ka: 0.5, kd: 0.25, ks: 0.55, exp: 2)
}
objects {
sphere(material: "yellow", center: p(0, 2, 0), radius: 2)
sphere(material: "orange", center: p(1, 0.75, 4), radius: 0.75)
plane(material: "gray", point: Point3D.ORIGIN, normal: Normal.UP)
instance(object: bunny, material: "chocolate") {
scale(v(5, 5, 5,))
translate(v(-1.5, 0, 6))
}
}
}
Here an "instantiation" with translation and scaling is used.
One of the big advanges of using a Groovy DSL is, that it is interpreted and runtime and so you can change the source and run it again without recompiling or restarting the program.
In the next example you can see the reflections for some geometric objects.
Examples of rendered images
Download
The code is available at GitHub.