In the previous chapter you have seen how to create a simple scene,
composed by elementary blocks such as lights, materials, objects
and cameras.
What you miss now is a deeper knowledge of the interactions
between these entities.
These interactions are provided by a sort of glue that connects
all of the elements, the most important class of the Lightflow Rendering Interface:
the scene.
Here is an overview of the scene class methods with step by step explanations. If you want to use Lightflow productively you should read it carefully, because you may find some unexpected behaviours, that you will comprehend and appreciate only after some times.
int newCamera (string type, list parameters)Creates a camera. Objects of this type simulate the functionality of real photographic cameras, and their task is to produce a two-dimensional image from a three-dimensional scene.
int newImager (string type, list parameters)Creates an imager. An imager is a special output device that is used by cameras when rendering. These devices may not only able to store the resulting image to a file, but they may also accomplish particular operations onto it. An example is the halo imager class, which simulates haloing around the visible light sources, reproducing a common lens effect.
int newInterface (string type, list parameters)Creates an interface object. Interfaces are special data containers which mantain global information relative to the scene. This type is necessary to hold all the data and the various settings that are to be used during the rendering stage, and that are not known a priori by the Lightflow Rendering Interface. This need arises by the fact that the Lightflow Rendering Interface is completely extensible and it is not based on any particular and monolithic rendering device. This implies that each extension class may have its own global data.
int newInterior (string type, list parameters)Creates an interior. Interiors are materials that evaluate the behaviour of light inside the volumetric spaces defined by solids. An example could be a gas contained into a sphere, or a suspension contained into a glass pot. Note however that interiors are a property of materials, not of geometries. That is to say that each material possesses one and only one interior evaluator. Then this property transfers to geometric objects since each object possesses a material. The mechanism to attach interiors to materials is similar to the one used to attach materials to objects: blocks of materials sharing the same interior are defined using interiorBegin and interiorEnd before and after their creation.
int newLight (string type, list parameters)Creates a new light. By default lights are turned off. LightOn and LightOff should be called to change their state, that is to say to specify which materials (and interiors) they illuminate.
int newMaterial (string type, list parameters)Creates a material. Each material defines the way light is reflected by a surface, and by the volume this surface encloses. The volumetric behavior of a material is defined by the current interior.
int newObject (string type, list parameters)Creates an object. Objects are the geometric entities that define a scene. Each object is made up of a unique material, which is the one that was current at the object's creation. For this reason objects should always be declared inside a material block.
int newPattern (string type, list parameters)Creates a pattern. A pattern is a function that associates a value to each surface or volume element it is evaluated on. For this reason there are two distinct types of patterns: volumetric and superficial. Volumetric patterns may be used to determine a property of an interior, while the superficial ones may be used for materials.
int newTexture (string name)Loads a texture and returns its handle. This may be used by some patterns that perform texture mapping, even if this is not the only possible application.
int newTrimmer (string type, list parameters)Creates a trimmer. Trimmers are functions that cut away parts of a parametric surface, and thus they are commonly used by objects. For example the "NURBS" object possesses a channel named "trimmer" that accepts a trimmer handle as input, and that allows to specify which portions of the surface should be considered.
void imagerBegin (int imager) / void imagerEnd (void)Define an imager block. These blocks may be nested, since these functions mantain a stack which is global to the scene. Each block defines the current imager, which is used by all the cameras that are created into it. There is no default imager, so each camera should be created into an imager block to produce a result.
void interiorBegin (int interior) / void interiorEnd (void)Define an interior block. These blocks may be nested, since these functions mantain a stack which is global to the scene. Each block defines the current interior, which is used by all the materials that are created into it. By default there is no interior, and materials could be created even outside of a block.
void lightOn (int light) / void lightOff (int light)Turn on or off a light. The state of each light influences the materials and the interiors, since each material and each interior has a list of contributors that illuminate it: these contributors are the lights that were turned on at its creation. Note that an interior and the material it was associated to may possess different sets of contributors.
void lightBegin (void) / void lightEnd (void)Define a lighting block. These blocks may be nested, since these functions mantain a stack which is global to the scene. These functions are used to store the active state of lights at a given time in order to restore it later on. This is useful when there is a fixed set of lights that illuminate all of the materials and then there is an additional set that varies from material to material. For example if our scene contains the sun and two lamps and we want the sun to illuminate everything, and each lamp to illuminate only its neighbourhood, we could do this:
sun = s.newLight( "directional", ... ) lamp1 = s.newLight( "point", ... ) lamp2 = s.newLight( "point", ... ) s.lightOn( sun ) ... # materials of distant objects s.lightBegin() s.lightOn( lamp1 ) ... # materials of the neighbours of lamp1 s.lightEnd() ... # materials of distant objects s.lightBegin() s.lightOn( lamp2 ) ... # materials of the neighbours of lamp2 s.lightEnd() ... # materials of distant objectsObviously this is not the only method, since we could also switch on/off lamp1 and lamp2 before and after their neighbours' definition, but in scenes where the lights are many and their configuration is complex the possibility of forgetting to turn off some of them may become very high.
void materialBegin (int material) / void materialEnd (void)Define a material block. These blocks may be nested, since these functions mantain a stack which is global to the scene. Each block defines the current material, which is used by all the objects that are created into it. There is no default material, so each object should be created into a material block.
void addObject (int object)Adds an object to the scene. Objects that are created but not added are not rendered.
void transformBegin (transform transformation) / void transformEnd (void)Defines a transformation block. These blocks may be nested to compose multiple transformations. A transformation is an affine function that moves spatial points. This set of functions comprises translations, rotations, scalings and their compositions, which are documented within the definition of the transform type. Each block defines the current transformation, which is applied to the objects, lights, materials, interiors and patterns that are created into it. Note that the current transformation is the result of the composition of all the nested transformation blocks. This composition is performed in reverse order to facilitate hierarchical modeling, that is to say that if you want to move an object at (1, 0, 0) and rotate it by 90 degrees around the y axis that passes for this point, you should state the rotation first and the translation then. Otherwise you would move the object at (1, 0, 0) and then rotate it around the origin, bringing it to (0, 0, 1), instead of changing its orientation only.
void radiosity (void)Computes the radiosity distribution over the scene.
void render (int camera, int width, int height, float startcol=0.0, float startrow=0.0, float endcol=1.0, float endrow=1.0)Renders a camera view. width and height specify the resolution of the image, while the optional parameters specify the portion of the image to be rendered. These numbers may be specified both as integers going from 0 to width for the start/end columns and from 0 to height for the start/end rows, and as fractionals going from 0 to 1: in this case they are interpreted as fractions of the relative image dimensions.
Now that the review of the scene class is completed we may start to illustrate its actual behaviour by compiling some ad hoc examples. The next will show you how to create a gaseous cloud using interiors.
from lightflow import * s = scene() s.lightOn( s.newLight( "point", [ "position", vector3( 5.0, -5.0, 4.0 ), "color", vector3( 300.0, 300.0, 300.0 ) ] ) ) gas = s.newInterior( "dust", [ "kr", vector3(1.0, 0.9, 0.8), "kaf", 0.3, "density", 0.3, "sampling", 40.0, "caching", vector3(-1.2,-1.2,-1.2), vector3(1.2,1.2,1.2) ] ) s.interiorBegin( gas ) cloud = s.newMaterial( "transparent", [] ) s.interiorEnd() s.materialBegin( cloud ) s.addObject( s.newObject( "sphere", [ "radius", 1.2 ] ) ) s.materialEnd() plastic = s.newMaterial( "standard", [ "ka", vector3( 0, 0, 0.5 ), "kc", vector3( 1, 0.5, 0.5 ), "kd", 0.5, "km", 0.1 ] ) s.materialBegin( plastic ) s.addObject( s.newObject( "sphere", [ "radius", 0.5 ] ) ) s.materialEnd() saver = s.newImager( "tga-saver", [ "file", "ball3.tga" ] ) s.imagerBegin( saver ) camera = s.newCamera( "pinhole", [ "eye", vector3( 0, -4, 0 ), "aim", vector3( 0, 0, 0 ) ] ) s.imagerEnd() s.render( camera, 300, 300 )(view image)
from lightflow import * s = scene() s.lightOn( s.newLight( "point", [ "position", vector3( 4.0, -6.0, -5.0 ), "color", vector3( 200.0, 200.0, 200.0 ) ] ) ) light1 = s.newLight( "point", [ "position", vector3( -7.5, -6.0, 2.0 ), "color", vector3( 300.0, 150.0, 150.0 ) ] ) light2 = s.newLight( "point", [ "position", vector3( -2.0, 0.0, 8.0 ), "color", vector3( 150.0, 300.0, 150.0 ) ] ) light3 = s.newLight( "point", [ "position", vector3( 8.0, -6.0, 5.0 ), "color", vector3( 150.0, 150.0, 300.0 ) ] ) s.lightBegin() s.lightOn( light1 ) plastic1 = s.newMaterial( "standard", [ "ka", vector3( 0.1, 0.1, 0.1 ), "kc", vector3( 1, 1, 1 ), "kd", 0.5, "km", 0.1 ] ) s.lightEnd() s.lightBegin() s.lightOn( light2 ) plastic2 = s.newMaterial( "standard", [ "ka", vector3( 0.1, 0.1, 0.1 ), "kc", vector3( 1, 1, 1 ), "kd", 0.5, "km", 0.1 ] ) s.lightEnd() s.lightBegin() s.lightOn( light3 ) plastic3 = s.newMaterial( "standard", [ "ka", vector3( 0.1, 0.1, 0.1 ), "kc", vector3( 1, 1, 1 ), "kd", 0.5, "km", 0.1 ] ) s.lightEnd() s.transformBegin( transform().translation( vector3( -2.0, 0, 0 ) ) ) s.materialBegin( plastic1 ) s.addObject( s.newObject( "sphere", [ "radius", 1.0 ] ) ) s.materialEnd() s.transformEnd() s.materialBegin( plastic2 ) s.addObject( s.newObject( "sphere", [ "radius", 1.0 ] ) ) s.materialEnd() s.transformBegin( transform().translation( vector3( 2.0, 0, 0 ) ) ) s.materialBegin( plastic3 ) s.addObject( s.newObject( "sphere", [ "radius", 1.0 ] ) ) s.materialEnd() s.transformEnd() saver = s.newImager( "tga-saver", [ "file", "lights.tga" ] ) s.imagerBegin( saver ) camera = s.newCamera( "pinhole", [ "eye", vector3( 0, -5, 0 ), "aim", vector3( 0, 0, 0 ), "distance", 0.75 ] ) s.imagerEnd() s.render( camera, 300, 300 )(view image)