Chapter 6. Creating Visual Effects

This chapter describes how to use environmental, atmospheric, lighting, and other visual effects to enhance the realism of your application.

Using pfEarthSky

A pfEarthSky is a special set of functions that clears a pfChannel's viewport efficiently and implements various atmospheric effects. A pfEarthSky is attached to a pfChannel with pfChanESky(). Several pfEarthSky definitions can be created, but only one can be in effect for any given channel at a time.

A pfEarthSky can be used to draw a sky and horizon, to draw sky, horizon, and ground, or just to clear the entire screen to a specific color and depth. The colors of the sky, horizon, and ground can be changed in real time to simulate a specific time of day. At the horizon boundary, the ground and sky share a common color, so that there is a smooth transition from sky to horizon color. The width of the horizon band can be defined in degrees.

A pfChannel's earth-sky model is automatically drawn by OpenGL Performer before the scene is drawn unless the pfChannel has a draw callback set with pfChanTravFunc(). In this case it is the application's responsibility to clear the viewport. Within the callback pfClearChan() draws the channel's pfEarthSky.

Example 6-1 shows how to set up an pfEarthSky().

Example 6-1. How to Configure a pfEarthSky

pfEarthSky  *esky;
pfChannel *chan;
 
sky = pfNewESky();
pfESkyMode(esky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
pfESkyAttr(esky, PFES_GRND_HT, -1.0f);
pfESkyColor(esky, PFES_GRND_FAR, 0.3f, 0.1f, 0.0f, 1.0f);
pfESkyColor(esky, PFES_GRND_NEAR, 0.5f, 0.3f, 0.1f,1.0f);
pfChanESky(chan, esky);


Atmospheric Effects

The complexities of atmospheric effects on visibility are approximated within OpenGL Performer using a multiple-layer sky model, set up as part of the pfEarthSky function. In this design, individual layers are used to represent the effects of ground fog, clear sky, and clouds. Figure 6-1 shows the identity and arrangement of these layers.

Figure 6-1. Layered Atmosphere Model


The lowest layer consists of ground fog, extending from the ground up to a user-selected altitude. The fog thins out with increasing altitude, disappearing entirely at the bottom of the general visibility layer. This layer extends from the top of the ground fog layer to the bottom of the cloud layer's lower transition zone, if such a zone exists. The transition zone provides a smooth transition between general visibility and the cloud layer. (If there is no cloud layer, then general visibility extends upward forever.) The cloud layer is defined as an opaque region of near-zero visibility; you can set its upper and lower boundaries. You can also place another transition zone above the cloud layer to make the clouds gradually thin out into clear air.

Set up the atmospheric simulation with the commands listed in Table 6-1

Table 6-1. pfEarthSky Routines

Function

Action

pfNewESky()

Create a pfEarthSky.

pfESkyMode()

Set the render mode.

pfESkyAttr()

Set the attributes of the earth and sky models.

pfESkyColor()

Set the colors for earth and sky and clear.

pfESkyFog()

Set the fog functions.

You can set any pfEarthSky attribute, mode, or color in real time. Selecting the active pfFog definition can also be done in real time. However, changing the parameters of a pfFog once they are set is not advised when in multiprocessing mode.

The default characteristics of a pfEarthSky are listed in Table 6-2.

Table 6-2. pfEarthSky Attributes

Attribute

Default

Clear method

PFES_FAST (full screen clear)

Clear color

0.0 0.0 0.0

Sky top color

0.0 0.0 0.44

Sky bottom color

0.0 0.4 0.7

Ground near color

0.5 0.3 0.0

Ground far color

0.4 0.2 0.0

Horizon color

0.8 0.8 1.0

Ground fog

NULL (no fog)

General visibility

NULL (no fog)

Cloud top

20000.0

Cloud bottom

20000.0

Cloud bottom color

0.8 0.8 0.8

Cloud top color

0.8 0.8 0.8

Transition zone bottom

15000.0

Transition zone top

25000.0

Ground height

0

Horizon angle

10 degrees

By default, an earth-sky model is not drawn. Instead, the channel is simply cleared to black and the Z-buffer is set to its maximum value. This default action also disables all other atmospheric attributes. To enable atmospheric effects, select PFES_SKY, PFES_SKY_GRND, or PFES_SKY_CLEAR when turning on the earth-sky model.

Clouds are disabled when the cloud top is less than or equal to the cloud bottom. Cloud transition zones are disabled when clouds are disabled.

Fog is enabled when either the general or ground fog is set to a valid pfFog. If ground fog is not enabled, no ground fog layer will be present and fog will be used to support general visibility. Setting a fog attribute to NULL disables it. See “Atmospheric Effects” for further information on fog parameters and operation.

The earth-sky model is an attribute of the channel and thus accesses information about the viewer's position, current field of view, and other pertinent information directly from pfChannel. To set the pfEarthSky in a channel, use pfChanESky().

Patchy Fog and Layered Fog

A pfVolFog is a class that uses a multi-pass algorithm to draw the scene with a fog that has different densities at different locations. It extends the basic layered fog provided by pfEarthSky and introduces a new type of fog: a patchy fog. A patchy fog has a constant density in a given area. The boundaries of this area can be defined by an arbitrary three-dimensional object or by a set of objects.

A layered fog changes only with elevation; its density and color is uniform at a given height. It is defined by a set of elevation points, each specifying a fog density and, optionally, also a fog color at the point's elevation. The density and the color between two neighboring points is linearly interpolated.

Figure 6-2 illustrates the basic difference between patchy fog and layered fog.

Figure 6-2. Patchy Fog Versus Layered Fog


Compared to a layered fog in pfEarthSky, a layered fog in pfVolFog has distinct advantages:

  • It can be specified by an arbitrary number of elevation points.

  • Each elevation point can have a different color associated with it.

  • A layered fog in pfVolFog is not dependent on an InfiniteReality-specific texgen. It can also be drawn using only 2D textures to simulate the 3D texture. Thus, a layered fog in pfVolFog can virtually be used on any machine.

Creating Layered Fog

A pfVolFog is not part of the scene graph; it is created separately by the application process. Once created, elevation points of a layered fog can be specified by calling pfVolFogAddPoint() or pfVolFogAddColoredPoint() repeatedly. The fog initialization is completed by calling pfApplyVolFog().

Example 6-2. Fog initialization Using pfVolFogAddPoint()


pfVolFog *lfog;
lfog = pfNewVolFog(arena);
pfVolFogAddPoint(lfog, elev1, density1);
pfVolFogAddPoint(lfog, elev2, density2);
pfVolFogAddPoint(lfog, elev2, density2);

pfApplyVolFog(lfog); 


Creating Patchy Fog

The boundary of a patchy fog is specified by pfVolFogAddNode(pfog,node),where node contains the surfaces enclosing the foggy areas. It is possible to define several disjoint areas in the same tree or by adding several different nodes. Note that each area has to be completely enclosed, and the vertices of the surfaces have to be ordered so that the front face of each surface faces outside the foggy area. The node has to be part of the scene graph for the rendering to work properly.

Example 6-3. Specifying Patchy Fog Boundaries Using pfVolFogAddNode()


pfVolFog *pfog;
pfNode   *fogNode;
pfog = pfNewVolFog(arena);
fogNode = pfdLoadFile(filename);
pfVolFogAddNode(pfog, fogNode);
pfAddChild(scene, fogNode);
pfApplyVolFog(pfog); 

Patchy and layered fog can be combined but only if layered fog has a uniform color; that is, it is specified using pfVolFogAddPoint() only.

Initializing a pfVolFog

The function pfApplyVolFog() initializes a pfVolFog. If at least two elevation points were defined, it initializes data structures necessary for rendering of a layered fog, including a 3D texture. Any control points defined afterward are ignored. If a node containing patchy fog boundaries has been added prior to calling pfApplyVolFog(), a patchy fog is initialized. Since function pfVolFogAddNode() only marks the parts of the scene graph that specifies the texture, it is possible to add additional patchy fog nodes, even after pfApplyVolFog() has been called.

Table 6-3 summarizes routines for initialization and drawing of a pfVolFog.

Table 6-3. pfVolFog Routines

Function

Action

pfNewVolFog()

Create a pfVolFog.

pfVolFogAddChannel()

Add a channel on which pfVolFog is used.

pfVolFogAddPoint()

Add a point specifying fog density at a certain elevation.

pfVolFogAddColoredPoint()

Add a point specifying fog density and color at a certain elevation.

pfVolFogAddNode()

Add a node defining the boundary of a patchy fog.

pfVolFogSetColor()

Set color of a layered fog or patchy fog.

pfVolFogSetDensity()

Set density of a patchy fog.

pfVolFogSetFlags()

Set binary flags.

pfVolFogSetVal()

Set a single attribute.

pfVolFogSetAttr()

Set an array of attributes.

pfApplyVolFog()

Initialize data structures necessary for rendering fog.

pfVolFogAddChannel()

Add a channel on which pfVolFog is used.

pfVolFogUpdateView()

Update the current view for all stored channels.

pfDrawVolFog()

Draw the scene with fog.

pfGetVolFogTexture()

Return the texture used by layered fog.

The attributes of a pfVolFog are listed in Table 6-4.

Table 6-4. pfVolFog Attributes

Attribute

Identifier

Default

Color

PFVFOG_COLOR

0.9, 0.9, 1

Density

PFVFOG_DENSITY

1.0

Density Bias

PFVFOG_DENSITY_BIAS

0

Maximum Distance

PFVFOG_MAX_DISTANCE

2000

Mode

PFVFOG_MODE

PFVFOG_LINEAR

Layered Fog Mode

PFVFOG_LAYERED_MODE

PFVFOG_LINEAR

Patchy Fog Mode

PFVFOG_PATCHY_MODE

PFVFOG_LINEAR

Resolution

PFVFOG_RESOLUTION

0.2

64 x 64 x 64

Texture Size

PFVFOG_3D_TEX_SIZE

 

The flags of a pfVolFog are listed in Table 6-5.

Table 6-5. pfVolFog Flags

Flag

Identifier

Default

Close surfaces.

PFVFOG_FLAG_CLOSE_SURFACES

1

Force patchy fog passes.

PFVFOG_FLAG_FORCE_PATCHY_PASS

0

Use layered patchy fog.

PFVFOG_FLAG_LAYERED_PATCHY_FOG

0

Use 2D texture.

PFVFOG_FLAG_FORCE_2D_TEXTURE

0


Updating the View

A pfVolFog needs information about the current eye position and view direction. Since this information is not directly accessible in a draw process, it is necessary to call pfVolFogAddChannel() for each channel at the beginning of the application. Whenever the view changes, the application process has to call pfVolFogUpdateView(). See programs in /usr/share/Performer/src/sample/apps/C/fogfly or /usr/share/Performer/src/sample/apps/C++/volfog for an example. If you do not update the view, the fog will not be rendered.

If the application changes the position of the patchy fog boundaries (for example, by inserting a pfSCS, pfDCS, or pfFCS node above the fog node) or the orientation of the whole scene with respect to the up vector (for example, the use of a trackball in Perfly), the fog may not be drawn correctly.

Drawing a Scene with Fog

To draw the scene with a fog, the draw process has to call pfDrawVolFog() instead of pfDraw(). This function takes care of drawing the whole scene graph with the specified fog. Expect the draw time to increase because the scene is drawn twice (three times if both patchy and layered fog are specified). In case of a patchy fog there may also be several full-screen polygons being drawn. You can easily disable the fog by not calling pfDrawVolFog().

Since boundaries of patchy fog are in the scene graph, do not use pfDraw() to draw the scene without fog; instead, use pfDrawBin() with PFSORT_DEFAULT_BIN, PFSORT_OPAQUE_BIN, and PFSORT_TRANSP_BIN.

A patchy fog needs as deep a color buffer as possible (optimally 12 bits per color component) and a stencil buffer. Use at least a 4-bit stencil buffer (1-bit is sufficient only for very simple fog objects). It may be necessary to modify your application so that it asks for such a visual.

Deleting a pfVolFog

A pfVolFog can be deleted using pfDelete(). In case of a layered fog it is necessary to delete the texture handle in a draw process. The texture is returned by pfGetVolFogTexture(). See the example in /usr/share/Performer/src/sample/apps/C/fogfly.

Specifying Fog Parameters

This section describes how to manage the various parameters for both layered and patchy fog.

Layered Fog

As mentioned earlier, a layered fog of a uniform color is specified by function pfVolFogAddPoint(), which sets the fog density at a given elevation. The density is scaled so that if the fog has a density of 1, the nearest object inside the fog that has full fog color is at a distance equal to 1/10 of the diagonal of the scene bounding box. The layered fog color is set by function pfVolFogSetColor() or by calling pfVolFogSetAttr() with parameter PFVFOG_COLOR and a pointer to an array of three floats.

A layered fog of nonuniform color is specified by function pfVolFogAddColoredPoint(), which sets the fog density and the fog color at a given elevation. The color set by pfVolFogSetColor() is then ignored.

The layered fog mode is set by function pfVolFogSetVal() with parameter PFVFOG_LAYERED_MODE and one of PFVFOG_LINEAR, PFVFOG_EXP, or PFVFOG_EXP2.

It is also possible to set the mode both for a layered and patchy fog at once by using parameter PFVFOG_MODE. The default mode is PFVFOG_LINEAR. The function of the mode parameter is equivalent to the function of the fog mode parameter of the OpenGL function glFog().

The size of a 3D texture used by a layered fog can be modified by calling pfVolFogSetAttr() with parameter PFVFOG_3D_TEX_SIZE and an array of three integer values. The default texture size is 64x64x64, but reasonable results can be achieved with even smaller sizes. The sizes are automatically rounded up to the closest power of 2. The second value should be equal to or greater than the third value. If 3D textures are not supported, a set of 2D textures is used instead of a 3D texture (the number of 2D textures is equal to the third dimension of the 3D texture). Every time the r coordinate changes more than 0.1, a new texture is computed by interpolating between two neighboring slices, and the texture is reloaded. The use of 2D textures can be forced by calling: pfVolFogSetFlags() with flag PFVFOG_FLAG_FORCE_2D_TEXTURE set to 1.


Note: Once a layered fog is initialized by calling the pfApplyVolFog(), changing any of the parameters described here will not affect rendering of the layered fog.


Patchy Fog

The density of a patchy fog is controlled by function pfVolFogSetDensity() or by using pfVolFogSetVal() with parameter PFVFOG_FOG_DENSITY. As in the case of a layered fog, the density of a patchy fog is scaled by 1/10 of the diagonal of the scene bounding box.

You can specify an additional density value that is added to every pixel inside or behind a patchy fog boundary using the function pfVolFogSetVal() with parameter PFVFOG_FOG_DENSITY_BIAS. This value makes a patchy fog appear denser but it may create unrealistically sharp boundaries.

The patchy fog color is set by function pfVolFogSetColor() or by calling pfVolFogSetAttr() with parameter PFVFOG_COLOR and a pointer to an array of three floats. If the blend_color extension is not available, patchy fog will be white.

The patchy fog mode is set by function pfVolFogSetVal() with parameter PFVFOG_PATCHY_MODE and one of PFVFOG_LINEAR, PFVFOG_EXP, or PFVFOG_EXP2.

It is also possible to set the mode both for a patchy and layered fog at once by using parameter PFVFOG_MODE. The default mode is PFVFOG_LINEAR.


Note: The parameters of a patchy fog can be modified at any time and they will affect the rendering of the subsequent frame.


Advanced Features of Patchy Fog

 A patchy fog can be animated by modifying the geometry of the fog nodes. When changing the content of geosets specifying the fog boundary, make sure that the geosets are fluxed and that the bounding box of each geoset is updated. In addition, function pfVolFogAddNode() has to be called every time the fog bounding box changes.

 If flag PFVFOG_FLAG_LAYERED_PATCHY_FOG is set, the layered fog is used to define the density of a patchy fog. The layered fog is then present only in areas enclosed by the patchy fog boundaries. Since layered fog is computed for the whole scene, it is important to set fog parameter PFVFOG_MAX_DISTANCE to a value that correspods to the size of the patchy fog area (for example, a diameter of its bounding sphere). Use function pfVolFogSetVal() to modify the maximum distance parameter.

The example in /usr/share/Performer/src/sample/C++/volfog illustrates the use of a layered patchy fog that is also animated.

Performance Considerations and Limitations

The quality and speed of patchy fog rendering can be controlled by calling pfVolFogSetVal() with parameter PFVFOG_RESOLUTION. The resolution is a value between 0 and 1. Higher values will reduce banding and speed up the drawing. On the other hand, high values may cause corruption in areas of many overlapping fog surfaces. The default value is 0.2, but you may use values higher than that if your fog boundaries do not overlap much.

The following are other performance considerations:

  • The multipass algorithms used for rendering layered and patchy fog may produce incorrect results if the scene graph contains polygons that have equal depth values. To avoid such problems, a stencil buffer is used during rendering of the second pass. You can disable this function by setting flag PFVFOG_FLAG_CLOSE_SURFACES to 0.

  •  By default, the multi-pass algoritm is applied only when the boundaries of a patchy fog are visible. This may cause undesirable changes of semi-transparent edges of scene objects when fog objects move into or away from the view. To force the use of the multi-pass algorithm, call pfVolFogSetFlags() with flag PFVFOG_FLAG_FORCE_PATCHY_PASS set to 1.

  • A layered fog is faster to render than a patchy fog; use a layered fog instead of a patchy fog whenever possible. Rendering of both types of fog together is even slower; so, you may try to define only one type.

  • Changing the fog mode does not affect the rendering speed in the case of a layered fog but rendering of a patchy fog is slower for fog modes PFVFOG_EXP and PFVFOG_EXP2. If you prefer using non-linear modes, try to use them only for layered fog and not for patchy fog.

  • You can speed up drawing of a patchy fog by reducing the size of the fog boundaries. In case of several disjoint fog areas, the size of a bounding box containing all boundaries will affect the draw time and quality. Try to avoid defining a patchy fog in two opposite parts of your scene. Try also to increase the value of resolution (if there are not too many overlapping fog boundaries) or reduce the patchy fog density.

  • If there is a lot of banding visible in the fog, try to choose a visual with as many bits per color component as possible. Keep in mind that a patchy fog needs a stencil buffer. You can also try to apply all techniques mentioned in the previous item—reducing the size of patchy fog boundaries, increasing resolution, or decreasing density.

  • If a patchy fog looks incorrect (the fog appears outside the specified boundaries) make sure that the vertices of the fog boundaries are specified in the correct order so that front faces always face outside the foggy area.

  • If you see a darker band in a layered fog at eye level, make sure the texture size is set so that the second value is equal to or greater than the third value.

OpenGL Performer has the following limitations in regards to fog management:

Layered fog:

  • The values of a layered fog are determined at each vertex and interpolated across a polygon. Consequently, an object located on top of a large ground polygon may be fogged a bit more or less than the part of the polygon just under the object.

  • A layered fog works fast with a 3D texture. Reloading of 2D textures during the animation can be slow.

Patchy fog:

  • The method does not work well for semitransparent surfaces. If your scene contains objects that are semitransparent or that have semitransparent edges, (for example, tree billboards or mountains in Performer Town), these objects or edges may be cut or may be fogged more than the neighboring pixels. Even if a semitransparent edge of a billboard is outside the fog, it will not be smooth.

  • All areas of a patchy fog have the same color.

  • A layered patchy fog is extremely sensitive to the size of the fog area and the density of the layered fog. Specifically, the fog values accumulated along an arbitrary line crossing the bounding box of the fog area should not reach 1.

  • A patchy fog needs a stencil buffer and the deepest color buffers possible.The rendering quality on a visual with less than 12 bits per color component is low unless the fogged area is very small compared to the size of the whole scene.

  • If the blend_color extension is not available, the patchy fog color will be white.