Subsections


2. User Manual

2.1 Overview

FlowVR-render proposes an advanced framework for distributed rendering. FlowVR-render is built on top of FlowVR. It is based on viewer modules that send primitives to renderer modules using a specific protocol. Viewers describe the graphics properties of objects to be rendered while renderers are in charge of computing these graphical representations.

By analogy to OpenGL, a viewer is similar to an OpenGL client application while the renderer takes the role of the OpenGL graphics-card driver. However, there are several important differences:

A scene is built from one or several viewer modules that are in charge of issuing primitives to the renderers. Usually a viewer is dedicated to one type of objects. To add a new object in a scene, it just requires to add the corresponding viewer to the application.

Viewers can run asynchronously, using FlowVR greedy patterns, enabling to include in the same scene objects that are not updated at the same rate.

Each primitive contains the description of a batch of polygons (points, lignes or triangles) and their rendering parameters (color, position, etc.). It uses resources like shaders, coordinates and other attributes via a vertex buffer, element indices via an index buffer and optionnally one or several textures.

Each primitive also stores its bounding box. This information is required to optimize communications using frustum culling. It is useful to specify this information in the primitive as it is costly to recover from the other parameters (especially when using shaders that can change vertex positions).

A name can be specified for each primitive for identification in error messages, visual selection feedback, or filters based on name patterns.

Some rendering algorithms require primitives to be processed in a given order. FlowVR Render provides a mechanism to enforce this ordering by associating each primitive with an order value. This number defines a partial ordering between primitives. A primitive with a lower order value must be rendered before a primitive with a higher value. Primitives with the same value can be rendered in any order. Different values will mainly be used when rendering order can affect the final image like for transparent primitives or user-interface overlays. For a given order value, renderers can re-order primitives to improve performance. For instance, primitives can be sorted front-to-back to take advantage of fast Z-culling, primitives with similar parameters such as textures and shaders can be gathered to reduce state switching overheads.

Large datasets such as textures or vertex attributes are often used by several primitives in the scene, sometimes across several viewers. To avoid duplicating them in each primitive we define resources. A resource can encapsulate a shader, texture, vertex buffer or index buffer. It is identified by an unique ID. Each primitive refers to the IDs of all the resources it uses. Notice that it introduces a one-way dependency between primitives and resources, but not between primitives.

For a viewer, many of the parameters do not change from one iteration to the other. To avoid re-issuing primitives that did not changed, the protocol relies on a retained like mode. Renderers maintain the current description of the scene, and viewers describe the changes to this description. Each change is encapsulated into a chunk. At each frame, a viewer sends one message containing a list of chunks. A chunk can correspond to a:

This protocol is purely one-way, from viewers to renderers. In particular, IDs are generated by the viewers and not the renderers. This property is useful when the viewers and renderers are not tightly synchronized (for example storing messages on disk and replaying them later).


2.2 The Renderer

The renderer is a FlowVR metamodule called flowvr-renderer. It comes with its .desc file: desc/flowvr-renderer.desc.xml.

This metamodule can spawn several renderer that can be synchronized (genlock, datalock).

Users should not have to modify the renderer.

2.2.1 Rendering

A renderer opens an OpenGL windows where it renders primitives according to the messages received from viewers on its input port scene.

Notice that with NVIDIA graphics cards, it is possible to activate the antialiasing (usefull for screen shots or videos) using the nvidia-settings utility or setting the environment variable __GL_FSAA_MODE to 4 or 5.

2.2.2 Keyboard and Mouse Inputs

When the rendering windows get the focus, flowvr-renderer can capture the mouse and keyboard events. We list below the different behaviors that some keys enable to control.

The F1 key enables to switch between the following different modes:

If no viewer control the camera (the camera is a special resource ), it can be controlled from the keyboard and mouse:

By pressing the F2 key, the user can force to activate these keys, even if a viewer control the camera.

Other active keys are:

2.2.3 Command Line Arguments

Several command line arguments enable to control the behavior of the renderer. To get the list of the ones available tape in:

flowvr-renderer -h

In particular it enables to control the background (-black, -white and -b background.png), to tell the renderer to record a video (-video), and specify the display configuration, which can either be a file or a direct specification of the window size. The following section will detail this.

2.2.3.1 Display Configuration

When FlowVR Render is used in a distributed rendering setup, where several instances are rendering a part of the image, it is necessary to specify which instance render which part, in other word what is the view frustum of each renderer instance.

If the screen setup is a simple tiling of each image in a grid without overlapping, as shown in figure  , then the dimension of each screen as well as the number of rows and columns as @resxxresy[@nxxny[@aspect]], where resx and resx is the resolution of each screen, nx and ny the number of screens horizontally and vertically (defaulting to 1x1, i.e. a single screen), and optionally aspect the aspect ratio of the whole display area (only necessary of the pixels are not squares).

For other more generic configurations, a file is used to describe the parameters of each renderer. This description is much more powerful, as it can describe arbitrary screen transformations, as well as blending masks for smooth overlaps, and stereo setups. This file only contains general parameters and points to external files storing the parameters (view frustum transform, blending mask) for each renderer. The file is formated as a simple text file, each line containing the name of a parameter followed by its value. The following description is geared toward a display-wall setup, where several projectors, each controlled by a renderer, are projecting a small image on a big common screen. We use two coordinate systems: the local projector-space, corresponding to the pixels of the image of each projector, and the global screen-space, which is where the final complete image is specified.

The parameters in the display configuration file are:

After the global parameters, the file contain a line for each renderer instance with the following format: hostname win_x0 win_y0 win_nx win_ny view, where hostname identifies the renderer instance, win_* specify the corrdinate of the rendering window, and view contains the eye of this view (L or R) and the projector row and column number (yxx). So for example for a 2x2 non-stereo display wall, the top-left projector would be L0x0, the top-right L0x1, the bottom-left L1x1, and the bottom-right L1x1.

The view specification is used to find the files describing the projector's transform matrix, multiplicative and additive masks. The file format patterns are:

  1. path/proj_yxx_homography.txt for the frustum transform (3x3 homography from projector-space to screen-space).
  2. path/proj_yxx_mask.png for smooth blending mask (usually a grayscale image) used in mask correction.
  3. path/proj_yxx_add.png for additive blending mask used in lumi correction.
  4. path/proj_yxx_mult.png for multiplicative blending mask used in lumi correction.

These files are not intended to be created by hand but instead to be created procedurally, either from a high-level description of the setup, or from measurements (i.e. calibration data of the projectors). In the base FlowVR Render distribution, the flowvr-gendispconfig is provided to generate configuration of display-walls with projectors aligned on a grid with a specific vertical and horizontal overlap between projectors. Its command line format is :

flowvr-gendispconfig nx ny resx resy bx by gamma
where nx is the number of columns of projectors, ny the number of rows, resx and resy the resolution of each projector, bx and by the number of overlapping pixels horizontally and vertically, and gamma the estimated gamma response (tuning this value will make the overlapping regions appear darker of brighter). This tool assumes that the hosts controlling the projectors are called visu1, visu2, ... If this is not the case you have to change the hostnames manually in the main display configuration file.

2.2.4 Output Ports

A renderer has the following output ports:

2.2.5 Input Ports

A renderer has the following output ports:

2.2.6 Multi Renderer Synchronization

Multiple renderers can be synchronized for driving multi-projector or multi-display environments.

Datalock need to be ensured for the keyboard and mouse events.


2.2.6.1 Swaplock

Actually, launching one flowvr-renderer metamodule starts two modules (two intermingled loops running in the same process). The rendering module, called render, and a swaplock module, called swaplock . This last module locks the buffer swap to the occurence of a beginIt event (only when this port is active, i.e. connected).

When swaplock is not required (i.e. a single display, or multiple separate displays) or when it is directly handled by the graphics cards then this module is not used.

To create a classical swaplock setup where all renderers are synchronized, then a barrier must link all the swaplock modules. Assuming the Visu is the flowvr-renderer metamodule, then the following perl code will create the necessary connections:

&barrier('Visu/swaplock','endIt','Visu/swaplock','beginIt','SwapLock',1);
Note that this code is included in the src/perl/render-swaplock.pl file provided with FlowVR Render. The first four arguments specify the source and destination of the barrier, SwapLock is the ID of the created filters, and 1 is the number of initial tokens to send (we must send one message to beginIt before receiving the first endIt signal).

As another example it is possible to link all renderers to a fixed frequency filter to control their refresh rate:

&moduleFrequency('Visu/swaplock',100);
Note that the renderers will only be synchronized as long as they can sustain this frequency.

Building upon these examples it is possible to design applications with different swaplock groups and controllable update frequencies

2.2.6.2 Datalock

Swaplock does make sense only if it is associated with a proper datalock, meaning that all renderers should render the same data at each frame. Datalock usually depends on the application, but some important considerations on the renderer level must be taken care of. Three data streams are used by renderers:

To resolve the first two all renderers' dt and keysmouse input ports must be connected to the same source. This source can for example be the events from the first renderer's window:

&addConnection('Visu/render/0','outdt','Visu/render','dt'); &addConnection('Visu/render/0','outkeysmouse','Visu/render','keysmouse');
Note that it is done by default in src/perl/render-swaplock.pl. This means that the keyboard and mouse of the first renderer will control all renderers. By customizing this code it can be changed to another renderer, or with a slightly more complicated graph it would also be possible to merge events from all renderers.

The last data stream is the scene itself. For this case many different schemes can be envisionned depending on the application. There is however two cases that are the most common and from which most other schemes can be constructed:

The first case is handled in src/perl/render-fifo.pl. The basic idea is to use a Merge filter on each renderer to collect messages broadcasted from all viewers. The datalock is handled implicitely as all connections are FIFO, hence all renderer with receive the same messages at each frame.

The second case is handled in src/perl/render-greedy.pl. The basic idea is the same as previously, except a coherent greedy is inserted before the merge filter. This Greedy synchronizor will decide which messages should be forwarded to the renderer, and this decision is unique for all renderers to ensure proper datalock.

2.3 Viewers

A viewer is a FlowVR metamodule with a specific scene output port. To ease programming a viewer, like declaring the scene, preparing chunks and changing primitives' parameters, FlowVR Render provides several classes that we detail in the following. FlowVR Render also comes with some basic viewers that we found useful for many applications.

2.3.1 Examples

2.3.2 Compilation

Rely on pkg-config to get the right flags and libraries when compiling a viewer:

g++ -o sphere1 sphere1.cpp `pkg-config -cflags -libs flowvr-mod flowvr-render`

2.3.3 .desc File

Each viewer should have its FlowVR .desc file. In particular this .desc should include the scene output port. See the share/flowvr-render/examples/something

2.3.4 Basic Viewers

FlowVR Render comes with several basic viewers located in src/viewers for the source code and modules/desc for their .desc files. Refer to these directories to get the full list of available viewers. The main ones are:

2.3.5 Viewer Programming

We present in the following the FlowVR Render specific functions to program a viewer. We do not detail the classical FlowVR functions for metamodule programming. Refer to theFlowVR documentation if required.

A viewer usually follows the following steps:

Notice it is adviced to set the shader primitives first, as most of the resources depend on them.

The doc/html/chunkwriter_8h-source.html file (source code at include/render/chunkwriter.h) contains most of the functions specific to viewer programming.

Figure: UML diagram of the primitives and resources used when describing a scene
Image render-uml3 .

Figure: Chunk format. Data correspond to resource and primitive attributes (Fig. [*]). Level is intended to handle level of details.
Image render-chunks

2.3.5.1 The Scene Output Port

A viewer is a FlowVR metamodule with a specific scene output port that can be declared as follow:

flowvr::render::SceneOutputPort pOut("scene");

Beside the default stamps defined for all metamodule messages, a viewer declare a scratch stamp for its scene port. It has a 1 or 0 value depending if the message contains chunks or not.

2.3.5.2 Primitive and Resource IDs

IDs for resources and primitives are generated using the following function from flowvr-base/include/moduleapi.h:

flowvr::ModuleAPI::generateID();

This function ensures the ID returned is unique through the whole application.

The camera has a predefined ID ID_CAMERA.

2.3.5.3 FLOWVR_DATA_PATH

A viewer loads its data files (shaders, buffers, textures) :

By default FLOWVR_DATA_PATH gives access to the data directory . Update this variable value, if you create new data repositories.

2.3.5.4 Resource Handling

To create a resource:

To load a resource from a file:

To generate a resource in a procedural way:

To update a resource:

To suppress a resource:

It is also possible to save a texture to a file with:

For instance the sphere1.cpp example only set a mesh for the sphere using:

scene.addMeshSphere(idVB, idIB);

The sphere2.cpp example additionally loads a texture from the file given in argument (set in the examplles/sphere2/net/sphere2.mml.xml file) or a default texture otherwise (a chessboard):

if (argc<2 || !(texture=scene.loadTexture(idTexture,argv[1]))) texture=scene.addDefaultTexture(idTexture);
It also loads the vertex shader share/flowvr-render/data/shaders/sphere_v.cg and the pixel shader data/shaders/sphere_p.c using:
scene.loadVertexShader(idVS, "shaders/sphere_v.cg"); scene.loadPixelShader(idPS, "shaders/sphere_p.cg");

2.3.5.5 Primitive Handling

To create a primitive:

To delete a primitive:

To move or rotate a primitive:

Both sphere examples just declare one primitive:

scene.addPrimitive(idPrim, "Sphere");

The sphere2.cpp example uses rotatePrimitive to rotate the sphere at each iteration by an angle of it degrees around the Vec3f(0,0,1) axis (default rotation axis):

scene.rotatePrimitive(idPrim, it);


2.3.5.6 Primitive Parameters

There are 4 functions for handling the parameters of a primitive depending on the type of parameter:

These functions take as parameters:


2.3.5.7 VISIBLE


2.3.5.8 ORDER


2.3.5.9 VSHADER

The sphere2.cpp example uses this parameter to add the previously loaded vertex shader to the sphere primitive:

scene.addParamID(idPrim, ChunkPrimParam::VSHADER,"",idVS);


2.3.5.10 PSHADER

The sphere2.cpp example uses this parameter to add the previously loaded pixel shader to the sphere primitive:

scene.addParamID(idPrim, ChunkPrimParam::PSHADER,"",idPS);


2.3.5.11 VBUFFER_ID

Both sphere examples example uses this parameter to :

scene.addParamID(idPrim, ChunkPrimParam::VBUFFER_ID, "position", idVB); scene.addParamID(idPrim, ChunkPrimParam::VBUFFER_ID, "normal", idVB);

The sphere2.cpp example uses this parameter to :

scene.addParamID(idPrim, ChunkPrimParam::VBUFFER_ID, "texcoord0", idVB);


2.3.5.12 VBUFFER_NUMDATA

Both sphere examples example uses this parameter to :

scene.addParam(idPrim, ChunkPrimParam::VBUFFER_NUMDATA, "normal", int(1));

The sphere2.cpp example uses this parameter to :

scene.addParam(idPrim, ChunkPrimParam::VBUFFER_NUMDATA, "texcoord0", int(2));


2.3.5.13 VBUFFER_V0


2.3.5.14 NBV


2.3.5.15 IBUFFER_ID

Both sphere examples example uses this parameter to add the previously loaded vertex buffer (sphere mesh) to the sphere primitive:

scene.addParamID(idPrim, ChunkPrimParam::IBUFFER_ID, "", idIB);


2.3.5.16 IBUFFER_I0


2.3.5.17 IBUFFER_NBI


2.3.5.18 TEXTURE

The sphere2.cpp example uses this parameter to :

scene.addParamID(idPrim, ChunkPrimParam::TEXTURE, "texture", idTexture);


2.3.5.19 PARAMVSHADER and PARAMPSHADER

All Cg uniform parameters (see http://developer.nvidia.com/object/cg_toolkit.html) of a shader can be controlled through the PARAMVSHADER or PARAMPSHADER parameter type.

Both examples

scene.addParamEnum(idPrim, ChunkPrimParam::PARAMVSHADER, "ModelViewProj", ChunkPrimParam::ModelViewProjection); scene.addParamEnum(idPrim, ChunkPrimParam::PARAMVSHADER, "ModelView", ChunkPrimParam::ModelView); scene.addParamEnum(idPrim, ChunkPrimParam::PARAMVSHADER, "ModelViewIT", ChunkPrimParam::ModelView_InvTrans);

The sphere2.cpp example additionally use the mapscale from the vertex shader used to modify texture scaling. It enables to repeat twice the texture on the sphere if it is closed to a square texture (otherwize strong distortions would appear):

if (texture->nx < 1.5*texture->ny) scene.addParam(idPrim, ChunkPrimParam::PARAMVSHADER, "mapscale", Vec2f(2,1));


2.3.5.20 PARAMOPENGL

All primitives expose the following parameters that can be controlled through the parameter type PARAMOPENGL.

Notice that names and values are case-sensitives.


2.3.5.21 TRANSFORM


2.3.5.22 TRANSFORM_POSITION


2.3.5.23 TRANSFORM_ROTATION


2.3.5.24 TRANSFORM_SCALE


2.3.5.25 NAME

Figure: Primitive types (OpenGL like). Points, Lines and Triangles are the more common ones. The others enable to reduce the number of indices to send.
Image gl_points Image gl_lines Image gl_line_strip Image gl_line_loop Image gl_polygon
Points Lines Line Strip Line Loop Polygon
Image gl_triangles Image gl_triangle_fan Image gl_triangle_strip Image gl_quads Image gl_quad_strip
Triangles Triangle Fan Triangle Strip Quads Quad Strip

2.3.5.26 Send the Message

Once all set the message can be sent using the ChunkWriter::put function with the SceneOutputPort as parameter.

2.3.5.27 Shader Programming

2.3.5.28 Fonts

2.4 Examples

All examples are gathered into the examples directory. Go into this directory to get an up-to-date list.

2.4.1 Compilation and Execution

To compile example go into the corresponding directory and execute:

make application

To run an example on a single machine, launch a FlowVR daemon:

flowvrd&
and launch the application with:
make run

FlowVR-GUI
All the examples can also be configured, compiled and run from flowvr-gui ( see FlowVR documentation).

2.4.2 sphere1 and sphere2

These simple examples are detailed into previous sections. Coming with the sphere2 example, another Makefile (share/flowvr-render/examples/sphere2/Makefile-multi.mak) allows the user to display this example among several windows. Only the network description file changes (share/flowvr-render/examples/sphere2/net/sphere2-multi.mml.xml).

2.4.3 particules1 and particules2

A coupling between a particule simulation and a point viewer. The main difference between particles1 and 2 is that instead of display a pixel for each particle, it displays a textured sprite. This is done by the mean of another pixel and vertex shaders (see share/flowvr-render/examples/particles2, share/flowvr-render/data/shaders/sprite_p.cg and share/flowvr-render/data/shaders/sprite_v.cg)

It also features blending between particles.

2.4.4 text1 and text2

Those examples show how to manages fonts, and render text with flowvr-render. The second one show how to make a text insensitive to rotations, it always keeps its orientation.

2.4.5 volume1 and fire

These 2 examples uses a ray-casting shader for volume rendering. The volume1 example uses the share/flowvr-render/data/shaders/volume_p.cg based on a volume function taking a position parameter and returning the color of the volume at this position. In this example the function returne a constant value. The fire example the value returned is based on a noise and a flame texture (share/flowvr-render/data/shaders/volume_fire_p.cg).

These examples are based on the NVIDIA presentation http://download.nvidia.com/developer/presentations/2005/GDC/Sponsored_Day/GDC_2005_VolumeRenderingForGames.pdf

2.5 FlowVR-render Specific Filters

2.6 Advanced Networking Schemes

2009-04-06