Technical Goals
The Lua Scripted Ray Tracer extends the ray tracer completed in part 4 by adding
scriptability via the Lua scripting language.
Lua is a very lightweight language specifically designed for embedding into larger
compiled applications. In many ways, it's a more modern Tcl.
In roughly 1,300 lines of C code, every property available through our working subset of the POV-Ray syntax is available and scriptable via Lua. To facilitate assigning Lua scripts within a .pov file, the following modifiers were added to the
The
The Lua scripts must contain at least one of the following functions:
The parameters to these functions are as follows:
Global variables may be used outside of these two functions to store values common to both functions. Each object in the scene gets it's own, isolated copy of the Lua interpreter, so even if many objects share the same script the values of their global variables may be wildly different. Because of this, special functions must be used to pass and retrieve global variables to and from other objects in the scene. More on that later...
These scripts wouldn't be very interesting if they weren't able to communicate back with the data structures in the C++ ray tracer. The following special global variables are available in your scene:
To get the object type (if the object is a geometric primitive) or the object or
light name (assigned with the
To get or set the properties of an object (for example, the location of a light or
the center and radius of a sphere), you can use the
To get or set the pigment of an object, you can use the
Similarly, you can use
As mentioned above, each object receives its own state of the Lua interpreter, so global variables are essentially sandboxed from each other. If you want objects to react to one another, you need a way of passing values back and forth.
For this, you can use the
Lastly, there are functions for setting the POV-Ray camera location, up, right,
and look at vectors. Since these are usually very painful to use and not intuitive
for animating, it's suggested to use the functions provided in
The following example creates a camera that rotates around the origin with a radius of 10 units, hovering 5 units above the ground, and wobbling back and forth (rotating about it's gaze vector) once every quarter-turn.
Finally, some animation helper functions can be found in
In roughly 1,300 lines of C code, every property available through our working subset of the POV-Ray syntax is available and scriptable via Lua. To facilitate assigning Lua scripts within a .pov file, the following modifiers were added to the
camera
, light_source
, sphere
,
plane
, triangle
, and box
blocks:
name <some arbitrary name> script <my_lua_script.lua>
name
modifier is used to attach an arbitrary string to an object
which can come in handy for identifying a specific object in Lua scripts. The
script
modifier attaches a Lua script to a camera
,
light_source
, or geometric object. Predefined functions within this
Lua source file are then called before each frame is traced.The Lua scripts must contain at least one of the following functions:
function initial(t, l, n) -- do some scripty things end function final(t, l, n) -- do some more scripty things end
- t - Current time in the animation, from 0.0 to 1.0, where 0.0 is the first frame and 1.0 is the last frame.
- l - The number of lights in the scene.
- n - The number of objects in the scene.
initial
function of each camera
, light_source
, and geometric
object, followed by calling the final
function of each camera
,
light_source
, and geometric object. A script need not define both an
initial
and final
function. The two functions are provided
so that scripted objects can react to changes in other scripted objects. For
example, a collection of spheres may update their positions based solely on their
internal velocity in their initial
function, and then check for
collisions with other spheres and update their position accordingly in their
final
function.Global variables may be used outside of these two functions to store values common to both functions. Each object in the scene gets it's own, isolated copy of the Lua interpreter, so even if many objects share the same script the values of their global variables may be wildly different. Because of this, special functions must be used to pass and retrieve global variables to and from other objects in the scene. More on that later...
These scripts wouldn't be very interesting if they weren't able to communicate back with the data structures in the C++ ray tracer. The following special global variables are available in your scene:
-- Unique ID for geometric objects. obj_num -- Unique ID for light_sources. lt_num
name <>
modifier), you can use
the following functions:
-- For geometric objects obj_name_get(obj_num) -- For example, might return "my object". obj_type_get(obj_num) -- For example, might return "triangle". -- For light sources. lt_name_get(lt_num) -- Returns empty string if no name assigned.
obj_props_get()
and
obj_props_set()
functions for objects or lt_pos_get()
and
lt_pos_set()
for lights.
-- Get and set properties of a sphere center_x, center_y, center_z, radius = obj_props_get(obj_num) obj_props_set(obj_num, center_x, center_y, center_z, radius) -- Get and set the position of a light lt_x, lt_y, lt_z = lt_pos_get(lt_num) lt_pos_set(lt_num, lt_x, lt_y, lt_z)
obj_color_get()
and
obj_color_set()
functions and use the lt_color_get()
and
lt_color_set()
for light color.
-- Get and set pigment of an object red, green, blue, filter = obj_color_get(obj_num) obj_color_set(obj_num, red, green, blue, filter) -- Get and set the color of a light red, green, blue = lt_color_get(lt_num) lt_color_set(lt_num, red, green, blue)
obj_finish_get()
, obj_finish_set()
,
obj_xform_get()
, and obj_xform_set()
to get and set the finish
properties of an object and its transformation matrix. Since you are manipulating
the raw matrix, helper functions are available in xform_helper.lua
to generate common matrices for translations, rotations, and scales, as well as
multiply matrices. For vector helper functions, such as length, dot and cross products,
and reflection vectors, see vector_helper.lua
.As mentioned above, each object receives its own state of the Lua interpreter, so global variables are essentially sandboxed from each other. If you want objects to react to one another, you need a way of passing values back and forth.
For this, you can use the
obj_grab_var()
and obj_pass_var()
functions (lt_grab_var()
and lt_pass_var()
for lights).
-- Get the value of other_global_var from object number i, add one to it, and send it back. my_var = obj_grab_var(i, "other_global_var") my_var = my_var + 1 obj_pass_var(i, "other_global_var", my_var)
camera_helper.lua
to generate these vectors from more intuitive ones.The following example creates a camera that rotates around the origin with a radius of 10 units, hovering 5 units above the ground, and wobbling back and forth (rotating about it's gaze vector) once every quarter-turn.
-- Include the camera helper. dofile "camera_helper.lua" function initial(t, l, n) -- Rotate the camera in a circle 10 units away from the origin. local cam_x = math.sin(2 * math.pi * t) * 10 local cam_z = math.cos(2 * math.pi * t) * 10 -- Wobble the camera about the gaze vector 15 degrees either direction once every quarter-turn. local cam_wobble = math.sin(8 * math.pi * t) * 15 local cam = { eye = { x = cam_x, -- Eye location. y = 5, z = cam_z }, look = { x = 0, -- Look location. y = 0, z = 0 }, up = { x = 0, -- Natural world up, NOT CAMERA UP! y = 1, z = 0 }, rot = cam_wobble, -- Rotation about the gaze vector (look - eye) aspect = 1.77777778 } -- Aspect ratio of the frame (width / height) -- Generate POV-Ray camera vectors from more intuitive ones. local location = camera.location(cam) local up = camera.up(cam) local right = camera.right(cam) local look_at = camera.look_at(cam) -- Set the camera vectors. cam_loc_set(location.x, location.y, location.z) cam_up_set(up.x, up.y, up.z) cam_right_set(right.x, right.y, right.z) cam_lookat_set(look_at.x, look_at.y, look_at.z) end
anim_helper.lua
. These include
simple easing functions that can be chained for basic keyframing. They return an interpolated value
during the passed time range, and zero outside the time range, so they can be chained with simple addition.
For example, here's how to animate a value linearly from t = 0.0 to 0.3, hold a value from 0.3 to 0.7, and
use a cubic out easing function from 0.7 to 1.0:
-- Include the animation helper. dofile "anim_helper.lua" function initial(t, l, n) -- Animated value. local my_value = anim.linear(-5, 5, 0.0, 0.3, t) + -- Linearly interpolate from -5 to 5 on the time range 0.0 to 0.3 anim.hold(5, 0.3, 0.7, t) + -- Hold at 5 on the time range 0.3 to 0.7 anim.cubic_out(5, 12, 0.7, 1.01) -- Cubic out easing from 5 to 12 on the time range 0.7 to 1.0 end
Usage Instructions
Usage: raytrace -W<width> -H<height> -I<input file> [-F<frame count> -S<start frame> -R<duration in frames> -D[ambient|diffuse|specular|shadows|reflection|refraction|antialiasing]]
-W1280
- Sets the width of the frame to 1280 pixels.-W720
- Sets the height of the frame to 720 pixels.-Imyscene.pov
- Sets the input file to myscene.pov.-F60
- Renders out 60 frames instead of a single frame. When this option is specified, targa files are created inside of a new directory named the same as the input .pov file (but without the .pov extension) and automatically numbered. For example, this would create a new directory./myscene
and start renderingmyscene01.tga
,myscene02.tga
, etc. in it.-S5
- Specify a specific start frame offset. For example, this would start rendering at frame #5. Defaults to first frame if not present.-R15
- Duration (number of frames) to render. For example this would let you render 15 frames starting at frame 5 of a 60 frame total animation. When not present, this defaults to the total number of frames.-Dambient
- Disables calculation and contribution of Phong ambient light.-Ddiffuse
- Disables calculation and contribution of Phong diffuse light.-Dspecular
- Disables calculation and contribution of Phong specular light.-Dshadows
- Disables calculation and rendering of simple shadows.-Dreflection
- Disables calculation and contribution of reflected light.-Drefraction
- Disables calculation and contribution of refracted light.-Dantialiasing
- Disables casting of supersampling rays for antialiasing.
References
- Lua 5.1 Reference Manual: http://www.lua.org/manual/5.1/
- Embedding a Scripting Language Inside Your C/C++ Code: http://www.debian-administration.org/articles/264
- FFmpeg Documentation: http://ffmpeg.org/ffmpeg-doc.html
- Wikipedia article on Supersampling: http://en.wikipedia.org/wiki/Supersampling
- Rotating a vector another arbitrary vector: http://www.blitzbasic.com/Community/posts.php?topic=57616#645017