Now we should see the most astonishing feature of the
Lightflow Rendering Tools, the one that will make your
images real, and their colors alive: global illumination.
In the last five years many renderers integrated special
techniques to handle the light transport phenomena, but they
almost always provided slow algorithms, that would also fail in
many situations, producing both uncorrect results and
anaesthetical artifacts.
Lightflow's Global Illumination Engine produces
almost perfect images, which are so physically accurate as
good-looking to the human eye.
Before entering the next tutorial, I would like to spend some
words on the internals of Lightflow's Global
Illumination Engine, giving the right space to some
important credits.
At this point, you should already have understood that
Lightflow is not based on a monolithic structure: on the
contrary it adopts a divide and conquer philosophy.
In particular the proprietary Multiway Light Channels split the
complex problem of light transport in many simpler parts, each
treated with ad hoc algorithms.
The result is a mix inspired by the most powerful rendering
techniques available to date, such as
Arvo's backward ray-tracing [1] [2],
Kajiya's path tracing [3],
Jensen's photon maps [4],
Shirley's photon density estimation [5],
Ward's irradiance gradients [7], and probably many
others that have at least influenced the wonderful research
field of Computer Graphics...
To try Lightflow's Global Illumination Engine we will use the famous Cornell Box, an experimental scene that was invented at the Cornell University to test different algorithms.
#include < Lightflow/LfLocalSceneProxy.h > void main(void) { LfLocalSceneProxy* s = new LfLocalSceneProxy(); LfArgList list; LfTransform trs; list.Reset(); list << "trace-depth" << LfInt( 6 ); list << "radiosity-samples" << LfInt( 400 ); list << "radiosity-threshold" << 0.1; list << "radiosity-reuse-distance" << 0.25, 0.4, 0.01; list << "photon-count" << LfInt( 300000 ); list << "photon-clustering-count" << LfInt( 2000 ) << LfInt( 100 ); s->NewInterface( "default", list ); // the "trace-depth" attribute controls the maximal number of ray-traced // light bounces. // the "radiosity-depth" attribute controls the maximal number of // radiosity iterations, that is to say the number of bounces of the indirect // illumination. // the "radiosity-samples" attribute sets the amount of rays that are // used to sample the light space at every surface location. Normally // values between 200 and 500 produce good results. Note that this parameter // is very influent on the rendering time, since light sampling is one // of the most time consuming tasks. // "radiosity-threshold" sets the maximal error bound in the radiosity // estimation. A value of 0.1 means that the error is allowed to be 10% // of the real value. // "radiosity-reuse-distance" sets the screen, maximum and minimum distance // from different sampling locations. This parameter is the only one that // must be set accordingly to the scene size. The smaller these values are, // the better the result will be, but usually a good value for the // screen distance is from 0.2 to 0.5, while a good value for the // maximum distance is everything greater than one fifth of the length // of the visible surfaces. // In this case we are modeling a room with sides 2 unities long, hence // a value of 0.4 will prove to be good enough. The minimum distance // should be an order of magnitude less. // should be an order of magnitude less. // The "photon-count" parameter controls the amount of photons that are // spread into the scene to compute the global illumination. Obviously // more photons means better approximations and longer times. list.Reset(); list << "position" << LfPoint( 0, 0, 0.98 ); list << "direction" << LfVector3( 0, 0, -1 ); list << "angle" << 0.0 << PI / 2.0; list << "radius" << 0.05; list << "samples" << LfInt( 7 ); list << "color" << LfColor( 8, 8, 8 ); s->LightOn( s->NewLight( "soft-conic", list ) ); // We simulate an area light with a conic light that produces soft // shadows. The spreading angle of the light is set to 90 degrees // (PI / 2 in radians) to obtain the same light distribution of a patch // light source. We could also put a real "patch" light, with a well // defined surface, but the computation times would have been longer. // Check the class documentation to see how area lights work, and how // fake soft shadows may be obtained with the "soft" and "soft-conic" types. list.Reset(); list << "kc" << LfColor( 3, 3, 3 ); list << "shadowing" << 0.0; LfInt neon = s->NewMaterial( "matte", list ); list.Reset(); list << "kdr" << LfColor( 0.9, 0.9, 0.9 ); list << "ksr" << LfColor( 0.5, 0.5, 0.5 ); list << "km" << 0.07; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 1 ); list << "caustics" << LfInt( 0 ) << LfInt( 0 ); LfInt whitewash = s->NewMaterial( "generic", list ); list.Reset(); list << "kdr" << LfColor( 0.8, 0.1, 0.1 ); list << "ksr" << LfColor( 0.5, 0.5, 0.5 ); list << "km" << 0.07; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 1 ); list << "caustics" << LfInt( 0 ) << LfInt( 0 ); LfInt redwash = s->NewMaterial( "generic", list ); list.Reset(); list << "kdr" << LfColor( 0.2, 0.3, 0.8 ); list << "ksr" << LfColor( 0.5, 0.5, 0.5 ); list << "km" << 0.07; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 1 ); list << "caustics" << LfInt( 0 ) << LfInt( 0 ); LfInt bluewash = s->NewMaterial( "generic", list ); list.Reset(); list << "kdr" << LfColor( 0.9, 0.9, 0.9 ); list << "ksr" << LfColor( 0.5, 0.5, 0.5 ); list << "km" << 0.07; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 1 ); list << "caustics" << LfInt( 0 ) << LfInt( 0 ); list << "visibility" << LfInt( 1 ); LfInt trnswash = s->NewMaterial( "generic", list ); list.Reset(); list << "fresnel" << LfInt( 1 ); list << "IOR" << 9.0; list << "kr" << LfColor( 1, 1, 1 ); list << "kt" << LfColor( 1, 1, 1 ); list << "kd" << 0.0; list << "km" << 0.02; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 0 ); list << "caustics" << LfInt( 2 ) << LfInt( 2 ); metal = s->NewMaterial( "physical", list ); list.Reset(); list << "fresnel" << LfInt( 1 ); list << "IOR" << 1.57; list << "kdr" << LfColor( 0, 0, 0 ); list << "kdt" << LfColor( 0, 0, 0 ); list << "ksr" << LfColor( 1, 1, 1 ) << LfColor( 0.5, 0.8, 1 ); list << "kst" << LfColor( 1, 1, 1 ) << LfColor( 1, 0.6, 0.2 ); list << "kr" << LfColor( 1, 1, 1 ); list << "kt" << LfColor( 1, 1, 1 ); list << "km" << 0.02; list << "shinyness" << 1.0; list << "radiosity" << LfInt( 0 ); list << "caustics" << LfInt( 2 ) << LfInt( 2 ); glass = s->NewMaterial( "generic", list ); s->MaterialBegin( whitewash ); list.Reset(); list << "points" << LfVector3( -0.25, -0.25, 0.995 ) << LfVector3( 0.25, -0.25, 0.995 ) << LfVector3( -0.25, 0.25, 0.995 ) << LfVector3( 0.25, 0.25, 0.995 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( -0.25, 0.25, 0.99 ) << LfVector3( 0.25, 0.25, 0.99 ) << LfVector3( -0.25, 0.25, 1.00 ) << LfVector3( 0.25, 0.25, 1.00 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( -0.25, -0.25, 0.99 ) << LfVector3( -0.25, -0.25, 1.00 ) << LfVector3( 0.25, -0.25, 0.99 ) << LfVector3( 0.25, -0.25, 1.00 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( 0.25, -0.25, 0.99 ) << LfVector3( 0.25, -0.25, 1.00 ) << LfVector3( 0.25, 0.25, 0.99 ) << LfVector3( 0.25, 0.25, 1.00 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( -0.25, -0.25, 0.99 ) << LfVector3( -0.25, 0.25, 0.99 ) << LfVector3( -0.25, -0.25, 1.00 ) << LfVector3( -0.25, 0.25, 1.00 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( neon ); list.Reset(); list << "points" << LfVector3( -0.25, -0.25, 0.99 ) << LfVector3( 0.25, -0.25, 0.99 ) << LfVector3( -0.25, 0.25, 0.99 ) << LfVector3( 0.25, 0.25, 0.99 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( trnswash ); list.Reset(); list << "points" << LfVector3( -1, -1, -1 ) << LfVector3( 1, -1, -1 ) << LfVector3( -1, -1, 1 ) << LfVector3( 1, -1, 1 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( whitewash ); list.Reset(); list << "points" << LfVector3( -1, -1, -1 ) << LfVector3( -1, 1, -1 ) << LfVector3( 1, -1, -1 ) << LfVector3( 1, 1, -1 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( -1, -1, 1 ) << LfVector3( 1, -1, 1 ) << LfVector3( -1, 1, 1 ) << LfVector3( 1, 1, 1 ); s->AddObject( s->NewObject( "patch", list ); list.Reset(); list << "points" << LfVector3( -1, 1, -1 ) << LfVector3( -1, 1, 1 ) << LfVector3( 1, 1, -1 ) << LfVector3( 1, 1, 1 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( redwash ); list.Reset(); list << "points" << LfVector3( -1, -1, -1 ) << LfVector3( -1, -1, 1 ) << LfVector3( -1, 1, -1 ) << LfVector3( -1, 1, 1 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( bluewash ); list.Reset(); list << "points" << LfVector3( 1, -1, -1 ) << LfVector3( 1, 1, -1 ) << LfVector3( 1, -1, 1 ) << LfVector3( 1, 1, 1 ); s->AddObject( s->NewObject( "patch", list ); s->MaterialEnd(); s->MaterialBegin( glass ); s->TransformBegin( trs.Translation( LfVector3( -0.45, 0, -0.1 ) ) ); list.Reset(); list << "radius" << 0.35; s->AddObject( s->NewObject( "sphere", list ) ); s->TransformEnd(); s->MaterialEnd(); s->MaterialBegin( metal ); s->TransformBegin( trs.Translation( LfVector3( 0.45, 0.4, -0.65 ) ) ); list.Reset(); list << "radius" << 0.35; s->AddObject( s->NewObject( "sphere", list ) ); s->TransformEnd(); s->MaterialEnd(); list.Reset(); list << "file" << "cornell.tga"; LfInt saver = s->NewImager( "tga-saver", list ); s->ImagerBegin( saver ); list.Reset(); list << "eye" << LfPoint( 0, -2.99, 0 ); list << "aim" << LfPoint( 0, 0, 0 ); LfInt camera = s->NewCamera( "pinhole", list ); s->ImagerEnd(); s->Render( camera, 300, 300 ); delete s; }(view image)
Here this manual ends. Now you should have at least understood the
main mechanims upon which Lightflow is based, otherwise
you'll better reread the first chapters. If you cannot
comprehend some of the examples because the effect of some
classes is not very clear, try checking the relative class documetation.
If then something still remains obscure feel free to contact
me via email, using the subject Lightflow Explanations.
I will also appreciate any suggestion (in this case use the
subject Lightflow Suggestions, please).
For now, enjoy Lightflow and have a good time !