jsfarm tutorial


What's the simplest possible scene?
An empty expanse, filled with nothingness. Let's define it.
(nothing)
Note that nothing is an entirely valid scene object; it has no volume and never reflects any rays. It is used as the default state for things like groups.
 | 

Well that looks boring.
Okay, let's add a plane.
(plane normal base)
The plane object takes two parameters: a normal and a base. The base is simply any point that lies on the plane; the normal is a vector that points away from the plane. Note that "plane" is a misnomer; it's actually a halfspace. In this case, the plane has a normal of "positive Y" and is located at "<0 -1 0>".
 | 

It still looks boring! I do not understand!
We added a plane, but the plane is not illuminated by anything. We need to add a light source to the scene. As opposed to a traditional raytracer, a light source is any object that emits light of its own. It does not have to be a point source. We'll use a downwards-facing white plane as our sky. So we want to add a second object. This means we have to define a group. When we add our second plane, we have to make sure it emits light using the shine modifier. We will also use color to set the reflective color to black, to make sure we only emit light.
(group ...)
Groups combine multiple objects into one! For every point, the visible object is simply the one that's closest to the camera.
(color rgb object)
(shine rgb object)
The shine function changes the emitted color of an object. The color function changes the diffuse color of an object.
 | 

Hm, we've defined two planes but only one of them is visible.
That's correct. Only one of them emits light.
But! I wanted one of them to be illuminated by the other!
Indeed. What we require is indirect illumination. So we're gonna take our existing scene and wrap it in the pathtrace function.
(pathtrace num-bounces scene)
pathtrace computes indirect illumination. Whenever a ray hits an object, it casts a random sampling ray onwards, to estimate the light that the object receives. This randomness is the main cause of noisy images in path tracing.
(fuzz object)
fuzz slightly jiggles the direction of the ray being traced. The result is anti-aliasing "for free".
 | 

Somehow I expected more.
The default material of the floor is a perfect diffuse reflector. The ceiling is white; consequentially, the floor is also white. Let's make this scene more interesting by putting a sphere on the ground. Note that the origin is at <0 0 0>; to be visible, an object has to be somewhere in +Z.
(sphere center radius)
This one should be self-explanatory.
 | 
Huh. Shouldn't it be uniform white again? The sphere is also a perfect diffuse reflector.
Indeed. Well observed! However, since the sphere touches the plane, it's easy for a ray to get "stuck" underneath it, especially since we only set ten bounces. Try to increase the number of bounces to 100.

This scene still looks boring.
What, one uniformly white sphere not good enough for you?? Fine, whatever, have a box.
I just-
Have ten boxes!! And ten cylinders!!!
 | 
(rand)
Returns a random number between 0 and 1.
(cylinder from to radius)
A capped cylinder between from and to with radius radius.
(for/group variable from to body)
A for-loop, where every pass through the loop (for variable from from to to - 1) is expected to produce one object. The objects are combined into a group.
(box from to)
An axis-aligned box with one corner at from and the other at to.
Okay, okay, you can make boxes and cylinders. I'm very impressed with your boxes and cylinders.
You better be.
Out of interest, how many boxes can you make per scene?
32509.
That's an oddly specific number.
Six planes for each side of the box, makes 408 bytes. Five intersections for the planes, 540 bytes. And one bounding box, 68 bytes. In sum, 1016 bytes. There's 32MB allocated per thread. Minus 512KB reserved for the stack, and divided by 1016, that's 32509 boxes. Honestly, 1016 bytes is kind of depressing. Maybe you can rewrite the raytracer to be more efficient. :)
Rewrite the raytracer?
Try and click on the tabs - you have complete access to the raytracer's source right here in your browser. You can change it in any fashion you want.

Hm. This looks cool but maybe it'd look even cooler if I could see it from above and the left.
Why I have just the thing for that!
(camera position lookat scene)
Normally, there is an implicit camera positioned at <0 0 0> and looking towards +Z, with "up" being +Y and "right" being +X. Using the camera function will transform the scene to position the camera at position and looking towards lookat.
 | 

This is neat, but the sphere in the middle is in the way of my view. Can we sink it into the ground? Like, bore a hole right down the middle and put it in there?

Now we get into the realm of "Constructive Solid Geometry" (CSG).

This might get a bit complicated. We highly recommend that you go off and read that Wikipedia page if you are not yet familiar with CSG.

Fundamentally speaking, CSG is just the application of set theory to solid bodies. In other words, what we have considered a plane is in reality a half-space, a dividing surface above which is air and below which is solidity. Similarly, a sphere is just the set of all points within its radius.

It is important to understand that there is a difference between a sphere and an "anti-sphere" (a negated sphere) - a sphere is an object surrounded by a lot of nothing, an anti-sphere is a bubble of nothing, surrounded by a lot of stuff.

This will require an example. To illustrate, let's take the intersection between our sphere and a box.

(intersect objects...)
The intersect object is an object formed by the intersection of its parameters. At each point on its surface, the innermost object is visible.
 | 

I see. So the combination of box and sphere means that the corners of our box have been filed off.

... Wait. I don't see.

It's straightforward. Fundamentally, an intersect object is comprised of only those parts of either object that are inside the other object.

The edge length of the box is a bit larger than the diameter of the sphere. So towards the sides of the box, where it was smaller than the sphere, only the box is visible, since it's inside of the sphere. However, since the sphere is round, it's smaller at the corners than the box, so at those spots it's inside of the box. So only the sphere is visible.

Now, how can we use this to cut out a hole in the ground?

Simple: we form the intersect between the floor and a negated cylinder.

With a normal cylinder, we'd get all the parts of the floor that are inside the cylinder; with a negated one, we'll get an object comprised of all the parts of the floor that are outside the cylinder.

Essentially, the cylinder will be missing from the floor.

Also, we lower our central sphere-box-thing into the hole we just created, neatly hiding it from view.

(negate object)
The negate object is identical to its parameter object, except that the parameter object's inside becomes its outside and its inside becomes its outside. It essentially turns the object inside-out.
 | 

I think I get the idea.
Cheers! Now go forth and render pretty pictures.
[light]  [dark]