Lecture 4

Virtual Reality Modelling Language

4.1. Introduction

The remaining three lectures of this course (lectures 4, 5 and 6) will concentrate on creating virtual worlds or 3D scenes and using the Virtual Reality Modelling Language (VRML) as a basis for examples. Lectures 4 and 5 will introduce VRML and the basic techniques and features which it provides. The last lecture of the course, lecture 6, will deal with constructing an example virtual world using VRML and the techniques we will have learnt. So, lectures 4 and 5 will contain simple examples of how certain techniques are performed, lecture 6 will have a single example of how we can combine these techniques to create a virtual world or 3D scene.

These lectures will not require any of the mathematical background we covered in lectures 2 and 3, though a thorough understanding of three dimensional co-ordinate systems and transformations in 3-space will be needed. VRML takes care of how we translate, rotate or scale objects, but we still need to know why and what values or transformations are necessary to get the results we want.

As mentioned in the first lecture of this course, it should not be necessary to buy any books for this course and this is especially so in the case of VRML. Everything you need, as far as the course is concerned, should be covered in the lecture notes and lecture slides. However, if you want to learn more about VRML or want to become proficient in its use then I recommend the following book (repeated from lecture 1 notes) :

This book is concerned purely with VRML (version 2.0) and is a great one to learn how to create virtual worlds and objects. It covers all of the modelling techniques supported by VRML2 and gives excellent explanations as to how to use them. It only briefly covers more advanced topics such as prototyping and scripting. The book does repeat things a lot which makes it a bit tedious to read cover to cover, but means its an excellent reference for the language. This book was also used as the basis for these lecture notes and many of the examples are the same.

VRML has a very large network of support in the WWW community and there are hundreds of web pages devoted to tutorials, examples and resources. A few good starting points are:

http://www.sdsc.edu/vrml/

http://vrml.sgi.com/

http://vwww.com/

4.2. VRML

VRML (often pronounced vermel) is the current standard for specifying, viewing, manipulating and exploring virtual worlds and 3D graphics on the Internet. Using VRML it is possible to create a network of linked virtual worlds which can be traversed and explored much in the same way you explore a normal HTML page through the use of hyperlinks. During the conception of VRML, it was originally termed the Virtual Reality Mark-up Language which was following in the footsteps of HTML (Hyper Text Mark-up Language). This name was soon dropped in favour of Virtual Reality Modelling Language which is much more representative of what VRML actually is. The VRML language specification is currently in version 2.0, which is vastly different to the initial 1.0 version. The very latest specification for VRML is now termed VRML97 (wonder where this inspiration came from?) which is basically the VRML 2.0 specification but with some minor document and functional changes.

The current specification for VRML (which incidentally is a great reference for creating VRML scenes) can be found at the VRML Consortium web pages:

http://www.vrml.org/

VRML is not a programming language as such, it is a scene description language and deals with statically describing all the objects and attributes which make up a 3D scene. VRML does, however, support dynamic features, scripting and complex user interaction by allowing an interface to either JavaScript or VRMLScript code (both practically identical). So, you can use VRML to define your 3D world then either JavaScript or VRMLScript to control how the world behaves or how it reacts to the user’s interactions. We will be concentrating on the static aspects of VRML, but will also briefly touch upon the use of JavaScript.

There are many graphical tools which can be used to create VRML worlds, however we will be concentrating on creating VRML files from the pure source code alone.

4.3. Making a start

VRML is a text-based scene description language. To create our 3D worlds we write a text file which specifies the structure and organisation of our VRML world. The VRML file is effectively a blueprint for the construction of our world, it specifies structure, contents, materials, etc. The VRML viewer or browser then uses this blueprint to produce a graphical representation of our world which we can explore freely. Because VRML is text based, it means you can create your 3D worlds using nothing more complex than a text editor. However, a VRML viewer is necessary to actually see the result of your work and these are more platform dependant.

4.3.1. The VRML file

VRML filenames are normally specified with a ".wrl" extension. This extension is often pronounced as "dot whirl" or "dot world". A VRML file can contain four main types of component :

Out of these four, the only required item to make a VRML file is the VRML header. Prototypes, routes and scripts are more advanced components of a VRML scene, and we will only be mentioning these very briefly.

Other components which a VRML scene can include are:

As previously mentioned, the only really required item to create a VRML file is the VRML header. Without this header a VRML browser may not recognise the file as being VRML code. One thing also to remember when writing a VRML scene is that VRML is case sensitive so it does matter whether you use upper or lower case characters. Finally, whitespace is ignored in VRML files so you can format your text files however you like, just remember to make it clearly readable. The more structured your file then the more readable it will be for you or another reader.

4.3.2. The VRML Header

The VRML header is a single line comment which must be on the first line of any VRML file. The exact format for the header is :

#VRML V2.0 utf8

Note that this line must be exactly as printed above for the browser to recognise the file as VRML code. We will now explain what this line actually specifies.

If we are just sticking to plain VRML 2 files (which we are in this lecture), then all you need to do is copy the above line exactly into the first line of your VRML file.

4.3.3. Comments

As previously mentioned, a hash character ‘#’ denotes the start of a comment. Anything after this character up to the end of the current line is ignored by the VRML viewer. The exception to this rule is the VRML header which is a special form of comment.

Comments are extremely useful when creating a VRML file, they allow you to annotate points in your file with your own descriptions, ideas and intentions. If you use plenty of comments then when you or someone else comes to look at your file some time later, you will have a much easier time trying to understand it. You should comment your files well!

4.3.4. Nodes

Nodes are the building blocks of a VRML scene. Essentially an individual node is any form of construct in a VRML file and can describe shapes, groups, transformations, lights, materials, timers, sensors, etc. Nodes generally consist of :

For example, the following Cone node shows a node of type "Cone" and specifying values for two fields, bottomRadius and height. If you look up the Cone node in the VRML specification, you will notice that it has two additional fields which we have not specified here. These fields have default values which will be used instead.

Cone
{
   SFInt32 bottomRadius 1.0
   SFInt32 height 2.0
}

All of the nodes and fields within VRML follow the same naming convention. Node names all begin with a capital letter and fields all begin with a lower-case letter. Both node and field names capitalise the first letter of each word within their name. For example the node type IndexedFaceSet has capital I, F and S.

4.3.5. Fields and field values

Fields define the attributes of a node. Most nodes will have a number of fields, normally each having a default value. This default value will be used unless you specify an alternative in your VRML file. The order in which you specify the fields of a node is not important. In the above example we could, if we liked, specify height before bottomRadius. To find the default values for a node’s fields you will have to look at the node definition in the VRML specification or a good textbook. I would suggest at least having a look at the VRML specification (URL given earlier), it is very readable and is great for looking up node types to find their field names and default values.

It is often good practice to specify important field values, even if they are the same as the default values. It helps a reader to see exactly what is happening without having to constantly refer to the VRML specification. For example, in the Cone node above, the field values given are identical to the default values yet it makes much more sense when these important fields are explicitly stated.

VRML uses a Cartesian co-ordinate system, however the units used in VRML do not correspond to any real world measurements such as centimetres or inches. YOU must decide what your VRML units will represent. For example, if your modelling something fairly small such as a pencil, you may decide that 1 VRML unit will correspond to 1 centimetre and build your model accordingly. Or, if your modelling a building you may decide that 1 VRML unit will correspond to 1 metre. Also remember that you can always have fractional units if necessary, so your model is not limited by the granularity of the co-ordinate system.

4.3.5.1. Field types

Fields within nodes can be used to specify any attribute of a particular node, for example they can specify height, length, colour, size, time, text, other nodes, or even lists of attributes. For this reason, each field is of a particular field type, for example SFBool, SFRotation, MFNode and MFInt32. The naming convention for these types always begins with either SF or MF. Types beginning with SF represent single value types, where only one item is specified such as a colour or a single number. Types beginning with MF represent multiple value types which may contain any number of values. For example, a list of co-ordinates describing an object’s path during animation. MF field values are usually specified as a list of values enclosed within square braces, [ ]. You can also use commas to separate values if you desire - the VRML parser will ignore them but they may make your text more readable.

There are a lot of possible field types in VRML, though they are mostly self descriptive. For these lectures our only concern for field types is so we know what sort of value to give a particular field. The following table shows the field types available in VRML. One thing you should note is the American spelling of nodes and fields such as SFColor or center. If you use the English spelling then VRML will not recognise it as a correct keyword.

Single value fields (SF)

Multiple value fields (MF)

SFBool

 

SFColor

MFColor

SFFloat

MFFloat

SFImage

 

SFInt32

MFInt32

SFNode

MFNode

SFRotation

MFRotation

SFString

MFString

SFTime

 

SFVec2f

MFVec2f

SFVec3f

MFVec3f

4.3.6. Defining and using node names

To make creating VRML scenes more convenient and more efficient, you can define a name to refer to any node in your VRML file. Once a node has a name you can then use that node again later in your file without having to retype the node. For example, if you created a car object, you could name the node which encompassed your car with something like "MyCar". Later in the file you could create another car by simply referring to that name. Using defined names also makes it very easy to change attributes of your world very quickly. Say your two cars were originally red, but you think blue would look better. You only have to change the colour in the original definition of "MyCar" and all the other cars you made will change colour as well.

A VRML file can contain any number of defined node names, but you cannot define the same node name twice. Names are also case-sensitive so "MyCar" and "mycar" are considered different names. Node names must also not be the same as any of the VRML keywords or any of the predefined node types. So, defining the name "Cone" would be illegal as a node of type "Cone" already exists.

In order to define a node name, you use the keyword "DEF" before the node definition. So, to assign the name "PointyThing" to our Cone example earlier we would use:

DEF PointyThing Cone
{
   bottomRadius 2.0
   height 1.0
}

The name "PointyThing" can now be used anywhere in our file where we could normally put a Cone object. You can define a name to represent any type of node, not just shapes, but you can only use your defined name where you could normally use a node of the same type. So using "PointyThing" where a number is expected is not legal.

To use your defined names, you simply put the "USE" command followed by your node name. So, we will use a Shape node as an example. A node of type Cone is a valid value for the geometry field.

Shape
{
   geometry USE PointyThing
}

4.4. Building shapes

If we want to add any kind of shape or visible object into our VRML scene we have to use the Shape node. This node defines both the geometry and appearance of the shape we want and is nothing more than a wrapper for these two properties. If you look at the VRML specification, you will see that the Shape node has only two fields, appearance and geometry. The nature of these two fields is fairly self explanatory.

So, lets build our first VRML world. We will start off with a very simple Cone, nothing else. We will not even specify any attributes for the cone, or an appearance for it. These will be set by the default values. One thing to note is that the default appearance of objects is dependent on the VRML viewer you use. Normally the appearance defaults to a plain, bright white colour. However, the viewer we will be using for these lectures (GLView by EMD Enterprises) decided a light pink/purple was better. So, the code for our file is as follows:

#VRML V2.0 utf8

Shape
{
   geometry Cone
   {
   }
}

This results in the following scene when loaded into our VRML viewer :

Figure 1. Our first VRML scene

One thing to note, usually the background for a VRML scene is black, however, seeing as these notes are intended to be printed out I have selected a white background. This will be the same for all the examples and figures used in these lectures.

Now, we will try changing our file a little by modifying the attributes for our cone. Lets make the base smaller. We can do this by changing the bottomRadius field from its default of 1.0 to a new value of 0.5. Note that we have not specified a new value for the height field, so this will remain at a default height of 2.0 units. The code for our new example is:

#VRML V2.0 utf8

Shape
{
   geometry Cone
   {
      bottomRadius 0.5
   }
}

Which results in a new scene as shown in figure 2, below.

Figure 2. Our new scene with a slightly slimmer cone.

Hopefully you will now be comfortable with the concept of defining a VRML file, using nodes and fields, and how we can change the values of fields to affect our 3D scene. The geometry field in a Shape node can take a large number of predefined nodes. VRML supports 4 primitive shapes (Box, Sphere, Cylinder and Cone) and a large number of more complex nodes. The full list of valid nodes for the geometry field is : Box, Cone, Cylinder, Sphere, ElevationGrid, Extrusion, IndexedFaceSet, IndexedLineSet, PointSet, and Text. We will be looking at each of these in more detail later.

4.5. Selecting materials

As previously mentioned, the Shape node has two fields, geometry and appearance. We have already mentioned how we can use the geometry field to specify what type of shape or object we are creating. Now we will look at how we can control the appearance of these shapes by specifying a value for the appearance field. This field controls all aspects of how the shape will look, including colour, luminance, transparency or textures. These can be broken down into two categories, textures and materials.

Before we look at the VRML code in detail, we should learn more about how colours are specified and how the VRML viewer generates the picture we see.

4.5.1. Understanding appearance

The VRML viewer uses many parameters which you specify in order to create the desired appearance for your objects and your world. There are four main factors which we should consider for VRML: Colour; shading; luminance and transparency. Understanding how these work within the VRML viewer will enable you to gain more precise control of the appearance of any materials you use. We will explain each of these now.

4.5.1.1. Colour

Colour in the everyday world is judged by a very imprecise naming or descriptive system. Terms such as brick-red, deep-blue or charcoal are frequently used to describe the colour of some object. While these names are excellent for giving a mental impression of a particular colour, we need a much more accurate and concise method for specifying colours within our virtual world. A common method for specifying a particular colour within computer graphics is to give an RGB value. This value is actually a set of three numbers, from 0.0 to 1.0, specifying the amount of Red, Green and Blue which a particular colour contains. By selecting appropriate proportions of each you can create any colour you like. More importantly, you can refer to this colour concisely and accurately enabling you to recreate it identically by using its RGB value.

An RGB value is always stated in the order Red, Green then Blue. So the value [0.0 1.0 0.0] would be pure green. To obtain colours such as black (i.e. the absence of light) you set R, G and B all to zero. To obtain white (i.e. pure light containing all colours) you set R, G and B all to one. The following table lists some common colours:

Colour

Red

Green

Blue

Pure red

1.0

0.0

0.0

Pure green

0.0

1.0

0.0

Pure blue

0.0

0.0

1.0

Bright white

1.0

1.0

1.0

Pure black

0.0

0.0

0.0

Yellow

1.0

1.0

0.0

Cyan

0.0

1.0

1.0

Magenta

1.0

0.0

1.0

Light grey

0.75

0.75

0.75

Medium grey

0.5

0.5

0.5

Dark grey

0.25

0.25

0.25

Dark red

0.5

0.0

0.0

Dark green

0.0

0.5

0.0

Dark blue

0.0

0.0

0.5

Another method for specifying colours is by using HSV values. This is essentially the same principal as using RGB colours, but it uses a different method for measuring each colour. Similar to RGB colours, HSV colours are specified as three values from 0.0 to 1.0. HSV stands for hue, saturation and value, and the three digits you specify are the values for each of these attributes.

The hue value selects a colour from a red-green-blue-red colour wheel and is often what you refer to when describing a colour. For example, a green box has a green hue. The saturation part of an HSV colour selects the amount of white which must be "mixed" with the colour. A saturation of zero leaves a brilliant colour, while increasing the saturation produces a more pastel or washed-out colour. Finally, the value of an HSV colour sets the brightness of the colour. A value of 0.0 has no brightness and will produce black, irrespective of the hue and saturation. A value of 1.0 produces a full-bright colour.

Colours within VRML are specified as RGB values. However, HSV colours are extremely useful for smoothly changing from one colour into another. VRML uses HSV colours behind your back when told to animate a colour from one value into another. You do not need to know how to specify or use HSV colours for this course, just be aware that they exist and that they are used widely in computer graphics.

4.5.1.2. Shading

In the real world, if you shine light upon a shape such as a ball or sphere, the sides facing the light appear bright while the sides facing away from the light become darker. The brightness of any point on an object depends on how much it faces the light source. These variations in brightness are termed shading. Using shading provides a heightened sense of depth and gives a more realistic appearance to 3D objects in our virtual world.

Figure 3(a) shows a VRML sphere which has been shaded by the viewer to give a sense of depth. Figure 3(b) shows the same sphere but without the shading. This makes the sphere appear almost flat because it’s surface colour is uniform.

The VRML browser automatically shades shapes by determining where any light sources are. So, if a light source was to the right of the sphere while we were still looking from the front, the right hand side of the sphere would appear bright while the left hand side would appear dark.

One thing to note is that all VRML browsers include a ‘headlight’ which is attached to the user’s viewpoint. You can switch this headlight on or off, but it is left on by default. This headlight illuminates the scene wherever you look. Figure 3(a) shows the effect of the headlight. We are looking at the sphere straight on, so the light is shining onto its front. Note how the shading increases further away from our headlight.

Figure 3(a). A shaded sphere.

Figure 3(b). The same sphere without shading.

4.5.1.3. Luminance

Some objects emit their own light, such as light bulbs, television screens or very hot metals. Brightly glowing objects such as a light bulb appear simply as a single colour, any shading is washed out by the light they are emitting. Within VRML we can specify how much light (and also what colour) a glowing shape will emit. VRML simply shades emissive objects differently to normal objects to give the impression that it is emitting light. Take figure 3(b) again, this was produced by specifying a full emissive colour of red causing the VRML browser to ignore any shading on the object.

4.5.1.4. Transparency

Transparency is a measure of how much light an object will allow to pass through it. Opaque objects do not pass any light and appear solid, e.g. a book. Transparent objects let all light through and appear almost invisible, i.e. a glass window pane. Other objects such as a fine gauze or sunglasses or semitransparent as they only let some light through. We can specify the transparency of an object in our VRML worlds to create various different effects such as glass or water.

Different VRML browsers deal with transparency in different ways. One technique uses a form of colour-blending to simulate how a coloured object would appear through another coloured, semitransparent object. A simpler and more efficient technique overlays a dither pattern on any objects behind a semitransparent screen. This dither pattern is effectively a fine mesh consisting of dots and spaces. The spaces mean any objects behind the mesh can be seen, while the dots block the objects from view. By varying the proportions of dots to spaces you can see more or less of the objects behind, giving an impression of more or less transparency. The second technique is far more efficient, however, the first technique provides a much more realistic result.

4.5.2. The appearance node

The Appearance node is just another simple wrapper for the two primary fields, material and texture. The Material node is concerned with colour, transparency, shininess and luminance. The Texture node allows you to map more realistic textures or images (static or dynamic) onto a shape's surface. At the moment we will concentrate only on specifying a Material node.

If we look at the definition for the Material node in the VRML specification we will see it has the following fields (default values also shown) :

Material
{ 
  SFColor diffuseColor 0.8 0.8 0.8 
  SFColor emissiveColor 0 0 0 
  SFFloat transparency 0
  SFFloat ambientIntensity 0.2
  SFFloat shininess 0.2
  SFColor specularColor 0 0 0
}

We will now briefly explain what each of these fields represents, and how you can use each to control the appearance of a shape.

4.5.2.1. diffuseColor

The diffuseColor field specifies an RGB value for the colour of the object. This colour is dependant on any light sources and the VRML browser will shade the object accordingly. If there is no light in the scene then the object will appear dark. The default value for this field is a very light grey.

4.5.2.2. emissiveColor

The emissiveColor field specifies an RGB value for the amount and colour of light which a material will emit. This will make a shape appear to glow with this colour. Emissive colour is not dependent on light sources in a world and will not be shaded. Note that specifying an emissive colour value will make shapes appear to glow, however they do not act as a light source themselves so will not illuminate any surrounding shapes. The default value for the emissiveColor field is black which indicates no glow.

4.5.2.3. transparency

The transparency field is a value between 0.0 and 1.0 and species the level of transparency a shape will possess. A value of 0.0 makes a completely opaque shape while a value of 1.0 is completely transparent. Values between these extremes give a semitransparent effect. The default value is 0.0 giving opaque materials.

4.5.2.4. ambientIntensity, shininess and specularColor

The remaining fields of the material node are all concerned with how a material behaves under light. We will be discussing light sources later and may come back to these fields then.

4.5.3. Some examples

We will now look at a few examples which show the effects of varying the properties of a shape’s material node. First we will look at several examples of setting the diffuseColor field.

diffuseColor 1.0 0.0 0.0
Red

diffuseColor 0.0 1.0 0.0
Green

diffuseColor 0.0 0.0 1.0
Blue

diffuseColor 1.0 1.0 0.0
Yellow

diffuseColor 1.0 0.0 1.0
Magenta

diffuseColor 0.0 1.0 1.0
Cyan

Figure 4(a-f). Showing various settings for the diffuseColor field and their effects.

Now, we will look at various settings for the emissiveColor field. Remember, materials using an emissive colour appear to glow and are not affected by light sources. Figures 5(a-e) shown below all have a diffuseColor field set to 0.0 0.0 0.0 (i.e. Black). The emissiveColor is varied from black (0.0 0.0 0.0) to pure green (0.0 1.0 0.0) in steps of 0.25. There are no lights in the scene.

Effect:

emissiveColor

0.0, 0.0, 0.0

0.0, 0.25, 0.0

0.0, 0.50, 0.0

0.0, 0.75, 0.0

0.0, 1.0, 0.0

Figure 5(a-e). Showing increasing emissiveColor field values.

Finally, we will look at how the transparency field affects an object’s appearance. In figures 6(a-f) below, we have two spheres, one red and one green. The red sphere is larger and is placed in front of the green sphere, completely obscuring it from view. In figure 6(a) the material for the red sphere has a transparency field of 0.0, i.e. completely opaque. Through figures 6(b-f) we increase the transparency in values of 0.2, until the red sphere is completely transparent. Note how the green sphere becomes more and more apparent and the red sphere becomes less visible.

transparency 0.0

transparency 0.2

transparency 0.4

transparency 0.6

transparency 0.8

transparency 1.0

Figures 6(a-f). Showing the effect of varying levels of transparency.

4.5.4. Example VRML code

All the above examples (except the transparency example) used a very basic VRML file to test the various material node attributes. This code is shown below so you can begin to get a feel for how VRML scenes are written. The only difference between the code below and the examples shown above is the white background which was explained earlier.

#VRML V2.0 utf8

Shape
{
  appearance Appearance
  {
    material Material
    {
      # These are the fields we alter for the examples 
      diffuseColor 0.0 0.0 0.0
      emissiveColor 0.0 0.0 0.0
      transparency 0.0
    }
  }

  # We will make this shape a sphere
  geometry Sphere
  {
    radius 1.0
  }
}

4.6. Grouping and inlining

One of the most powerful features in 3D graphics is the ability to group objects together and treat them as a single entity. For example, a chair would be constructed from a number of individual objects but we can group these objects together and treat them all as a single entity, as a chair. So, if we move the chair then all the components of it will also move, if we rotate the chair then they will all rotate. Using grouping we can quickly create very complex objects then use these objects to create even more complex scenes with ease. VRML supports grouping in a number of ways, the most basic of which is the Group node.

4.6.1. The Group node

If we look at the VRML specification, we can see the format of the group node is extremely simple. Out of the three fields shown here, there is only one we are really interested in, the children field. This field has type MFNode meaning that it expects a list of other nodes, and we can see from the definition that the default value is an empty list. Any nodes which we place into the children field of the group will be the components of our object. We can then refer to the entire object by simply referring to the Group node. This concept is extremely important to understand as it is a very heavily used principle within VRML scenes.

The two other fields specified here are bboxCenter (Note another American spelling) and bboxSize. These specify a bounding box which is an imaginary volume of 3D space which totally encloses all the children of our group. These values are only used by the VRML browser to speed up rendering of the scene. Also, if you do not specify a value for these then the browser will automatically compute the correct size and centre of the bounding box for you. This is much easier than specifying it yourself. There are times when it is useful to specify these values, but this is out of the scope of this course.

Group 
{ 
  MFNode children [ ]
  SFVec3f bboxCenter 0 0 0
  SFVec3f bboxSize -1 -1 -1
}

4.6.2. Examples of grouping

A very simple example of grouping would be to create a set of 3D axes similar to those used in earlier lectures. We create this as a group of three coloured boxes of different dimensions. We want one box for each of the x, y and z axes. In VRML, all shapes are centred around the origin of the current co-ordinate system, so we simply have to alter the dimensions of the boxes to make them extend along the appropriate axes (they will all be centred on the origin, irrespective of size). We will use a red box for the x-axis, a green box for the y-axis and a blue box for the z-axis. Note that seeing as this is only a simple example we will not be able to put arrowheads onto these axes, so the positive direction will not be obvious.

Let’s first take a look at the syntax for the Box node (this is a complicated one!):

Box
{
  SFVec3f size 2 2 2
}

It has only one field, size, which specifies the x, y and z dimensions of the box. One thing you may notice about the default values for all the primitive shapes in VRML is that they all fit within the values -1.0 and 1.0 on all axes. I.e. the Sphere has radius 1.0, the Cone has bottomRadius 1.0 and height 2.0, the Box has size (2 2 2). We will keep with this convention and make our axes extend from -1.0 to 1.0 in each direction.

So, for the x-axis we want it to go from -1.0 to 1.0 (i.e. an x-size of 2.0), and we want it to be fairly thin. A good value to use may be 0.1 (i.e. 5% of length). So, the size for our x-axis Box will be (2.0 0.1 0.1). We will build the other boxes similarly but swapping the dimensions around. The sizes for all the boxes are:

Box node for x-axis

size 2.0 0.1 0.1

Box node for y-axis

size 0.1 2.0 0.1

Box node for z-axis

size 0.1 0.1 2.0

Now, the code for our VRML world follows. Note that we are defining a name for our group of axes called "XYZaxes". We could then use that name later in the file to create copies of our axes. Also note how we specify a list of values in the children field, we give our list of nodes within square braces. Probably the most noticeable difference is the actual size and complexity of the file, even for such a simple example. VRML files can become extremely large very quickly, and it is very easy to lose track of bracket and parenthesis pairings. A good layout is critical to being able to easily work with your file. There are ways of breaking up the complexity of a VRML scene which we will look at next.

#VRML V2.0 utf8

# Our group begins here. We are defining this group as "XYZaxes"
# so we can refer to the group object later on if we like

DEF XYZaxes Group
{
  # The children node specifies anything that belongs to the group
  children
  [
    # First lets create the X-axis box
    Shape
    {
      appearance Appearance
      {
        material Material
        {
          # A bright red colour for the x-axis
          diffuseColor 1.0 0.0 0.0
        }
      }

      geometry Box
      {
        # Long in the X-direction and thin in Y and Z
        size 2.0 0.1 0.1
      }
    }

    # Now lets create the Y-axis box
    Shape
    {
      appearance Appearance
      {
        material Material
        {
          # A bright green colour for the y-axis
          diffuseColor 0.0 1.0 0.0
        }
      }

      geometry Box
      {
        # Long in the Y-direction and thin in X and Z
        size 0.1 2.0 0.1
      }
    }

    # Finally, lets create the Z-axis box
    Shape
    {
      appearance Appearance
      {
        material Material
        {
          # A bright blue colour for the z-axis
          diffuseColor 0.0 0.0 1.0
        }
      }

      geometry Box
      {
        # Long in the Z-direction and thin in X and Y
        size 0.1 0.1 2.0
      }
    }
  ]
}

Okay, now we have created our VRML file, but what does it look like? Figure 7 below shows the rendered version of our file. Now that we have created a group, we can refer to the entire object as a single node.

Figure 7. An example of grouping three boxes to create a set of axes.

4.6.3. Inlining

As we have just seen in the previous example, even the simplest scenes need large VRML text files. This is not too bad in the above example but if we planned on creating anything remotely complex it would soon become a problem. One way we can get round this problem is to break down a VRML scene into a number of components and define each component in a separate file. We then inline the appropriate files when we need them. Inlining effectively inserts a specified file into your current file. For example, we could place the axes we have just created into a file called "XYZaxes.wrl" and inline it wherever necessary. Using this feature you can rapidly build up a library of VRML objects which we can reuse in any number of scenes.

VRML handles inlining by using the Inline node. The definition of this node is fairly simple:

Inline 
{ 
   MFString url []
   SFVec3f bboxCenter 0 0 0
   SFVec3f bboxSize -1 -1 -1
}

As with the group node, only one field is of importance to us, the url field. This specifies the location of the file we want to inline. Because this location is a URL (Uniform Resource Locator), we can specify a file by either a local path name or an Internet address. So, we could use any of the following:

"XYZaxes.wrl",

"c:\my-stuff\XYZaxes.wrl", or even …

"http://pc-vrg.dur.ac.uk/XYZaxes.wrl"

Similar to creating a web page, it is always better to specify relative paths as opposed to an absolute path. Then, if you move your files you wont have to go in and edit all the inline URLs.

The other two fields actually have more of a use in the Inline node than in the Group node. They perform the exact same function - specifying a bounding box which encompasses all the objects in the inlined file. However, it is advantageous to specify the actual size and centre of this bounding box yourselves if you know it. If the file loads ok then the VRML browser will calculate these values for you, but if the file cannot be loaded for any reason, if you specify a bounding box then the browser will display a wire-frame box around the area which the inlined file would have occupied. This is purely optional, but it provides a handy tool for determining what is wrong with your worlds.

An important point to note about inlining is that defined names (created using the DEF keyword) do not work between files. So, any names we defined in our "XYZaxes.wrl" file will not be available in any files we inline it into. This is both a good and bad thing. It is bad in that we cant use any colours or objects we may have defined in the inlined file. It is good in that we don’t have to worry about what defined names were used in these files, we can freely create new names without worrying about a conflict.

4.6.4. Example of inlining

As an example of inlining, we will create a simple new file containing a single small sphere. We will inline our XYZaxes so we can see where the sphere has been created with respect to the world co-ordinate system. We will redefine the inlined file to the name XYZaxes again so we can easily create more copies of this group within this file. Notice that we have filled in the bboxCenter and bboxSize fields because we know what these dimensions should be.

#VRML V2.0 utf8

# First of all we will inline our XYZaxes.wrl file and redefine the
# group to the name "XYZaxes".

DEF XYZaxes Inline
{
   # First the location of the file we want to inline.
   url "XYZaxess.wrl"

   # We know the size of our XYZAxes model so might as well enter it.
   bboxCenter 0.0 0.0 0.0
   bboxSize 2.0 2.0 2.0
}

# Now we will create a small yellow sphere
Shape
{
   appearance Appearance
   {
      material Material
      {
         diffuseColor 1.0 1.0 0.0
      }
   }

   geometry Sphere
   {
      radius 0.3
   }
}

Figure 8(a) below shows what this file looks like in the VRML browser. Notice how we have our XYZaxes model displayed even though it is not defined in the file. The Inline node imported the definition into our file. Because we specified a bounding box size and centre, if a problem occurred trying to load the inlined file then the browser displays a bounding box showing where the objects in the file would have been. Figure 8(b) shows this bounding box.

Figure 8(a). Example of an inlined file.

Figure 8(b). The inlining failed so the browser displays a bounding box.

4.7. Transformations

In VRML all shapes we build are centred around the origin of the current co-ordinate system. Obviously, this isn’t really that much use unless we can move these objects around. What we can do in VRML is create new co-ordinate systems which are built relative to the world co-ordinate system and can be position, orientated and scaled in any way we desire. We can then build shapes in these new co-ordinate systems, in effect transforming them. In VRML you can create any number of co-ordinate systems, each of which is transformed with respect to another co-ordinate system. These co-ordinate systems create a tree structure, the root of which is the world co-ordinate system. The world co-ordinate system always remains constant. Any new co-ordinate systems built on top of an existing one are called child co-ordinate systems, the existing one being the child’s parent co-ordinate system. If we transform any co-ordinate system then all of it’s children and all objects defined in them will be transformed relative to the parent. The entire tree of co-ordinate systems, including any shapes built within those systems, is often referred to as the scene graph. It is essential that you are comfortable with notion of these nested co-ordinate systems as they provide the basis for any 3D scene. Most other 3D packages use similar principles.

4.7.1. The Transform node

The VRML node we need to do this is the Transform node which is a more advanced version of the Group node. If we look at the definition for the Transform node we can see it has some additional fields to the Group node:

Transform 
{ 
   MFNode children []
   SFVec3f translation 0 0 0
   SFRotation rotation 0 0 1 0
   SFVec3f scale 1 1 1
   SFVec3f center 0 0 0
   SFRotation scaleOrientation 0 0 1 0
   SFVec3f bboxCenter 0 0 0
   SFVec3f bboxSize -1 -1 -1
} 

Some of these fields we are already familiar with, namely the children, bboxCenter and bboxSize nodes. We will now explain the remaining fields.

4.7.1.1. The translation field

The translation field specifies an XYZ vector which translates the new co-ordinate system along this vector. A translation vector of (4, 5, 2) would mean that the child co-ordinate system would have its origin centred on the point X=4, Y=5, Z=2 of the parent co-ordinate system. Any shapes built within this new co-ordinate system would be translated with it. The default value is (0, 0, 0) which builds the child co-ordinates at the origin of the parent co-ordinates.

We will use our XYZaxes model developed earlier to demonstrate this in action. If we take the source code file we created in section 4.7.4. above, we already have a set of XYZ axes placed at the world co-ordinate system origin. We also have a yellow sphere placed at the centre of these axes so we can distinguish them from any new XYZaxes models we introduce. We will use this code as the base for our examples and will refer to it as the base code. Now, we will create a new co-ordinate system which is translated by (1, -1, -1) and place a new set of XYZaxes in this co-ordinate system. We can add the following code to the end of the base code listing:

Transform
{
   translation 1.0 -1.0 -1.0

   children
   [
      USE XYZaxes
   ]
}

This code creates a new co-ordinate system at position 1.0x, -1.0y, -1.0z. The contents of this co-ordinate system are a set of our XYZaxes. Note that we have used the defined name to refer to axes which saves us retyping its contents. Figure 9, below, shows the results of our addition - a new set of axes is created and translated away from our original set.

Figure 9. Example showing a translation using the Transform node.

4.7.1.2. The rotation and center fields

The rotation field is of type SFRotation, a four-value setting which specifies a rotation axis and a rotation angle. The first three values of the rotation field specify the X, Y and Z components of a vector which will be used as the rotation axis. The fourth value is the rotation angle which is specified in radians. You may remember from the previous lectures that the mathematics behind rotating about an arbitrary vector was complex, however, we are not concerned with the mathematics here as the VRML browser will do it for us. We can specify any vector and angle we like. However, figuring out exactly which vector and angle we need to get the orientation we want still takes some effort to comprehend. We will just deal with some simple cases here.

We already know the vectors for our X, Y and Z axes so if we wanted to we could create three nested co-ordinate systems and use XYZ fixed angles (Lecture 3) to create any orientation we liked. More usually though we would do this with only one Transform node.

Note that the rotation angle must be specified in radians. In case you can’t remember how to convert between radians and degrees, you use the following conversion:

Most of the time we use standard angles such as 45° , 90° and 180° . The following table provides some of the more useful conversions.

Degrees

Radians

Radians

0

0.0000

45

0.7854

90

1.5708

135

2.3562

180

3.1416

225

3.9270

270

4.7124

315

5.4978

360

6.2832

So, as an example, if we wanted to rotate our new co-ordinate system by 45° about the Y-axis (so a yaw rotation of 45° ) we would specify a rotation field of (0.0 1.0 0.0 0.7854). The first three values specify the unit vector for the Y-axis, the fourth value specifies a rotation of 0.7854 radians which is 45° .

Finally, the Transform node also has a center field which affects how rotations are calculated. The center field specifies the XYZ co-ordinates of the point about which to rotate. By default this field is (0, 0, 0) so rotation is performed about the co-ordinate system’s origin. However, depending on the object we have created, it may be more natural to have this centre point positioned elsewhere.

We will now see an example of using the rotation and center fields. If we take our base code which we mentioned earlier (the XYZaxes with yellow sphere) and the following section of code:

Transform
{
   translation 1.0 -1.0 -1.0
   rotation 0.0 0.0 1.0 1.5708

   children
   [
      USE XYZaxes
   ]
}

Note that we have still translated the new co-ordinate system (just to move it away from our original) but this time we are performing a 90° rotation about the Z-axis. For this example we have left the center field undefined, so rotation will be performed about the new co-ordinate system’s origin. Figure 10(a) below shows the result of this addition to the base code. Now, if change the center field we can change the point about which we will rotate. We will set the new value to (-1.0, 0.0 0.0) which is the leftmost tip of the red X-axis. Our axes should now rotate up about this point. To do this, we change the above code as follows:

Transform
{
   translation 1.0 -1.0 -1.0
   rotation 0.0 0.0 1.0 1.5708
   center -1.0 0.0 0.0

   children
   [
      USE XYZaxes
   ]
}

The result of this new transformation is shown in figure 10(b). Compare this with the first rotation example (Fig. 10(a)). In the first example we can clearly see that we have rotated 90° about the Z-axis, the red X-axis is now aligned vertically. Note that the position of the new axis has not changed, only it’s orientation. Now, comparing this with figure 10(b) we see a very different result. The new axes have pivoted about the leftmost tip of the red x-axis. The result is that the position of the object also changes.

Figure 10(a). Rotation of 90° about Z, centred on the origin.

Figure 10(b). Rotation of 90° about Z, centred on point (-1.0, 0.0, 0.0)

4.7.1.3. The scale field

We have already seen how we can translate and rotate co-ordinate systems. The Transform node also provides us with the ability to scale a co-ordinate system and any objects created within it. The scale field specifies an XYZ triple which holds our scale factors for the X, Y and Z axes respectively. The default value of the scale field is (1.0, 1.0, 1.0) which means no scaling. If we reduce any of these values we can compress the co-ordinate system in a particular direction, or if we increase any values we can stretch the co-ordinate system likewise. When a co-ordinate system is scaled, all it’s children are scaled respectively as well. So, if we specified a scale field value of (0.5, 2.0, 1.0) this would compress the co-ordinate system to half it’s size in the X-direction, stretch it to double it’s size in the Y-direction and leave the Z-direction unchanged.

As with the rotation field, you can specify a center point about which to scale. By default all shapes are scaled about the origin of the new co-ordinate system, but it is possible to specify another point about which to scale.

Another field of the Transform node is the scaleOrientation field. This field allows you to specify an arbitrary vector or axis about which to perform the scale operation. Conceptually, this performs a rotation specified by the scaleOrientation field (exactly the same principles as the rotation field) then scales by the XYZ values given in the scale field, and finally rotates the object back to its original position. Of course, this is all done behind your back so you only see the end result of the scale. The following examples may help to explain the different features of scaling.

This example uses a simple Cone shape as a target for our scaling. We must add the following code to our base code:

Transform
{
   translation 1.0 -1.0 -1.0
   children Shape
   {
      appearance Appearance
      {
         material Material
         {
            diffuseColor 0.0 0.0 1.0
         }
      }

      geometry Cone
      {
         bottomRadius 0.5
         height 1.0
      }
   }
}

This code produces a single blue cone which is centred about the position (1.0, -1.0, 1.0) wit respect to our world co-ordinates. The result of this code is shown in figure 11(a). Now, we will introduce a scaling of (2.0, 1.0, 1.0) which will stretch the cone in the X-direction to double its size. To do this we add the following line to the Transform node (just after the translation field):

scale 2.0 1.0 1.0

The result of this scaling is shown in figure 11(b). Note how the cone is still centred about its original point, yet its dimensions have changed. Note also how the left-most tip of the cone is now in line with the world Z-axis.

Now, we can change the centre of our scaling by introducing the following code, placed just after the scale field we introduced above. This chooses the leftmost tip of the original cone as the scaling centre.

center -0.5 -0.5 0.0

The result of this addition is shown in Figure 11(c). Note that although the size of the new cone is the same as in figure 11(b), its position is different. Because the scaling origin was at the leftmost tip of the cone, it was only stretched outwards to the right, so this position has remained constant.

Finally, we will now remove the center field and introduce a scaleOrientation field in its place. We will use a scaleOrientation of (0.0 0.0 1.0 0.7854).

scaleOrientation 0.0 0.0 1.0 0.7854

This effectively rotates the cone by 45° about it’s Z axis, performs the scaling of (2.0 1.0 1.0) then rotates the shape back to its original orientation. The results of this are a little weird, as the skewed cone in figure 11(d) shows. Don’t worry about how to use scaleOrientation here, we are only covering it for completeness.

Figure 11(a). The original pre-scaled cone.

Figure 11(b). Scaling by (2.0, 1.0, 1.0)

Figure 11(c). Same scaling but about a centre point of (-0.5, -0.5. 0.0).

Figure 11(d). Scaling using a scaleOrientation of (0 0 1 0.7854).

4.8. A complete example

Now that we have covered the various aspects of basic VRML modelling separately, we will integrate everything covered in this lecture into a single example. We will create a simple model of an articulated desk lamp. The lamp will consist of 4 components, a base, a lower arm, an upper arm, and a lamp shade. We will use a box for the base (A flat cylinder would be better, but we will use a box for completeness) , cylinders for the arms and a cone for the shade. We will be using transformations to position and orientate each component and will be using nested co-ordinate systems so that if, for example, the lower arm is moved then the upper arm and shade will also move.

We will begin by creating a group node to encompass the entire lamp object, and defining a name "DeskLamp" for it. We will also add the lamp base to this group. We first need to define a suitable scale for our co-ordinate system. We will use a scale of 1 meter = 1 VRML unit. A decent size for our lamp base is 10cm*10cm and 1cm thick, so the Box size will be (0.1, 0.01, 0.1). We are also defining the appearance node "LampAppearance" which we can use for all the components to give a uniform appearance. The code is as follows:

#VRML V2.0 utf8

# The group containing our whole desk lamp. We can refer to the
# whole lamp as a single object using this group.

DEF DeskLamp Group
{
   children
   [
      # First we create the lamp base
      Shape
      {
         appearance DEF LampAppearance Appearance
         {
            material Material
            {
               # A dark grey color
               diffuseColor 0.3 0.3 0.3
            }
         }

         geometry Box
         {
            size 0.1 0.01 0.1
         }
      }
   ]
}

Figure 12. The start of our lamp - a simple square base.

The result of this first stage (i.e. the lamp base) is shown in figure 12. Our next stage is to add in the lower arm of the lamp. We will use a long, thin cylinder for this. We also define a name for the cylinder we create so we can reuse it in creating the upper arm. Our lower arm must be enclosed within a translation node so we can position it above the base and also allow it to rotate. We will set the centre of rotation to be the lower end of the arm, seeing as this is where the joint with the base is made. Our lamp arms are going to be 30cm long (height of 0.30) and 1cm thick (so will have a radius of 0.005). The new code is as follows, any additions are shown in red:

#VRML V2.0 utf8

# The group containing our whole desk lamp. We can refer to the
# whole lamp as a single object using this group.

DEF DeskLamp Group
{
   children
   [
      # First we create the lamp base
      Shape
      {
         appearance DEF LampAppearance Appearance
         {
            material Material
            {
               # A dark grey color
               diffuseColor 0.3 0.3 0.3
            }
         }

         geometry Box
         {
            size 0.1 0.01 0.1
         }
      }

      # Now we create the lower lamp arm
      Transform
      {
         # we need to translate it above the base
         translation 0.0 0.15 0.0

         # we want the rotation center to be at the lamp base joint
         center 0.0 -0.15 0.0

         # We want to be able to tilt the lamp up and down
         # we will start it off at 45 degrees
         rotation 0 0 1 -0.7854

         children
         [
            DEF LampArm Shape
            {
               appearance USE LampAppearance
               geometry Cylinder
               {
                  height 0.30
                  radius 0.005
               }
            }
         ]
      } 
   ]
}

Figure 13. The lamp base and lower arm.

The state of the lamp so far can be seen in figure 13. The next stage is to add in the upper arm. We will need to translate the upper arm so that it starts at the top of the lower arm. We will also define the rotation centre of the upper arm to be at the joint with the lower arm. We can also reuse the defined shape for the lower arm. Our new code is shown below, with additions over the last code shown in red:

#VRML V2.0 utf8

# The group containing our whole desk lamp. We can refer to the
# whole lamp as a single object using this group.

DEF DeskLamp Group
{
   children
   [
      # First we create the lamp base
      Shape
      {
         appearance DEF LampAppearance Appearance
         {
            material Material
            {
               # A dark grey color
               diffuseColor 0.3 0.3 0.3
            }
         }

         geometry Box
         {
            size 0.1 0.01 0.1
         }
      }

      # Now we create the lower lamp arm
      Transform
      {
         # we need to translate it above the base
         translation 0.0 0.15 0.0

         # we want the rotation center to be at the lamp base joint
         center 0.0 -0.15 0.0

         # We want to be able to tilt the lamp up and down
         # we will start it off at 45 degrees
         rotation 0 0 1 -0.7854
         children
         [
            DEF LampArm Shape
            {
               appearance USE LampAppearance
               geometry Cylinder
               {
                  height 0.30
                  radius 0.005
               }
            }

            # Now we add in the upper arm
            Transform
            {
               # translate to the top of the lower arm
               translation 0.0 0.30 0.0

               # set the rotation centre at the joint between arms
               center 0.0 -0.15 0.0

               # set the initial rotation to some angle
               rotation 0 0 1 2.0
               children
               [
                  # Just reuse the same shape as the lower arm
                  USE LampArm
               ]
            }
         ]
      } 
   ]
}

Figure 14. The lamp base and two articulated arms.

Our new lamp which has a base and two articulated arms is shown in figure 14. All we have left to do now is add in the lamp shade which we will construct from a simple cone. We have to translate the cone to the end of the second arm, and also rotate it to point in the correct direction. A reasonable size for our lamp shade is 15cm long (height field is 0.15) with an end radius of 5cm (bottomRadius field is 0.05). The final code is shown below, additions made in this stage are again shown in red.

#VRML V2.0 utf8

# The group containing our whole desk lamp. We can refer to the
# whole lamp as a single object using this group.

DEF DeskLamp Group
{
   children
   [
      # First we create the lamp base
      Shape
      {
         appearance DEF LampAppearance Appearance
         {
            material Material
            {
               # A dark grey color
               diffuseColor 0.3 0.3 0.3
            }
         }

         geometry Box
         {
            size 0.1 0.01 0.1
         }
      }

      # Now we create the lower lamp arm
      Transform
      {
         # we need to translate it above the base
         translation 0.0 0.15 0.0

         # we want the rotation center to be at the lamp base joint
         center 0.0 -0.15 0.0

         # We want to be able to tilt the lamp up and down
         # we will start it off at 45 degrees
         rotation 0 0 1 -0.7854
         children
         [
            DEF LampArm Shape
            {
               appearance USE LampAppearance
               geometry Cylinder
               {
                  height 0.30
                  radius 0.005
               }
            }

            # Now we add in the upper arm
            Transform
            {
               # translate to the top of the lower arm
               translation 0.0 0.30 0.0

               # set the rotation centre at the joint between arms
               center 0.0 -0.15 0.0

               # set the initial rotation to some angle
               rotation 0 0 1 2.0
               children
               [
                  # Just reuse the same shape as the lower arm
                  USE LampArm

                  # Finally, we add in the lamp shade
                  Transform
                  {
                     # we need to translate it to the end of the second arm
                     translation 0.0 0.15 0.0

                     # we need to rotate it round to face perpendicular to
                     # the upper arm (built parallel to arm by default)
                     rotation 0 0 1 -1.5708

                     children
                     [
                        Shape
                        {
                           # reuse our standard lamp appearance
                           appearance USE LampAppearance
                           geometry Cone
                           {
                              bottomRadius 0.05
                              height 0.15
                           }
                        }
                     ]
                  }
               ]
            }
         ]
      } 
   ]
}

Figure 15. The completed desk lamp.

Figure 15 shows the final picture of our desk lamp. We have created a total of four nested co-ordinate systems in this model. Firstly we have the Group node which is built within the world co-ordinate system. We then create a Transform node which is rotated and translated with respect to the Group co-ordinate system for our lower arm. Within this we create another Transform node for the upper arm, again translated and rotated with respect to its parent. Finally we create yet another Transform node to position the lamp shade. Using this method, if we change any of the angles for either of the arms then everything connected to them will rotate accordingly.


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

Last updated: Tuesday 10 February, 1998.