Chapter 17

The VRML Art Gallery: A VRML World by Hand

-by Justin Couch


CONTENTS


You now have enough information to start creating your first large VRML 2.0 world. In Chapter 2, "Up and Running: First VRML Creation," you created everything using readily available tools, which limited you to generating 1.0 worlds. Now that you've gotten this far, it's time to dust off your favorite text editor because this VRML world is going to be done all by hand.

One of the best things about virtual reality is that you don't need to conform to traditional earthbound construction-you can do whatever you want. I'm the sort of person who likes to experiment with things, so this creation is going to be a virtual art gallery. What am I going to put in this gallery? Well, my co-author has been good enough to create the content for me:

If you've been reading the preceding few chapters, many of the pieces will look familiar to you. Once you've created a few worlds, you will find that you've built up a library of parts you use over and over. Now you'll see the reason for doing what seemed like trivial examples in previous chapters; when you combine them, you get quite a stunning scene.

Designing the Gallery

So far in your collection of parts, you have a marble column, a tree, an arrow floating in space, and a few lights and cameras. Not much, but it's really all you need. It's amazing what you can do with just a few basic parts. I also have eight images from my co-author in my collection of pictures that I want to display.

A series of islands sounds like fun to me. Of course, since this is virtual reality, they will have to float in space, too. The islands will be connected by a series of wooden bridges, and the marble columns can be used to act as a gateway.

Constructing the Islands

Each island will be made from an IndexedFaceSet. You could have used the ElevationGrid to create the islands, but to get a real 3D look, I didn't want all the islands to be the same height. Using IndexedFaceSets allows you to create a floating island that can be viewed from all angles.

The first island is easy. It's almost a cone, but if you use a face set, you can control some of the properties.

  1. Declare the list of vertices that you want to make the island from. The island will have an octagonal base and a single-point vertex at the top in the center.
  2. Next, join the vertices so you get a triangular face that has the base as one side of the octagon and the top as the single center point. There should be eight of these.
  3. Create the base by making a single large face that has the edges of the octagon as the face's edges.
  4. Put this all into a Shape node and make the material color green-the islands must be grass-covered, after all.
  5. Load the single island and move around it, checking that each face is visible. If not, then reverse the order of the vertices declared in the coordIndex field. Don't forget to check the base as well.

The island declaration is given in Listing 17.1.

Tip
Make note of the order that the vertices are declared in the coordIndex field. If they were declared in the opposite order (for example, 8, 0, 1, 0), the island would become invisible when you moved over the top of it. Normals aren't specified for the face, so the renderer works it out. If you have problems with the face not appearing when you expect it to, then try reversing the order.


Listing 17.1. The basic island.

#VRML V2.0 utf8
#
# A simple island constructed from an IndexedFaceSet
Shape {
    appearance appearance {
        material Material {
            diffuseColor 0.1 0.5 0.2
            emissiveColor 0.05 0.1 0.05
        }
    }
    geometry IndexedFaceSet {
        coord Coordinate {
            point [
                20 0 0, 15 0 15, 0 0 20, -15 0 15, -20 0 0, -15 0 -15, 0 0 -20,
Â15 0 -15,
                0 10 0
            ]
        }
        coordIndex [
            8, 1, 0, 8, -1,
            8, 2, 1, 8, -1,
            8, 3, 2, 8, -1,
            8, 4, 3, 8, -1,
            8, 5, 4, 8, -1,
            8, 6, 5, 8, -1,
            8, 7, 6, 8, -1,
            8, 0, 7, 8, -1,
            0, 1, 2, 3, 4, 5, 6, 7, 0, -1,
        ]
    }
}

Creating More Islands by Reusing Objects

If you wrote out all the code for the object every single time you used the file, it would soon get out of hand. With VRML, you can reuse an object in the file by naming it, then using that name when you need the object again. This method applies to any node in the scene, which could be anything from a material to a script.

To name a node, place the keyword DEF and the name in front of the node you want to reuse. To use it somewhere else, insert the keyword USE and that same name. Yes, you've seen these keywords before. In Chapter 15, "Sprucing Up Models with Textures and Materials," when you texture-mapped the cylinders to make the marble column, you reused a single cylinder and scaled it to fit with the other cylinders.

Now create four more islands to go into the scene. The first island was based around the origin, so move it by placing a Transform node around it. You'll do the same thing when you clone the island, so that means you need to use the DEF keyword on the Shape node, then insert the USE keyword in the children of the other Transform nodes. Listing 17.2 shows how to do this:


Listing 17.2. Reusing parts of the scene graph with the DEF and USE keywords.

Transfrom {
    translation 0 -20 0
    children [
        DEF island Shape { # rest of shape definition
        }
    ]
}
transform {
    translation 50 0 0
    children [ USE island ]
}

When you use the keyword USE on an object, you're creating a reference to the original, not a copy of it. This can be both bad and good. By creating a reference, if any properties of the original change, every other version with the USE keyword will change as well. If you've declared many instances of the one object and you want them all to change color together, then this is an effective technique. Change one, and you change the rest. Of course, creating a reference has its bad points, too, because this method might cause unwanted side effects. Basically, keep use of the USE keyword to the minimum you need to get the effect you want. In the previous example, you used the DEF keyword on just the Shape node, rather than the whole Transform node, because that was all that was necessary.

Connecting the Islands

If you've seen the basic layout of the world already, you'll notice that I have placed four islands at one level and a fifth one at a different level. How should they be connected? I think a set of stairs would work nicely between them. Sticking to the theme, these stairs should just be floating in space. Make the stairs from boxes, and use the texture Bubsy used on the background of his pages to give them that surreal industrial look.

To construct the stairs, follow these steps:

  1. Create a very thin single box.
  2. Add the texture of the safety metal to the box, using the ImageTexture node in the appearance field.
  3. Create a series of boxes with each one offset from the next one below it to produce a single staircase.
  4. Take copies of the completed staircase and place them on each of the four sides that lead from the central island to the elevated ones.

Each one must be offset from the previous one, which you can declare in two different ways. The first method is to create several individual Transform nodes, putting the stairs at a fixed offset in relation to the origin or some other point in space. The second method is to position the first stair, then offset each subsequent one in relation to the one below it (assuming you build the staircase from the bottom up). I prefer the second method, because if you move the second stair, for example, then the rest automatically move to stay in the same relative position.

Once you've created the first staircase, then you want to copy it for the other staircases between the islands. Here's what I did:

Transform {
  translation 21 -20 0
  children [
    DEF stair_group Group {
      children [
        DEF a_stair Shape {
          appearance appearance {
            texture ImageTexture { url "safety_metal.jpg" }
          }
          geometry Box { size 2 0.1 6 }
       }
       Transform {
         translation 2 2 0
         children [
           USE a_stair
           Transform {
           # etc etc for the stairs
           }
         ]
      }
    }
  ]
}
Transform {
    translation 0 -20 -21
    rotation 0 1 0 1.57
    children [ USE stair_group ]
}

The tricky part to copying this staircase is that you need to use both the first untransformed step (as the base point) and the rest of the staircase. To do this, you must put a wrapper around both of them. Use the Group node as this wrapper; it has no function other than to collect a group of nodes together so you can use the DEF keyword on them. See how much cleaner the code has become when you get ready to add the stairs to the other islands?

Now connect the top islands with some wooden planks. By now you should know what to do-first, declare a single box with a wood texture, use the DEF keyword, then translate it into position.

Adding the Gallery Contents

Now that you have a basic world up and running, you need to put some pictures in it. I've chosen eight pictures from the first part of the book that I want to put in the upper levels of the islands. For now, I'm going to leave the bottom level empty.

Putting in the Pictures

Each of the pictures is a different size, so this time you can't use the DEF and USE keywords to save file space. Each image requires a box of its own. I used boxes rather than face sets to get the image on both sides without any effort. Later, you'll use this effect when doing guided tours.

To create an individual picture, you need to do the following:

  1. Create a thin vertical box.
  2. Apply the appropriate image as a texture, using ImageTexture.
  3. Adjust the size of the box to maintain the correct aspect ratio of the image.
  4. Place it on an island by using a Transform node. At the same time, you want to add a rotation so that the viewer needs a separate viewing position for each image in the world.

Creating separate boxes for each picture makes it even more "fun" when you're writing files by hand. It's a continual process of adjusting and reloading the files so that everything looks right. One of the many tasks involved was readjusting the boxes so that the corners weren't sticking into the island's hills. You'll discover similar problems when creating your own worlds.

Adding the Basic Guided Tour

If you don't specify a default viewpoint, then the browser puts you quite a distance away. To avoid this, add a viewpoint to the file. The first one declared is the default entry; in this case, it starts you off in the middle of the center island.

This is an art gallery, so everyone wants to view the pictures. In a real-life tour of an art gallery, people stop to look at the pictures, so you should, too, in your virtual gallery. For each picture, I created a viewpoint about 4 meters away looking straight at it. It's worth the planning effort to make sure you're facing in the correct direction. The amount of time you spend adjusting everything to get that right look can be huge, so a little planning never hurts.

Once you have these viewpoints, put them in a logical order so that the tour makes sense. You don't want visitors to your gallery jumping randomly around the scene. In Chapter 23, "Real-Life Examples: A 3D Gallery: An Advanced VRML World," when you continue developing this scene, you'll offer an automated guided tour. Having the viewpoints logically defined already will make automating the tour easier.

Adding Links to Other VRML Worlds and Pages

Although developing this world has been fun, remember that you need to make it a worthwhile place to visit, too. Since I've used my co-author's pictures in the gallery, it makes sense to link the pictures back to some description. In this case, I've set the links up to point to the corresponding file on the CD-ROM where that image was created.

  1. Create the basic image and box in the scene, as you normally would.
  2. Decide which page you're going to be linking it to.
  3. Build the Web pages.
  4. Place an Anchor node around the geometry you built in Step 1.

I found one of the images on the POVRay site, so I've supplied a link back to its home page so you can see where it came from. In Chapter 13, "Exploring VRML Browsers and Development Tools," you learned about the Anchor node; now you get to use it properly for the first time. Surround each of the pictures' Transform nodes with an Anchor-that's all you need to do to provide links. For example, the Anchor declaration for the picture linking back to the POVRay site looks like the following:

Anchor {
    url "http://www.povray.org/"
    children [
        # place the code to produce the picture here
    ]
}

For this chapter, all the links are to other HTML files. You could make the links point to another VRML file, as demonstrated in a previous chapter. There's no need to use the parameters field, either, because this is just a pure VRML document. It is possible to create worlds that use HTML frames. The next workshop, in Chapter 23, explores the use of multi-framed documents combining VRML and HTML.

Adding a Background

A few things are still missing in your VRML world. Every world you've created so far has had a black background. For some people, this is fine, but basic black can get a bit boring. Try spicing up the background a bit with the Background node.

The Background node is very handy; by using it, you can specify pictures or a color gradient for sky colors. You can even use a combination of colors and pictures. I like the idea of those islands up in space, so I found a star picture I'm going to use as the background. To do this, put the URL of the image file in the Background node, as shown here:

Background {
    eventIn      SFBool   set_bind
    exposedField MFFloat  groundAngle  []
    exposedfield MFColor  groundColor  []
    exposedField MFString backUrl      []
    exposedField MFString bottomUrl    []
    exposedField MFString frontUrl     []
    exposedField MFString leftUrl      []
    exposedField MFString rightUrl     []
    exposedField MFString topUrl       []
    exposedField MFFloat  skyAngle     []
    exposedField MFColor  skyColor     [ 0 0 0  ]
    eventOut     SFBool   isBound
}

Which one of the six fields for holding image file URLs do you pick? Usually, you choose the ones defining the sides. The background pictures are mapped onto a box surrounding the world, with each URL referring to one side of the box. If you want, you can specify a different image for each side. In this world, I wanted the whole lot to appear in a star field, so I put the same image file URL in all the url fields.

The other method of putting in a background is specifying a collection of colors and angles. You need to supply one more color than the number of angles because the colors go between a pair of angles, giving a ring effect. There are two fields: one for the ground and the other for the sky. You can combine the images and colors together. If you don't specify an image, then the color is used. Therefore, a common tactic is specifying the images around the sides but letting the sky/ground color show through on the other two-the top and bottom for the box that defines the background.

You want the star background to totally envelop the world, so the same declaration goes in each of the URL fields and nothing goes in the color field.

Making the User Fit the Scene

Sometimes you want to create your model in a scale that's different from what's normal. For example, a chemical model would be no good drawn at real scale. However, you need to consider the problem of fitting the user to the scene. The NavigationInfo node is used to give the browser some basic information about the properties of the user in the scene:

NavigationInfo {
    eventIn      SFBool   set_bind
    exposedField MFFloat  avatarSize       1.0
    exposedField SFBool   headlight        TRUE
    exposedField SFFloat  speed            1.0
    exposedField MFString type             "WALK"
    exposedField SFFloat  visibilityLimit  0.0
    eventOut     SFBool   isBound
}

The user can set the size, as well as other basic properties. By now, you've probably discovered the options available by clicking the right mouse button. Several of these options can be predefined when the scene is loaded by specifying the values in the NavigationInfo node. A detailed description of all the options is given in Chapter 13.

When you start talking about virtual reality, you'll see the word avatar pop up quite frequently; it refers to the user's representation in the virtual world. The avatar is what other people in the world see and also how the world reacts to your presence as you navigate through it.

The avatarSize field is particularly important in most VRML files. It contains several values that specify how the avatar will behave. The first value is your collision radius-how close you can get to objects. The second value defines how high your "eyes" sit above the terrain. VRML 2.0 allows you to define a terrain by using the ElevationGrid node. As you wander over one of these nodes, the browser maintains the specified distance above the ground.

If you have collision detection running, then the third value defines what the maximum step height is that the user can negotiate. Anything higher than this value and you will have to make the avatar negotiate (fly?) the obstacle. In the world you designed, the steps are 2 meters apart, so the step height needs to be more than 2 meters so you can "walk" up the stairs without any extra effort. However, in this scene I didn't add collision nodes to the stairs. Feel free to do this and experiment with the results.

Visibility limit, specified in the visibilityLimit field, defines how far you can see. If you have a very large world, then the value supplied in this field is important. If it's too small, then the visibility limit would cut out a lot of the scene that might need to be rendered. The headlight field sets the default state for its use, and the type field specifies what sort of navigation you want to do. The types of navigation available were defined in Chapter 13 and include the types Walk, Examine, and Fly.

The Final Touches

The island in the middle is still a little blank, so to spice it up a bit I'm going to drag out the tree and marble columns from previous chapters and place them on the island. The columns will form archways leading to the stairs, and I'll add a block across the top of the columns to complete the arch. For consistency, the blocks use the same marble texture.

Now I get to have a bit of fun. For each staircase, I'm going to put a message up as well. VRML does have a 3D text function, but getting the text to line up can be tricky. The most reliable method is to create the individual signs as transparent GIF images, using all the 3D effects you've learned from my co-author, and then place them on a plane that sits very close to the top block. One of the benefits of taking this approach is that you can create shadowed highlights you wouldn't normally get from creating the scene in VRML 3D text.

Adding Information to the Scene with WorldInfo

The final node covered in this chapter is WorldInfo. This one doesn't display anything on the screen; it just contains a series of text strings. The title field is the world title that normally appears in the browser's title bar. It has the same effect as the <TITLE> HTML tag.

If you want to store other information, like copyright notices, in the scene, then you put this in the info field. The browser might take this information and display it in a separate dialog box, like the infamous About box. A proposal has been put forward to place meta information in the info field, as well. The proposal uses essentially the same fields and names as the <META> tag. At this stage, it's not a recommended use of the info field, but it is valid nonetheless.

Workshop Wrap-up

There you have it-your first fairly large VRML world. The sample file was created all by hand; not a single modeling tool was used. Most of the file is consumed by the definition of each object type. Once you have the definition, then it's repeatedly cloned with the USE command. Imagine how much bigger the file would be if you had to declare each object explicitly.

With a bit of effort and time, you can create very cool worlds like the one presented in this chapter. Apart from the face sets for the islands, the rest of the scene was constructed using just the basic primitives with some color and textures thrown in. Careful design in the first place means you can spend more time playing around with things later. Also notice how I've used the viewpoints to act as a guided tour in a logical sequence around the art gallery, rather than use random ordering. Details like this are what make the difference between an average world and a good one.

Next Steps

Now that you've mastered the basics of VRML, it's time to move on to the more advanced topics covered in Part V, "Advanced VRML Techniques":

Q&A

Q:
Did you really write that whole scene by hand?
A:
Yes. All I used was Notepad with Netscape 2.02 with CosmoPlayer beta 1a. There were some serious memory leaks that resulted in having to shut down Netscape and restart it; however, they were the only two tools used. A lot of the pieces I just pinched from other files-like the columns and trees-so this makes the development time much shorter. Once you're happy with the basic design, then it's just a matter of using a Transform node to move copies around the scene graph.
Q:
Where did you get the textures from?
A:
Apart from the sign textures-which were created in Paint Shop Pro-everything can be done just by using standard textures and pictures straight from the Internet. As you can see, all the big pictures came straight from my co-author without any modifications. These days most of the modeling tools come with a collection of textures and images that can be used to create your worlds with. Also check out places like Netscape's background texture page or one of the Internet search engines for other places.
Q:
What other sorts of things can I do with VRML? This 2.0 stuff looks just like the 1.0 worlds. Why do I need to upgrade?
A:
VRML 1.0 is designed only for static scenes. So far, what you've covered is the equivalent functioning of 1.0, but with the 2.0 syntax. In the next part of the book, you get into what this latest version is all about: creating moving worlds.