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 Client
Side of the Lightflow Rendering Interface:
the LfSceneProxy.
As we said in the previous chapter, the Client Side API
does not let you create and access scene elements directly.
On the contrary it provides only a one way data communication
protocol, in which the client program passes data to the
Lightflow server through the use of a scene proxy.
This protocol is represented by a completely abstract class,
LfSceneProxy, whose behaviour is to be defined through
standard inheritance.
The Lightflow Rendering Interface sets only some rules for the
actual implementations of this class by estabilishing the
meaning of its methods and by providing some guidelines
for their behaviours.
Here is an overview of these methods, with step by step
explanations of their functionality.
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.
LfInt 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.
LfInt 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 be 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 of the Lightflow Rendering Tools, which simulates haloing around the visible light sources, reproducing a common lens effect.
LfInt 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.
LfInt 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.
LfInt 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.
LfInt 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.
LfInt 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.
LfInt 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.
LfInt 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.
LfInt 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 (LfInt 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 (LfInt 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 (LfInt light) / void LightOff (LfInt 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 (LfInt 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 (LfInt object)Adds an object to the scene. Objects that are created but not added are not rendered.
void TransformBegin (LfTransform 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 (LfInt camera, LfInt width, LfInt height, LfFloat startcol=0.0, LfFloat startrow=0.0, LfFloat 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 LfSceneProxy 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.
#include < Lightflow/LfLocalSceneProxy.h > void main(void) { LfLocalSceneProxy* s = new LfLocalSceneProxy(); LfArgList list; list.Reset(); list << "position" << LfPoint( 5.0, -5.0, 4.0 ); list << "color" << LfColor( 300.0, 300.0, 300.0 ); s->LightOn( s->NewLight( "point", list ) ); list.Reset(); list << "kr" << LfColor( 1.0, 0.9, 0.8 ); list << "kaf" << 0.3; list << "density" << 0.3; list << "sampling" << LfInt40; list << "shadow-caching" << LfPoint( -1.2, -1.2, -1.2 ) << LfPoint( 1.2, 1.2, 1.2 ); LfInt gas = s->NewInterior( "dust", list ); s->InteriorBegin( gas ); list.Reset(); LfInt cloud = s->NewMaterial( "transparent", list ); s->InteriorEnd(); s->MaterialBegin( cloud ); list.Reset(); list << "radius" << 1.2; s->AddObject( s->NewObject( "sphere", list ) ); s->MaterialEnd(); list.Reset(); list << "ka" << LfColor( 0, 0, 0.5 ); list << "kc" << LfColor( 1, 0.5, 0.5 ); list << "kd" << 0.5; list << "km" << 0.1; LfInt plastic = s->NewMaterial( "standard", list ); s->MaterialBegin( plastic ); list.Reset(); list << "radius" << 0.5; s->AddObject( s.NewObject( "sphere", list ) ); s->MaterialEnd(); list.Reset(); list << "file" << "ball3.tga"; LfInt saver = s->NewImager( "tga-saver", list ); s->ImagerBegin( saver ); list.Reset(); list << "eye" << LfPoint( 0, -4, 0 ); list << "aim" << LfPoint( 0, 0, 0 ); LfInt camera = s->NewCamera( "pinhole", list ); s->ImagerEnd(); s->Render( camera, 300, 300 ); delete s; }(view image)
#include < Lightflow/LfLocalSceneProxy.h > void main(void) { LfLocalSceneProxy* s = new LfLocalSceneProxy(); LfArgList list; LfTransform trs; list.Reset(); list << "position" << LfPoint( 4.0, -6.0, -5.0 ); list << "color" << LfColor( 200.0, 200.0, 200.0 ); s->LightOn( s->NewLight( "point", list ) ); list.Reset(); list << "position" << LfPoint( -7.5, -6.0, 2.0 ); list << "color" << LfColor( 300.0, 150.0, 150.0 ); LfInt light1 = s->NewLight( "point", list ); list.Reset(); list << "position" << LfPoint( -2.0, 0.0, 8.0 ); list << "color" << LfColor( 150.0, 300.0, 150.0 ); LfInt light2 = s->NewLight( "point", list ); list.Reset(); list << "position" << LfPoint( 8.0, -6.0, 5.0 ); list << "color" << LfColor( 150.0, 150.0, 300.0 ); LfInt light3 = s->NewLight( "point", list ); s->LightBegin(); s->LightOn( light1 ); list.Reset(); list << "ka" << LfColor( 0.1, 0.1, 0.1 ); list << "kc" << LfColor( 1, 1, 1 ); list << "kd" << 0.5; list << "km" << 0.1; LfInt plastic1 = s->NewMaterial( "standard", list ); s->LightEnd(); s->LightBegin(); s->LightOn( light2 ); list.Reset(); list << "ka" << LfColor( 0.1, 0.1, 0.1 ); list << "kc" << LfColor( 1, 1, 1 ); list << "kd" << 0.5; list << "km" << 0.1; LfInt plastic2 = s->NewMaterial( "standard", list ); s->LightEnd(); s->LightBegin(); s->LightOn( light3 ); list.Reset(); list << "ka" << LfColor( 0.1, 0.1, 0.1 ); list << "kc" << LfColor( 1, 1, 1 ); list << "kd" << 0.5; list << "km" << 0.1; LfInt plastic3 = s->NewMaterial( "standard", list ); s->LightEnd(); s->TransformBegin( trs.Translation( LfVector3( -2.0, 0, 0 ) ) ); s->MaterialBegin( plastic1 ); list.Reset(); list << "radius" << 1.0; s->AddObject( s->NewObject( "sphere", list ) ); s->MaterialEnd(); s->TransformEnd(); s->MaterialBegin( plastic2 ); list.Reset(); list << "radius" << 1.0; s->AddObject( s->NewObject( "sphere", list ) ); s->MaterialEnd(); s->TransformBegin( trs.Translation( LfVector3( 2.0, 0, 0 ) ) ); s->MaterialBegin( plastic3 ); list.Reset(); list << "radius" << 1.0; s->AddObject( s->NewObject( "sphere", list ) ); s->MaterialEnd(); s->TransformEnd(); list.Reset(); list << "file" << "lights.tga"; LfInt saver = s->NewImager( "tga-saver", list ); s->ImagerBegin( saver ); list.Reset(); list << "eye" << LfPoint( 0, -4, 0 ); list << "aim" << LfPoint( 0, 0, 0 ); list << "fov" << atan( 0.5 / 0.75 )*2.0; LfInt camera = s->NewCamera( "pinhole", list ); s->ImagerEnd(); s->Render( camera, 300, 300 ); delete s; }(view image)