SGI Techpubs Library

IRIX 6.5  »  Books  »  Developer  »  
OpenGL Optimizer Programmer's Guide: An Open API for Large-Model Visualization
(document number: 007-2852-002 / published: 1998-06-09)    table of contents  |  additional info  |  download
find in page

Chapter 4. Rendering Appropriate Levels of Detail

Typically, a renderable object in an OpenGL Optimizer application is a csGeoSet that approximates a surface with a mesh of triangles. Whether you create the set of triangles with a tessellator (see Chapter 11, “Rendering Higher-Order Primitives: Tessellators”) or import a model that already has a set of triangles, you do not always want to render every triangle.

For example, a nearby object requires many more triangles to approximate a smooth appearance than the same object requires when further away, where it might cover only a few pixels. Rendering the same set of vertices in both cases is an unnecessary load on the graphics pipeline. It is also reasonable to use less detail if an object is moving, when geometric details are less important.

The following sections in this chapter discuss the simplification tools:

Overview of Simplification Tools

The simplifier classes each act on a csGeoSet, creating another csGeoSet with fewer triangles. OpenGL Optimizer does not provide tools to simplify multiple csGeoSets in a scene graph, because there are too many possible context-dependent outputs for a general tool.

For an example of how to traverse a scene graph and simplify all the csGeoSets in it, see the files /usr/share/Optimizer/src/apps/simplify.h and simplify.cxx. To understand the traversers there, see Chapter 12, “Traversing a Large Scene Graph.”

Simplifier Classes

The base class opSimplify describes mesh simplifiers that create varying levels of detail from a given csGeoSet. The levels of detail allow you to eliminate unnecessary triangles when rendering. opSimplify lets you derive your own simplifiers.

OpenGL Optimizer includes two opSimplify classes, opSRASimplify (see “Creating LODs: opSRASimplify”) and opLatticeSimplify (see “Rossignac Simplification Algorithm: opLatticeSimplify”), which provide different mesh-simplifying algorithms. The algorithm available through opSRASimplify is more sophisticated and provides more detailed control than is available through opLatticeSimplify, but the algorithm in opLatticeSimplify is faster. By extending the opSRASimplify class, application developers can define their own evaluation function, thereby changing the order of simplification. (See “Evaluation Function” for more information.)

In OpenGL Optimizer 1.1 and later, opSRASimplify has enhanced decimation functionality. It also supports all polygonal geoset types as inputs and produces indext tristrips as output. opSRASimplify also has a method that recalculates the normals of a given csGeoSet, based on the orientation of its triangles. The method has the following prototype:

csGeoSet* calculateNormals(csGeoSet* srsGset)

Levels of Detail

Typically you place a set of simplified objects below a level-of-detail node (a LOD). This node allows you to control the trade-off between interactivity and rendering accuracy; costly detail is drawn only when you can see it.

The children of an LOD node represent objects with varying degrees of resolution, that is, varying numbers of triangles. Typically, as the index of the child of an LOD increases, resolution decreases and rendering rate, therefore, increases. In an extreme case, you may not want to render an object at all. The tool for this operation is discussed in “Detail Culling”.

Cosmo3D provides csLOD scene-graph nodes to allow you to set the appropriate level of surface detail for a particular view during a draw action. A csLOD is a switch node that selects among its children based on the distance from the viewpoint. See the Cosmo 3D Programmer's Guide for more details.

When you decide where to place an LOD in a scene graph, consider how much “popping” you can tolerate as the LOD switches between children during rendering. For example, you could have one LOD near the root node, or many LODs, one above each object in a scene.

LOD Insertion

You can insert a csLOD node below a csGroup node by calling the csGroup methods to add or replace a child node. See the Cosmo 3D Programmer's Guide for more details.

OpenGL Optimizer provides the function addLODChild() as an example for inserting an LOD node. The function takes care of initializations required before you add a child with the method from csGroup. The function addLODChild() is in /usr/share/Optimizer/src/apps/opoptimize/addLOD.cxx.

The class opMergeScenes lets you combine entire scene graphs that differ only in the levels of detail in their csGeoSets.

opSimplify: Base Class for Adding Level-of-Detail Nodes

The functions in the opSimplify class are not implemented; they are effectively virtual functions.

A simplifier takes a scene graph as input and creates a modified scene graph that has csLOD nodes with simplified children. From the opSimplify base class you can derive your own simplifiers.

Class Declaration for opSimplify

The class has the following main methods:

class opSimplify
{
public:
opSimplify(void);
~opSimplify(void);

public:
// Which child in csGroup to simplify from
enum WhichSrcEnum
{
SRC,  // Usually LOD 0
PREV  // Usually coarsest LOD
};

void   simplifyGraph( csNode *rootNode, int relativeDepth,
                                opLengthUnits units, int threadId );

// Simplify from the source
void simplifyFromSrc( int lodLevel );

// Simplify from the previous level
void simplifyFromPrev( int lodLevel );

// Simplifier precision parameter settings
void setRelativePercent( int lodLevel, opReal percent);
void setAbsolutePercent( int lodLevel,opReal percent);
void setPolyCount( int lodLevel, int polyCount);
void setAbsoluteTol( int lodLevel,opReal Tol);
void setRelativeTol( int lodLevel,opReal Tol );

opReal getRelativePercent( int lodLevel );
opReal getAbsolutePercent( int lodLevel );
int getPolyCount( int lodLevel );
opReal getAbsoluteTol( int lodLevel );
opReal getRelativeTol( int lodLevel );
};

Methods in opSimplify

simplifyGraph() 


Defines the graph to be simplified.

simplifyFromSrc() 


Specifies that the simplifier work on the most detailed object.

simplifyFromPrev() 


Specifies that the simplifier work on the previous level of detail.

The remaining methods set and get parameters that characterize the simplification process.

Creating LODs: opSRASimplify

Using different levels of details (LODs) based on distance can increase the performance of your application: the more distant the geometry or mesh from the viewer, the more simplified the LOD required to accurately represent it.

opSRASimplify, derived from opSimplify, gives you control over the creation of LODs. By providing simplifying parameters, you can specify the kinds of vertices that are removed in the creation of LODs.

Simplifying Parameters

opSRASimplify::decimateGeoSet() makes a copy of a csGeoSet and returns a simplified version of it (using the indexed csTriSet format) based on the following parameters:

  • Percentage of the original model.

  • Three scaling factors in an evaluation function.

  • Whether edge vertices are simplified or not.

If the simplification parameters do not allow any vertices to be removed, the returned csGeoSet is the same as the input csGeoSet.


Note: For an example of using opSRASimplify to simplify all csGeoSets in a scene, see “Sample Traversal Function From the opoptimize Sample Application” and the file /usr/share/Optimizer/src/apps/opoptimize/simplify.cxx.

The following sections discuss the opSRASimplify parameters.

Percentage of the Original Model

The simplification algorithm removes vertices until a specified percentage of the original number of triangles remains. The simplification can terminate before that percentage is reached if the removal of vertices is no longer possible due to other criteria.

setPercent() and getPercent() set and get, respectively, the percentage of the original set of triangles that should remain in the simplified csGeoSet. Percentage values range from 0.0 to 100.0. By default, the percentage value is set to DEFAULT_SRASIMP_PERCENT.

Evaluation Function

You can define your own evaluation function by extending the opSRASimplify class and implementing the virtual calulateVtxEval method. If you have a set of data, like strain information that should influence the simplification, you should define your own calculateVtxEval() method and implement a new evaluation functions. For details on how to do this, see the opSRASimplify manpage. The evaluation function implemented in the opSRASimplify is:

vertexWeight = W 0 *distance + W 1 *normalDeviation + W 2 *curvature

Then, set the values of W0, W1, and W2 through the method opSRASimplify::setWeights().

Each of the weights, when it has a high value compared to the other weights, preserves different characteristics of the mesh.

  • A high W 0 value selectively removes from the mesh all those vertices for which the distance between the old vertex and the average plane of the simplified polygon is small, as shown in Figure 4-1.

  • A high W1 value preserves sharp features of the mesh.

  • A high W2 value preserves high curvature regions.

    Figure 4-1. Evaluation Function

    Figure 4-1 Evaluation Function

Effects of Simplification

The illustrations below illustrate the effects of decimateGeoSet() for specific simplification parameters.

Figure 4-2 shows the original model. The following three models are obtained after applying decimateGeoSet() to the original with the arguments:

Notice what happens to the horizontal edge of the wing.

Figure 4-2. Original Model Used for Simplification

Figure 4-2 Original Model Used for Simplification

Figure 4-3. Model Simplified using percent 50 weights 100

Figure 4-3 Model Simplified using percent 50 weights 100

Figure 4-4. Model Simplified using percent 50 weights 010

Figure 4-4 Model Simplified using percent 50 weights 010

Figure 4-5. Model Simplified using percent 50 weights 001

Figure 4-5 Model Simplified using percent 50 weights 001

Because the horizontal edge of the wing is a high curvature region, it is approximated by a lot of small triangles. Thus, simplification with weights 1 0 0 removes these vertices, and you can see small dents beginning to form in the model. Simplification with weights 0 0 1 preserves high curvature regions, and the edge of the wing in the model is left intact.

Simplifier Features

The Simplifier has a number of flags you can set that define

  • whether the csCoordSet should be shared between the original and the simplified version

  • whether the normals of the simplified mesh should be recalculated

  • whether the sharp vertices of the simplified mesh should be split to prevent black shaded triangles

Look at the opSRASimplify reference pages for a detailed description.

Simplification Errors

If you simplify a csGeoSet with two adjacent triangles that were originally specified independently, cracks can appear in surfaces rendered after simplification. The cracks result from shared vertices that are identical but have a double entry in the csCoordSet array. The simplifier might eliminate one of the triangles, but not the other. The effect is an apparent tear or crack in the surface.

The simplifier doesn't check for intersecting triangles upon removal of a vertex and retriangulation. Self-intersecting meshes are therefore a possible result of simplification.

Rossignac Simplification Algorithm: opLatticeSimplify

The class opLatticeSimplify provides methods to apply the algorithm developed by Jarek Rossignac to simplify a csGeoSet. The algorithm is less complex than that available in opSRASimplify, so it is faster, but it gives a somewhat lumpy simplification. This simplifier is most appropriate for low levels of detail.

The algorithm takes a grid in space and moves each vertex in a csGeoSet to the nearest grid point. If the grid is too coarse, the result will strongly reflect the grid structure.

Class Declaration for opLatticeSimplify

The class has the following main methods:

class opLatticeSimplify : public opSimplify
{
public:
opLatticeSimplify(float gridSpacing);
virtual ~opLatticeSimplify();

csGeoSet *simplify(csGeoSet *);

Methods in opLatticeSimplify

opLatticeSimplify() 


Specifies the grid spacing used in the simplification.

simplify() 

Applies the Rossignac simplification to the specified csGeoSet.

Merging Graphs With Differing Levels of Detail: opMergeScenes

If you simplify all the csGeoSets in a scene graph to varying levels of detail, and create graphs that otherwise retain the identical structure, you can place the differing levels of detail in one scene graph with the methods of opMergeScenes. The merged scene graph has the following structure:

  • Above nodes in the tree that you specify with a callback, the output graph is identical to one of the input graphs.

  • Below the specified nodes, a csLOD node is inserted, providing a switch between the corresponding lower subgraphs of the input graphs.

Before the subgraphs are inserted, they are reorganized to reflect their relative positions in space and to facilitate rapid cull traversals. See “Spatializing a Scene Graph: opGeoSpatialize”.

For an example of how to use an opMergeScenes, see the sample application mergeLODDemo.

Figure 4-6. Merging Two Scene Graphs

Figure 4-6 Merging Two Scene Graphs

Class Declaration for opMergeScenes

The class has the following main methods:

class opMergeScenes : public csAction
{
public:
typedef bool (*LODCallback)(csNode *);

opMergeScenes(int maxScenes,int goalMin,int goalMax,
                                         opMergeScenes::LODCallback f);
~opMergeScenes();

void addScene(csNode *scene);
csNode *done();

void setGoalMin(int n) ;
void setGoalMax(int n) ;
void setInsertLODFunc(LODCallback f) ;

int getGoalMin() ;
int getGoalMax() ;
LODCallback getInsertLODFunc() ;

Main Features of the Methods in opMergeScenes

addScene() 

Adds a scene graph to the list of graphs to be merged.

apply() 

Is inherited from csAction and causes a traversal of the graph, merging subgraphs according to the criteria specified by LODCallback(). The first scene graph included by calling addScene() is the graph in which the csLOD nodes and subgraphs are inserted.

done() 

Has the same effect as apply(), but returns the root node of the merged graph. You don't have to call apply() if you call done().

opMergeScenes() 


Specifies

  • the maximum number of scene graphs to merge

  • the range of the number of triangles in the spatialized subgraphs (see “Spatializing a Scene Graph: opGeoSpatialize”)

  • a callback function that specifies when a node should have an LOD node inserted below it that switches among the corresponding subgraphs of the input graphs

setGoalMin(), getGoalMin(), setGoalMax(), and getGoalMax() 


Set and get the parameters that control the spatialization routine. See “Spatializing a Scene Graph: opGeoSpatialize”.

setInsertLODFunc() and getInsertLODFunc() 


Set and get the callback function that determines which nodes should be parents of the inserted LOD node(s) and subgrdaph children. Example node-selection criteria are the depth of nodes from the top of the graph, the height of nodes from the bottom of the graph, or specific node names.


Note: The merged scene graph is created by modifying the first scene graph in the list created by calls to addScene(); if you have further use for any of the graphs that you merge, make copies before you merge them.


OpenGL Optimizer Programmer's Guide: An Open API for Large-Model Visualization
(document number: 007-2852-002 / published: 1998-06-09)    table of contents  |  additional info  |  download

    Front Matter
    About This Guide
    Part I. Getting Started
    Part II. High-Level Strategic Tools for Fast Rendering
    Part III. Specific Tools for Fast Rendering
    Part IV. Managing and Rendering Higher-Order Geometric Primitives
    Part V. Traversers, Low-Level Geometry Processing, and Multiprocessing
    Part VI. Utilities and Troubleshooting
    Part VII. Appendices
    Glossary
    Index


home/search | what's new | help