Lecture 5

Virtual Reality Modelling Language

5.1. Building complex shapes

We have already seen in the previous lecture how we can create shapes using the VRML Shape node. We have also seen how we can define a number of primitive shapes using the Shape node’s geometry field and using the nodes Sphere, Cone, Cylinder and Box. Now we will see how we can create more complex shapes by defining the points, lines and facets of objects ourselves. There are several types of more complex geometry nodes available in VRML, but we will only look at the PointSet, IndexedLineSet and IndexedFaceSet nodes.

Within almost any 3D application we will need to define shapes ourselves and these will normally be a lot more complex than combinations of primitive shapes can allow. All 3D shapes can be described as a series of points (often named vertices), lines joining these points (often called edges) and sets of lines which form the edges of a face (or facet). For example, a complex face shape is shown below in figures 1(a) to 1(d). Figure 1(a) shows all the points or vertices that make up the object. Figure 1(b) shows the lines joining these points, these lines represent the edges of the facets used to "fill in" the model (this type of view is termed a "wireframe" model). Figure 1(c) shows the plain shaded facets which make up the overall face shape. Finally, figure 1(d) shows the same model but with Gouraud shading applied to make a smoother and less angular model.

Figure 1(a). Vertices of a complex face model.

Figure 1(b). Lines joining the vertices.

Figure 1(c). Groups of points and lines defining individual facets.

Figure 1(d). Gouraud shading to produce a smoother appearance.

The basic concept for creating complex 3D models is simply to define a list of all the vertices in your model and then create instructions detailing which points to join to form lines, or which points to group to form facets. This is exactly how we create such models in VRML. The three nodes, PointSet, IndexedLineSet and IndexedFaceSet are all very similar and each produce output similar to figures 1(a), 1(b) and 1(c) respectively. Figures 2(a) and 2(b) show an application of complex modelling in the game Quake2 by Id software. Figure 2(a) shows the wire frame model for one of the female player characters, figure 2(b) shows the textured model. Note that the texture or "skin" used can be changed to alter the appearance of the model. Also, animation of the characters is performed by plotting vertex movement between frames. By moving the vertices, the lines and facets will also move. This allows the creation of complex animation sequences with very little processor/memory overheads.

Figure 2(a). Wireframe player model from Quake2.

Figure 2(b). Textured player model.

5.1.1. Point sets

A point set is purely just a list of 3D co-ordinates which determine the location of each point. When drawing a point set, the browser simply places one coloured pixel at each of these co-ordinate, the resulting effect looking something similar to a star field. This effect is shown in Figure 1(a) above. The PointSet node in VRML has a very simple definition :

PointSet { SFNode color NULL SFNode coord NULL }

The coord field is the main one we are interested in. This takes a single Coordinate node which provides a list of all the vertex co-ordinates in our point set. The color node allows us to specify an individual colour for every point in our point set if we desire. If we look at the Coordinate node definition we will see how simple it is:

Coordinate { MFVec3f point [ ] }

This node includes a single field, point, which we can specify a list of XYZ co-ordinates for all of the vertices in our point set. As an example, we will define the eight points necessary for each corner of a 2*2*2 cube (i.e. identical dimensions to a default Box node). Remember that we want the centre of this box to be at the origin (0, 0, 0) so we will allocate co-ordinates appropriately. The following table lists each of the co-ordinates for our points. Notice that we have also numbered each co-ordinate with an index from 0 to 7. This will be needed shortly.

Point index

Co-ordinate

0

1.0 1.0 -1.0

1

-1.0 1.0 -1.0

2

-1.0 -1.0 -1.0

3

1.0 -1.0 -1.0

4

1.0 1.0 1.0

5

-1.0 1.0 1.0

6

-1.0 -1.0 1.0

7

1.0 -1.0 1.0

The VRML file needed to produce our point set is as follows:

#VRML V2.0 utf8

Shape
{
  # a glowing red appearance
  appearance Appearance
  {
    material Material
    {
      emissiveColor 1.0 0.0 0.0
    }
  }
  
  geometry PointSet
  {
    coord Coordinate
    {
      # remember we can use commas to seperate our co-ordinates
      # they are ignored by the VRML browser
      point
      [
        # We will do the "front" corners first, 
        # going counter clockwise
         1.0  1.0 -1.0,             # point 0
        -1.0  1.0 -1.0,             # point 1
        -1.0 -1.0 -1.0,             # point 2
         1.0 -1.0 -1.0,             # point 3
        # Now the "back" corners
         1.0  1.0  1.0,             # point 4
        -1.0  1.0  1.0,             # point 5
        -1.0 -1.0  1.0,             # point 6
         1.0 -1.0  1.0              # point 7
      ]
    }
  }
}

The result of this code is shown in Figure 3 below.

Figure 3. Example of a PointSet node. Points have been numbered with their index.

5.1.2. Indexed line sets

Indexed line sets are purely an extension of the point set. This time, you specify both a list of points, and instructions on how to "join the dots" to create the line set. Line segments in VRML must always be straight lines, in between two points. You can create polylines by listing a number of points for a line to join. A list of three points will create a line containing two line segments, one from the first to second point, and another from the second to third point.

If we now look at the definition for the IndexedLineSet node, we will see it is a little more complex than the PointSet node :

IndexedLineSet { SFNode color NULL SFNODE coord NULL MFInt32 coordIndex [ ] MFInt32 colorIndex [ ] SFBool colorPerVertex TRUE }

The two fields we are currently interested in are coord and coordIndex, the remaining fields specify various colouring attributes for the points and lines which we will not be covering in this course. We have already seen what the coord field does, this specifies a list of co-ordinates for each of the vertices in our model. The coordIndex field specifies the instructions for creating the lines.

Remember how in the previous example we give an index number from 0 to 7 for each of the co-ordinates in our point set. The coordIndex field allows us to specify lists of these index numbers for each line we want to construct. Each individual line is terminated by the value "-1", and you can specify as many different lines as you like.

As an example we will define a wire frame cube using our previous point set. To do this we will specify 6 line polylines. Two polylines will be for the front and back squares, the remaining four will join these squares to form a cube. The table below shows the line index lists we will use. Note we are terminating each line with the value "-1". Also note in the case of the two square sections that we are closing the lines by putting the first index again at the end of the index list. Remember that we want four lines for our square sections and to have four lines we need to specify 5 vertices.

Line description

Vertex index list

Front square

0 1 2 3 0 -1

Back square

4 5 6 7 4 -1

Top right join

0 4 -1

Top left join

1 5 -1

Bottom left join

2 6 -1

Bottom right join

3 7 -1

So, the complete list of polylines (as we would put in the coordIndex field) is as follows. Note how we are using commas again to improve the readability of the list.

[ 0 1 2 3 -1, 4 5 6 7 -1, 0 4 -1, 1 5 -1, 2 6 -1, 3 7 -1 ]

So, the full code for our new example is :

#VRML V2.0 utf8

Shape
{
  # a bright red appearance
  appearance Appearance
  {
    material Material
    {
      diffuseColor 1.0 0.0 0.0
    }
  }
  
  geometry IndexedLineSet
  {
    coord Coordinate
    {
      # remember we can use commas to seperate our co-ordinates
      # they are ignored by the VRML browser
      point
      [
        # We will do the "front" corners first, 
        # going counter clockwise
         1.0  1.0 -1.0,             # point 0
        -1.0  1.0 -1.0,             # point 1
        -1.0 -1.0 -1.0,             # point 2
         1.0 -1.0 -1.0,             # point 3
        # Now the "back" corners
         1.0  1.0  1.0,             # point 4
        -1.0  1.0  1.0,             # point 5
        -1.0 -1.0  1.0,             # point 6
         1.0 -1.0  1.0              # point 7
      ]
    }

    # Now we specify the list of indices for our lines
    coordIndex
    [
      0 1 2 3 0 -1,           # front square
      4 5 6 7 4 -1,           # back square
      0 4 -1,                 # top right join
      1 5 -1,                 # top left join
      2 6 -1,                 # bottom left join
      3 7 -1                  # bottom right join
    ]
  }
}

The output from our example can be seen below in figure 4.

Figure 4. Wire frame cube created using an IndexedLineSet.

5.1.3. Indexed face sets

For most models we want to create we will need to give them a solid appearance. To do this we need to define the solid faces which will make up our model. We do this by specifying a face set - a list of all the facets in our model. In VRML this is done using the IndexedFaceSet node. Similar to the IndexedLineSet node, we define a list of vertices then create a list of all the facets. For each facet we specify the indices of the points which will make the corners of the facet. A facet must have 3 or more vertices and can be as simple or as complex as you like, with a few limiting conditions:

Looking at the two conditions above, it is not hard to see why triangular facets are the most commonly used geometric within fast 3D graphics engines. Triangles are always both planar and convex. Furthermore, any complex model or facet can be broken down and represented as triangular facets. Graphics hardware which is optimised for drawing and transforming triangles can also be used to increase performance greatly.

Figure 5(a) below shows an example of a convex facet constructed from five vertices. Figure 5(b) shows an illegal facet containing a convex section. In order to draw this shape the facet must be broken down into two or more convex facets. This is shown in figure 5(c) where the original concave facet is broken down into two 4-point convex facets. Figure 5(d) shows the same shape but broken into triangular facets. Remember, any single facet can be represented as a combination of triangular facets.

Figure 5(a). A single convex facet using five vertices.

Figure 5(b). An illegal facet with a concave section.

Figure 5(c). Splitting into two smaller facets.

Figure 5(d). Constructing from triangular facets.

If look at the definition for the IndexedFaceSet node (in the VRML specification) we can see it is a lot more complex than the IndexedLineSet. Only a subset of all the available fields are listed here :

IndexedFaceSet { SFNode coord NULL MFInt32 coordIndex [ ] SFFloat creaseAngle 0 SFBool convex TRUE SFBool solid TRUE SFBool ccw TRUE }

We have already seen how we specify a list of vertex co-ordinates using the coord field, the process is identical to both the PointSet and IndexedLineSet nodes. The coordIndex field in the IndexedFaceSet node is very similar to the same field in the IndexedLineSet node. Instead of specifying vertices to join with lines, we specify vertices that will make up each facet. Again, the value "-1" is used to show when each facet ends and we can use commas to help make the list more readable.

Now, when we were specifying a line we have a definite start and finish for the line, whereas with facets we do not. You can start specifying a facet from any vertex on it, but you must then either go clockwise or counter clockwise round the facet specifying the remaining vertices. One thing of note is that we do not need to "close" the facet by specifying the first vertex again at the end - the VRML browser will do this for us as all facets must be closed. One thing we need to know is which is the "front" of each face. When displaying 3D models, most graphics engines will avoid having to draw the back of facets unless absolutely necessary. By convention (again!), if we specify the vertices in a counter clockwise manner then we will be looking at the front of the facet. You should follow this convention whenever possible, even though VRML offers an alternative. A good way to remember what your doing is to use the right hand grip rule (the one we use to figure out the sign of axis rotations). By pointing the thumb out from the front of a face, the direction of curl of your fingers will tell you which way to specify the co-ordinates. It is very easy to get confused and make a mistake, so think it out first.

So, as an example we will define the six faces for our cube shape. Note that it doesn’t matter which vertex we start on when specifying a facet as long as we go counter clockwise. So we could specify either [ 0 1 2 3 -1 ] or [ 2 3 0 1 -1 ] for the front face of the cube - they both are identical.

Face description

Vertex index list

Front face

0 1 2 3 -1

Back face

7 6 5 4 -1

Top face

1 0 4 5 -1

Bottom face

2 6 7 3 -1

Left face

1 5 6 2 -1

Right face

0 3 7 4 -1

So, the complete facet list which we would put in the coordIndex node would be:

[ 0 1 2 3 -1, 7 6 5 4 -1, 1 0 4 5 -1, 2 6 7 3 -1, 1 5 6 2 -1, 0 3 7 4 -1 ]

We showed a few extra fields in the IndexedFaceSet definition above, we will now exaplin these briefly:

Now, we will look at the full code for our cube example :

#VRML V2.0 utf8 Shape { # a bright red appearance appearance Appearance { material Material { diffuseColor 1.0 0.0 0.0 } } geometry IndexedFaceSet { coord Coordinate { # remember we can use commas to seperate our co-ordinates # they are ignored by the VRML browser point [ # We will do the "front" corners first, # going counter clockwise 1.0 1.0 -1.0, # point 0 -1.0 1.0 -1.0, # point 1 -1.0 -1.0 -1.0, # point 2 1.0 -1.0 -1.0, # point 3 # Now the "back" corners 1.0 1.0 1.0, # point 4 -1.0 1.0 1.0, # point 5 -1.0 -1.0 1.0, # point 6 1.0 -1.0 1.0 # point 7 ] } # Now we specify the list of indices for our facets coordIndex [ 0 1 2 3 -1, # front face 7 6 5 4 -1, # back face 1 0 4 5 -1, # top face 2 6 7 3 -1, # bottom face 1 5 6 2 -1, # left face 0 3 7 4 -1 # right face ] } }

Figure 6(a) below shows the result of our IndexedFaceSet example. Vertex numbers have been added in after rendering. Notice how the only difference between the IndexedFaceSet cube and the cube created using the primitive Box node (shown in figure 6(b))is the way the objects are shaded. By using an IndexedFaceSet we can have far greater control over the appearance and lighting attributes of an object. In fact, all the primitive shapes, Box, Sphere, Cylinder and Cone are all created from an IndexedFaceSet but with the more complex fields hidden away. We could easily vary the lighting properties of the cube in figure 6(a) to match identically the cube in figure 6(b).

Figure 6(a). Cube created using an IndexedFaceSet node.

Figure 6(b). Cube created using the Box node.

5.2. Controlling detail

We have now seen how we can create objects using both primitive shapes and more complex models. We will now look at how we can control that detail and display only as much as we need, when we need it. Having a world full of incredibly detailed objects may make it appear very realistic, but it will cost significantly in performance. The more detailed and complex a scene then the slower the refresh rate which makes for a very sluggish virtual environment. However, some objects in the scene will be further away than others and do not need complex and highly detailed models at this distance. The user will probably not be able to distinguish any intricate features anyway, so drawing them is a waste of resources.

One technique to help control the complexity of a scene and maintain a balance between realism and performance is level of detail (LOD) control. Using LOD you can specify a number of different models for the same object. These models would of varying complexity and the VR software would swap between them depending on how far the user is away from the object. If the user is close then the high detailed object will be displayed, if they are far away then a low detail object will be used. The trick to making LOD work is to create the models and define the replacement distances in such a way that the switch between models is as subtle as possible.

In VRML we can use the LOD node to specify which models we will use and at what distances we would like swapping to occur. If we look at the definition of the LOD node below we can see it has only three fields. The first field, level, is the list of models which we will be using. These are specified similar to the children field of Group and Transform nodes. If we have 3 models for our use then this field should only contain 3 nodes. We can use the Group node to encapsulate a model into a single node if necessary. These nodes must be specified in the order highest detail first. The range field specifies the distances at which swapping will occur. We only need to specify the transition points between models, so if we have 3 models we will need two transition distances. One for the transition between the high and mid detailed models, and the second for the transition between the mid and low detailed models. Finally, the center field simply specifies the co-ordinate to be used as a distance measure, by default this will be the centre of each of the models used. The distance is calculated between the viewer’s location and this center co-ordinate and compared against the range values to determine which model to use.

LOD { MFNode level [ ] MFFloat range [ ] SFVec3f center 0 0 0 }

We will use the following code as an example. This defines an LOD node with three shapes in it. The first (high detailed model) is a red cone, the second (mid detailed model) a green sphere and the third (low detailed model) a blue cylinder. We have defined the two transition points to be at 4 and 8 VRML units respectively.

#VRML V2.0 utf8 LOD { range [ 4, 8 ] level [ # First the high detailed model Shape { appearance Appearance { material Material { diffuseColor 1 0 0 } } geometry Cone { } } # Now the mid detailed model Shape { appearance Appearance { material Material { diffuseColor 0 1 0 } } geometry Sphere { } } # Finally the low detailed model Shape { appearance Appearance { material Material { diffuseColor 0 0 1 } } geometry Cylinder { } } ] }

Figures 7(a-c) show the result of the above code. Figure 7(a) shows the red cone as the high detailed model. Now, as we move our viewpoint back away from the object, when we reach a distance of 4 VRML units the model swaps to the green sphere as shown in figure 7(b). Finally as we reach a further distance of 8 units from the object then it swaps to the low detailed blue cylinder as shown in figure 7(c). These transitions are reversed as we approach the object again.

Figure 7(a).
High detailed model.

Figure 7(b).
Mid detailed model.

Figure 7(c).
Low detailed model.

5.2.1. A more realistic example

The example mentioned above highlights the changes between objects as the distance to the viewer increases. However, the aim of LOD culling is the exact opposite, we attempt to make the transition as smooth and subtle as possible. The following example shows a more realistic application of the LOD node. We will create a model of a pencil with three levels of detail. The first model (high detailed) will use a single IndexedFaceSet node to create a realistic, hexagonal bodied pencil. The second model (mid detailed) will simplify the model to just a cylinder for the body and a cone for the pencil tip. The final model (low detailed) will be just a single box with the same dimensions and appearance as the pencil body. We will use an LOD node to swap between the three models as necessary.

The following code will be placed in a file called "pencil-hd.wrl" and will be our high detailed model for the pencil. Note that there is some code in here which has not been covered in the lectures.

#VRML V2.0 utf8 # This file defines a high detailed pencil using an IndexedFaceSet # node the pencil body will be hexagonal and we will use # vertex colouring to blend the pencil tip to # black at the end point. # We will wrap our pencil into a group object DEF Pencil-HD Group { children Shape { geometry IndexedFaceSet { # Firstly we define all the vertices for our model coord Coordinate { point [ # pencil end (not tip) 8.00 0.00 0.40, # point 0 8.00 0.35 0.20, # point 1 8.00 0.35 -0.20, # point 2 8.00 0.00 -0.40, # point 3 8.00 -0.35 -0.20, # point 4 8.00 -0.35 0.20, # point 5 # Pencil end (just before tip) -6.60 0.00 0.40, # point 6 -6.60 0.35 0.20, # point 7 -6.60 0.35 -0.20, # point 8 -6.60 0.00 -0.40, # point 9 -6.60 -0.35 -0.20, # point 10 -6.60 -0.35 0.20, # point 11 # Pencil tip -8.00 0.00 0.00 # point 12 ] } # Now we define all of the facets of the pencil coordIndex [ # flat end of pencil 5 4 3 2 1 0 -1, # sides of pencil from end to just before tip 0 1 7 6 -1, 1 2 8 7 -1, 2 3 9 8 -1, 3 4 10 9 -1, 4 5 11 10 -1, 5 0 6 11 -1, # Now the pencil tip 6 7 12 -1, 7 8 12 -1, 8 9 12 -1, 9 10 12 -1, 10 11 12 -1, 11 6 12 -1 ] # yes this model is solid solid TRUE # We want the pencil edges to be distinct creaseAngle 3.14 # This next code hasn’t been covered in the lectures # We'll explain what its doing anyway # We want to specify a colour for each of the vertices # as opposed to specifying a colour for each face. colorPerVertex TRUE # We define a list of all the colours we will use # We only need three here. color Color { color [ 0.80 0.00 0.00, # colour 0, red (pencil body) 1.00 1.00 0.00, # colour 1, yellow (pencil tip) 0.00 0.00 0.00, # colour 2, black (pencil lead) ] } # Now, for each of the vertices in each of our faces, # we specify a colour index from the above list of colours # The format must be exactly the same as our coordIndex field. colorIndex [ # flat end of pencil (all red) 0 0 0 0 0 0 -1, # sides of pencil from end to just # before tip (all red again) 0 0 0 0 -1, 0 0 0 0 -1, 0 0 0 0 -1, 0 0 0 0 -1, 0 0 0 0 -1, 0 0 0 0 -1, # Now the pencil tip # Start off yellow and blend to black at the pencil tip 1 1 2 -1, 1 1 2 -1, 1 1 2 -1, 1 1 2 -1, 1 1 2 -1, 1 1 2 -1, ] } } }

Figure 8 below shows a picture of our new pencil model. Note that we specified a high creaseAngle value (3.14 radians = 180 degrees) so the edges on the pencil appear distinct. If we omitted this value then the VRML browser would blend the colours to give a smooth, rounded appearance.

Figure 8. A high detailed pencil created using a single IndexedFaceSet.

Now, we create the mid-detailed model. This will be placed in the file "pencil-md.wrl" and will consist of a red cylinder for the pencil body and a yellow cone for the tip. The following code creates this model.

#VRML V2.0 utf8 # This file defines a medium detailed pencil using a Cone and # a Cylinder object. DEF Pencil-MD Transform { # All shapes will be built by default facing upwards. # We will construct our pencil like this then rotate it # to match the orientation of the high detail model. rotation 0 0 1 1.5708 children [ # First the pencil body. Transform { # Translate the body down to accomodate the pencil tip. translation 0.0 -0.7 0.0 children Shape { appearance Appearance { material Material { # The same colour as the high detailed pencil body diffuseColor 0.8 0.0 0.0 } } # Dimensions of the pencil body - we have left room for # the pencil tip geometry Cylinder { height 14.6 radius 0.4 } } } # Now we add the pencil tip Transform { # Translate it up to fit on the end of the body translation 0.0 7.3 0.0 children Shape { appearance Appearance { material Material { # A light yellow colour diffuseColor 0.8 0.8 0.0 } } geometry Cone { height 1.4 bottomRadius 0.4 } } } ] }

Figure 9 below shows the model created from the above code. Notice the lack of detail compared to the previous model.

Figure 9. A medium detailed pencil model created using a cone and cylinder.

Finally, we will create a low detailed pencil model which will consist of nothing more than a red box with similar dimensions to the previous models. The code for this is placed in the file "pencil-ld.wrl" and is shown below.

#VRML V2.0 utf8 # This file defines a low detailed pencil using only a # Box node to represent the pencil body # We will group our shape together to be tidy DEF Pencil-LD Group { children Shape { appearance Appearance { material Material { # the same body colour as the other models diffuseColor 0.8 0.0 0.0 } } # A simple box shape with similar dimensions to the # high and mid detailed pencils. geometry Box { size 16.0 0.8 0.8 } } }

Figure 10 shows this simple model. At this distance you couldn’t easily guess it was supposed to be a pencil.

Figure 10. A low detailed model of a pencil using a single Box node.

Now that we have created each of the high, mid and low detail pencil models, we will create a file which will bring them together using an LOD node. Because we have placed each of the models in its own file we can simply inline these models as needed. The following code defines the LOD node. Note we are listing the models in order highest detail to lowest detail, and that we have set the transition distances to 30 and 60 VRML units respectively.

#VRML V2.0 utf8 # Create the LOD node and we also give it a name DEF Pencil LOD { range [ 30 60 ] level [ # Highest detail model first Inline { url "pencil-hd.wrl" bboxSize 16.0 0.8 0.8 } # Medium detail model Inline { url "pencil-md.wrl" bboxSize 16.0 0.8 0.8 } # Low detail model Inline { url "pencil-ld.wrl" bboxSize 16.0 0.8 0.8 } ] }

The following sequence of images, shown in Figures 11(a-e), show the pencil object as the viewpoint moves steadily away from it. Figures 11(a) and 11(b) still show the high detailed model. Figure 11(c) and 11 (d) then show the mid-detail model after the first transition distance is passed. Finally, figure 11(e) shows the low detail model after the second transition distance is exceeded. Note how the difference in appearance between successive frames is only slight, particularly between the high and mid detailed models. The low detail model stands out a bit more as the yellow tip is no longer visible.

Figure 11(a).
The high detail model.

Figure 11(b).
Moving away from the pencil, still showing the high detail model.

Figure 11(c).
Further away and the medium detail model is now used.

Figure 11(d).
Still using the medium detail model.

Figure 11(e).
The low detail model is now used for any greater distances.


This page is maintained by Peter Young, please send any comments to (peter.young@durham.ac.uk).

Last updated: Tuesday 17 February, 1998.