Chapter 25

Creating VRML Worlds


CONTENTS


In Chapter 25, you learned about creating, placing, and manipulating primitives in VRML. In this chapter, you'll take that knowledge and build on it to create more convincing VRML worlds. Aside from appearance and color issues, you'll look deeper into how to create efficient VRML worlds, and how to add hyperlinks that make them useful for maneuvering on the World Wide Web.

Primitive Appearances

You have two basic alternatives for creating different effects and appearances on primitives in VRML: the Material and Texture2 nodes. Material is used to control the colors assigned to the shapes. Texture2 is used to add graphics files as textures to your shapes.

The Material Node

The Material node accepts a number of basic properties: diffuseColor, ambientColor, emissiveColor, specularColor, shininess, and transparency. All of the numbers involved have values from zero to one. The following is the format:

Material {
   diffuseColor red_num green_num blue_num
   ambientColor red_num green_num blue_num
   emissiveColor red_num green_num blue_num
   specularColor red_num green_num blue_num
   shininess number
   transparency number
}

The first four properties accept values for each of the red, green, and blue channels for the color desired. The values can be any decimal between zero and one (although using a decimal past the "hundredths" place, like .507, is fairly useless).

Most important among the color values is probably diffuseColor, which is essentially the basic color of your primitive. The value ambientColor is sometimes described as "how dark the color is" and it's generally a slightly darker version of the same color as diffuseColor. Look at the following example:

Material {
   diffuseColor 0 0 1
   ambientColor 0 0 .8
   }

This sets the basic color to blue, with a slightly darker blue used for the ambientColor. The emissiveColor property determines what color your shape will be as it fades into the background. Generally, you'll want this to be darker-meaning you use a smaller number.

The property specularColor is used to represent the color of light bouncing off of the shape. Depending on how surreal your world is, you'll probably want this to be a white/yellowish color. An example of both these properties is the following:

Material {
   diffuseColor 0 1 0
   emissiveColor 0 .2 0
   specularColor .8 .6 .8
   }

This basically translates to "bright" green in the foreground and darker green in the background, with a yellow/white (with hints of green) as the "light-bouncing" color.

Note
Remember with these red, green, and blue values that as you approach one with all values, you get closer to white. 0,0,0 is black. Everything in between is a spectrum-each color is at its "brightest" at one while the other colors remain zero.

The last two Material properties are simply levels from zero to one. Both are fairly self-explanatory. shininess suggests how much light bounces off the object; transparency affects how solid the material appears. The default value for shininess is 0.2 (a little shiny); default for transparency is 0, or completely solid.

Example: Adding a Little Color

Let's work a little with the last example from Chapter 24, adding a little color to your ice cream cone for Kong. Notice that the Material node affects all other shapes in a particular Separator.

Create a new VRML document (or add the Material nodes to your work from last chapter), save it with a .wrl extension, and then enter Listing 25.1.


Listing 25.1  color.wrl  Changing the Colors of VRML Objects
#VRML V1.0 ascii
#
# Moving and flipping
# VRML primitives

Separator {
   Transform {
     translation 0 0 0
     rotation 1 0 0 3.14
   }
   Material {
     ambientColor .6 .4 .2
     diffuseColor .7 .5 .3
     emissiveColor .6 .4 .2

   }
   Cone {
     height .5
     bottomRadius .12
   }
}
Separator {
   Transform {
     translation 0 .25 0
   }
   Material {
     ambientColor .9 .9 .8
     diffuseColor 1 1 .5
     shininess .9

   }
   Sphere {radius .20}
}

The best advice for the red, green, and blue (RGB) values is simply to experiment with them until you get what you feel is close to the color you wanted. If you have a graphics program available to you, you might use its color palette to try different RGB levels to achieve the desired colors, then test them in your VRML browser.

In the example, I'm basically going for a light-brown cone and a yellowish sphere, which is meant to suggest a sugar cone and vanilla ice cream. I've also altered the translation values to try to line the ice cream up on top of the cone (so you can see the contrast). It loses something in this screenshot, but figure 25.1 will give you an idea of how this looks.

Figure 25.1 : Kong's cone in color. (The picture is black and white not the cone).

The Texture2 Node

The basic point of the Texture2 node is to allow you to wrap a graphic around a primitive. (And no, I don't know what happened to Texture1.) Texture2 takes the properties filename, wrapS, and wrapT. The basic format is the following:

Texture2 {
   filename "image URL"
   wrapS REPEAT/CLAMP
   wrapT REPEAT/CLAMP
}

Now, honestly, there's a lot more to the wrapS and wrapT, but it's rather confusing to me. Here's the scoop: many browsers tend to implement Texture2 in only the most basic ways. If your browser happens to support these two properties, then setting wrapS and wrapT to CLAMP forces just one instance of your graphic to be pasted on the primitive. Using REPEAT for both will tile the graphic all over the primitive. An example of this is as follows:

Texture2 {
   filename "earth.gif"
   wrapS CLAMP
   wrapT CLAMP
   }

REPEAT is the default value for both, so there's no need to include the properties if you want the image to tile onto your primitive.

Example: Covering Up Primitives

Here's a good example of how images will cover different primitives. Just about any graphics file will do-just make sure you have it in the same directory as the VRML file. Create a new .wrl file and enter Listing 25.2.


Listing 25.2  texture.wrl  Adding Textures to VRML Objects
#VRML V1.0 ascii
#
#adding Texture
#to VRML primitives
#

Separator {
    Separator {
        Texture2 {
           filename "wood.gif"
           wrapS CLAMP
           wrapT CLAMP
        }
        Translation { translation -3.5 0 0 }
        Sphere { }
    }  
    Separator {
        Texture2 { filename "wood.gif" }
        Translation { translation -1.25 0 0 }
        Cone { }
    }
    Separator {
        Texture2 { filename "wood.gif" }
        Translation { translation 1 0 0 }
        Cylinder { }
    }
    Separator {
        Texture2 { filename "wood.gif" }
        Translation { translation 3.5 0 0 }
        Cube { }
    }
}

Use whatever graphics file you'd like in place of wood.gif. You're probably better off with a texture, but it can be just about as much fun with the picture of a cartoon character or politician. In fact, you can make your VRML world look a little like some of the popular movies that have included VR by creating a flat cube for the face and pasting a graphic to it using the CLAMP values.

I'd also recommend that you experiment with different values, graphics, and primitives using this example.

Adding Hyperlinks in VRML

Links in VRML just require another node, the WWWAnchor node. This one accepts two basic properties, name and description as in the following example:

WWWAnchor {
   name "URL"
   description "Alternate text"
   }

The WWWAnchor node works a lot like a Separator node in that it actually includes the primitive and whatever descriptive nodes you've used to affect that node. An example might be:

WWWAnchor {
   name "http://www.fakecorp.com/worlds/world2.wrl"
    description "Into the next world"
   Separator {
        Texture2 { filename "wood.gif" }
        Translation { translation -1.25 0 0 }
        Cone { }
  }
}

The name property can accept any sort of URL, whether it's another VRML world, a regular HTML document, or a hypermedia link. The description text is similar to ALT text for the <IMG> tag. Some VRML browsers will allow the ALT text to pop-up on-screen to help the user decide if this is a useful link for them.

Example: Linking in Your VRML World

In this example, you'll create some basic primitives and see how different links react when clicked in your VRML world. Create a new VRML document and enter Listing 25.3.


Listing 25.3  links.wrl  Creating HTML Links for Your VRML Objects
#VRML V1.0 ascii
#
#adding links
#to VRML primitives
#


WWWAnchor {
   name "index.html" #A regular HTML page
    description "To Our Index Page"
   Separator {
        Translation { translation -3.5 0 0 }
        Sphere { }
    }
}
WWWAnchor {
   name "demo.moov" #A hypermedia link
    description "See the Presentation (QT 1.4mb)"
    Separator {
        Translation { translation -1.25 0 0 }
        Cone { }
    }
}
WWWAnchor {
   name "office.wrl" #Another VRML world
    description "Move to the Office"
    Separator {
        Translation { translation 1.25 0 0 }
        Cylinder { }
    }
}

You'll probably want to change the names of the different files (in the links) above so you can use files hanging around on your hard drive (make sure they're all in the same directory as your VRML document). You should also experiment with different types of files to see how things are loaded and passed between the HTML browser, the VRML browser, and other helper applications.

Note
Some VRML browsers download the .wrl file to the user's hard drive and then access it from there. That means that relative links in the .wrl file will break, since the links will now be "relative" to the user's hard drive. For this reason, it's a good idea to use absolute URLs (even for your texture images) if you add VRML worlds to a real Web site.

Back in your VRML world, things really haven't changed much. In some browsers, primitives will be highlighted when they're clickable. In others (like mine), you'll just get a slightly different cursor (see fig. 25.2).

Figure 25.2 : The cursor will change from this arrow to a crosshair for links.

More Fun with Shapes

So far, you've been dealing with the built-in primitives of VRML, and you've completely passed over the possibility of creating your own shapes. Is it possible? Sure. But it'll take some thinking. It's also possible, and timesaving, to use special commands to give your shapes "nicknames" for referring to shapes you can create. The advantage is that it then takes one line of VRML code to create another one!

More Nodes: Coordinate3 and IndexedFaceSet

Creating your own shapes takes two steps, and two different nodes. The first node, Coordinate3, is used to layout the coordinates for your new shape. This doesn't actually create anything in the VRML world. It's more of a template for the next node, IndexedFaceSet. Using this second node, you actually draw the faces of your shape by specifying the points for each.

Tip
Draw your object in as close to 3D as possible (or make it in clay or origami), and label the points (starting with zero). This will help you create it in VRML.

The Coordinate3 node is used with the point property in the following format:

Coordinate3 {
   point [
      x1-coord y1-coord z1-coord, #point 0
      x2-coord y2-coord z2-coord, #point 1
      ...,
]
}

Each coordinate for your shape requires an X, Y, and Z coordinate. This creates a point in your VRML world. Get enough points together, and you'll have a shape. But you won't be able to see anything.

The next step is to add the IndexedFaceSet node. The order in which you assign points in the Coordinate3 node is noticed by VRML, and you can use that to determine what points make up each "side" of your shape. The number -1 is used to tell IndexedFaceSet that you're done with that side. IndexedFaceSet uses the property coordIndex for the listing of sides, as in the following format:

IndexedFaceSet {
   coordIndex [
      point_num, point_num, point_num, -1, #side1
      point_num, point_num, point_num, -1, #side2
      ...
   ]
}

You should probably also consider that not every side necessarily has three points-in fact, many won't. That's why you use -1 to represent the end of a shape. Depending on your mood and the number of advanced degrees in mathematics you have, the sides of your shapes could have many, many points to connect.

Example: Up on the House Top

Here's a shape you might want to use in your VRML world-a rooftop. It takes six points and five sides to create this particular rooftop. Fortunately, you can limit the number of dimensions and triangular hypotenuses you're working with.

Figure 25.3 shows you a sketch of the rooftop, including the coordinates you'll use. It doesn't look like it, but the bottom points of this roof all sit at the same Y coordinate. It's tilted to show 3D on this 2D page.

Figure 25.3 : Here's your shape and the coordinates for each point.

Actually, it's not that bad, is it? Architects could learn from the symmetry of your rooftop. Now look again and see which sides you're going to need to draw with the IndexedFaceSet node. Figure 25.4 shows those sides.

Figure 25.4 : Here's your shape with the sides you need to draw.

Now, armed with all this information, you're ready to code this roof! Create a new VRML document and enter Listing 25.4.


Listing 25.4  rooftop.wrl  Creating the Rooftop Shape
#VRML V1.0 ascii
#
#Creating our own
#rooftop shape
#

Separator {

  Coordinate3 {
     point [
       5 0 0,       #0
       5 -5 -5,     #1
       5 -5 5,      #2
       -5 0 0,      #3
       -5 -5 -5,    #4
       -5 -5 5,     #5
    ]
      }

  IndexedFaceSet {
     coordIndex [
       0, 1, 2, -1,     #Side A
       0, 1, 4, 3, -1,  #Side B
       3, 4, 5, -1,     #Side C
       0, 2, 5, 3, -1,  #Side D
       5, 2, 1, 4, -1,  #Side E
     ]
  }
}

Notice in IndexedFaceSet that you're able to create the different sides required for this shape-both the triangles for the ends and the four-pointed rectangles for the slopes (and bottom) of the roof. You can see this roof in figure 25.5.

Figure 25.5: The rooftop complete.

Instancing

One of the major concerns with VRML worlds, especially as their popularity begins to grow, is the size of the world files. Currently, low bandwidth connections make using large VRML worlds more of a "cool toy" than a reasonable alternative to HTML. Higher bandwidth may change that in a future, and it's reasonably easy to see a time when VRML will make navigating the Web very interesting.

VRML itself addresses this problem with file size by noticing that many of the shapes you'll use to create your world happen to be rather similar to one another. You might want to create a world, for instance, with a number of houses in it. Creating a complete house every time can be a little intimidating for the designer, as well as expensive in terms of file size. (Look how much code it took just to create the rooftop!) So, VRML gives you something called instancing.

This is a little like creating an object in JavaScript and similar programming/scripting languages. Basically, you just assign a "nickname" to a particular node or group of nodes. When you want to use that node again, you just type the keyword USE, followed by the nickname, as in the following example:

DEF beach_ball Sphere { radius .5 }

USE beach_ball

This is a simple example, but notice how powerful this ability is. Now, instead of using all of the code back in Listing 25.4 to create another rooftop, you could use the DEF keyword to create a nickname for the entire process-like my_roof-and you could duplicate them to your hearts' content.

DEF needs to be used with a node, but that node needn't stand on its own. You can easily assign a DEF name to a Separator node, which could encompass an entire defined "object" in your world. You can even assign DEF to non-drawing nodes, as in the following example:

DEF make_red Material {
     ambientColor .9 0 0
     diffuseColor 1 0 0
     emissiveColor .9 0 0
   }

Now the command USE make_red can be used as a one-line statement to add red to subsequent nodes within your VRML world.

Example: A VRML Neighborhood

Using instancing, you can take your rooftop, add a house for it, instance the house, and create a complete neighborhood in short order. Create a new VRML world document and enter Listing 25.5.


Listing 25.5  nbr_hood.wrl  Using DEF for Cloning
#VRML V1.0 ascii
#
#Creating our own
#neighborhood
#

Transform {                   #move whole world away and below 1
    translation 0 -1 -50
}

Separator {                   #create the ground
   Material {
     ambientColor 0 .9 0
     diffuseColor 0 1 0
     emissiveColor 0 .5 0
   }
   Cube {
     height .01
     width 100
     depth 100
   }
}

DEF my_house Separator {     #define this as a my_house instance


  Material {                 #add color to main house
     ambientColor 0 0 .9
     diffuseColor 0 0 1
     emissiveColor 0 0 .5
   }

Separator {                  #move cube up above ground
     Transform {
         translation 0 2.5 0
     }

  Cube {                     #create house
     height 5
     width 8
     depth 8
  }
}

  Material {                 #add color to roof
     ambientColor .4 .9 .4
     diffuseColor .5 1 .5
     emissiveColor .5 .5 .5
  }  

  Coordinate3 {              #create roof points
     point [
       5 10 0,      #0
       5 5 -5,      #1
       5 5 5,       #2
       -5 10 0,     #3
       -5 5 -5,     #4
       -5 5 5,      #5
    ]
      }

  IndexedFaceSet {           #draw sections of roof
     coordIndex [
       0, 1, 2, -1,     #Side A
       0, 1, 4, 3, -1,  #Side B
       3, 4, 5, -1,     #Side C
       0, 2, 5, 3, -1,  #Side D
       5, 2, 1, 4, -1,  #Side E
     ]
  }
}                            #bracket ends this DEF instance

Separator {                  #new house
   Transform {
       translation 15 0 15
       rotation 0 1 0 1.57
   }

   USE my_house
}

Separator {                  #another new house
   Transform {
       translation -25 0 -25
       rotation 0 1 0 1.57
   }
   USE my_house
}

So you define an instance for the entire house, and then simply type the USE command to add another instance of it. Of course, they're all the same color, but at least you can use Transform to put the house in another part of your world and rotate it.

If you did want to change the colors of your house, you'd probably want to break out the parts of my_house, perhaps creating my_roof and my_house so you could use different Material nodes for each. Of course, you could always have different DEF statements for Material, so that eventually USE had houses like the following:

Separator {
USE make_red
USE my_roof
USE make_green
USE my_house
}

That creates an entire house in four lines! Plus, once you get a glimpse of your little VRML neighborhood, you'll probably want to figure out how to change house colors quickly (see fig. 25.6).

Figure 25.6 : Here's you, uh, smutty little village.

More VRML

Like our discussion of JavaScript, there's a lot more to VRML that can't be covered in this book. But, you've got a great start. For more VRML info, check out the following Web sites:

Summary

After you've learned to create the basic shapes in VRML, you can move on to making things feel more like a "world." Using the nodes Material and Texture2, for instance, you can add color, images, and light properties to your shapes.

The next step is to make your world useful for the Web-so you need to add hyperlinks. The WWWAnchor can be used to make any primitive or other shape a hyperlink to just about anything: another VRML world, an HTML document, or even a hypermedia file.

You can also create your own shapes. Using the Coordinate3 and IndexedFaceSet nodes, you can tell your VRML browser where the coordinates for your shape are-and then you can use those points to tell the browser where to draw the sides of your shape. These two may be among the most powerful nodes for serious VRML world creators.

Instancing, however, is easily the most powerful node for the lazy creator. Not to mention that it's good for low bandwidth connections to your VRML world. With instancing, you can create "nicknames" for your VRML objects-even from something as big as a house-and create another like it with a one-line command.

There's more to it than that, and the end of this chapter details some Web sites for learning more about VRML. Hopefully, you've got a good enough start to have some fun, though.

Review Questions

  1. Choose the one that would create a darker color:

    Material {diffuseColor .9 0 0}
    Material {diffuseColor .1 0 0}
  2. What RGB color are you working with in question 1?
  3. What's the major difference between REPEAT and CLAMP? Which one do you never actually need to type?
  4. Why would it be best to use absolute URLs for the following:

    Texture2 { filename "URL" }?
  5. What other VRML node works a lot like WWWAnchor?
  6. What's wrong with the following?

    WWWAnchor {
       name "http://www.fakecorp.com/index.html"
       description "Back to Index"
       Material {diffuseColor 0 0 .5}
         }
  7. For what is the -1 in the coordIndex property of IndexedFaceSet used?
  8. In the following:

    Coordinate3 {
       point [
         0 1 -1
         5 1 -1
         5 -1 -1
         0 -1 -1
       ]
    }

    What is the point number assigned to {5 -1 -1}?
  9. True or false. You can create an instance of the Transform node.
  10. When you create the primitive sphere {} and view it in a browser, where (virtually) are you in relation to the sphere?

Review Exercises

  1. Using any series of primitives or world you've created, use the Transform node to move the entire world away from the opening point-of-view.
  2. Using the rooftop you created in Listing 25.5, create a rooftop with different colors for each (or at least a few) of the sides.
  3. Change Listing 25.5 so that you can choose different colors or textures for each house and roof you create.
  4. Change Listing 25.5 so that each house becomes a clickable hyperlink. Also, use the AsciiText node from Chapter 24 to add a label to each house.