All Categories :
VRML
Chapter 16
Adding a Dash of Reality
-by Justin Couch
CONTENTS
So far, you've learned to populate your VRML worlds with primitives
and models. You've also learned some tricks for painting objects,
but using basic colors is just the first step. With textures,
you can make your VRML objects much more visually appealing, but
there's still a lot to do to make your worlds realistic. What's
missing? Well, there are no lighting effects. Another effect that's
missing is not being able to walk through walls, and you also
need a way to show details when you get close to an object. Luckily,
VRML addresses each of these problems.
This chapter introduces the basic techniques for adding that extra
touch of realism to your world:
- Start with the basic lighting types provided in VRML.
- Make walls and objects you can no longer walk through.
- Show how to change the amount of detail on an object as you
get closer to it.
- Find out how to set up different cameras to give you several
viewpoints.
By the time you've finished this chapter, you should be able to
create some very believable worlds. Just remember that the more
detail you put in, the longer it will take to download and the
slower the world will run.
If you have spent any time ray-tracing images for your Web pages,
you will have no problems with the VRML light source models. However,
you might notice some other differences. Unlike ray-tracing, there
are no ambient light settings. If you don't specify an emissive
or diffuse color value for your objects, you can't see them.
VRML defines three types of light sources: SpotLight, DirectionalLight,
and PointLight. Their names are pretty self-explanatory. You'll
look at examples of each, using a basic test scene that's shown
in Figure 16.1 without any lights except the headlight.
Figure 16.1: The basic test scene with the headlight turned on.
Note |
To show the effects of the light sources, define only diffuseColor values on all your objects. Chapter 13 briefly outlines the effects of the various color types used in the Material nodes. For this example, you'll define only diffuseColor values because emissiveColor would ruin some of the lighting effects. Emissive light sources emit their color even without other light sources in the scene. To see the effects of external lights, you need to make sure that the only lighting effects are produced by the lights themselves.
|
Although I mentioned that there were no ambient light sources,
this isn't completely correct. There are no separate ambient
light sources, but within each light node, there's a field value
that can control the ambient light intensity. The resulting ambient
light is the sum of all the values defined in the light nodes.
For each of the examples, you'll see what the light looks like
in the scene, as well as commentary on the effects.
DirectionalLight specifies a direction along which the given light
color travels; it's the equivalent of a distant light like the
sun. All the rays of a directional light are parallel.
- Start by specifying a node called the DirectionalLight node.
One of the fields within this node is the direction field.
- Now, set the direction field values to -1
-1 -1. This should get the sun shining over your shoulder
toward the objects. Place this node at the top of the file, as
shown in Listing 16.1.
Listing 16.1. Extract from the file illustrating DirectionalLights.
#VRML V2.0 utf8
#
# A world to show the effects of various light sources
# The directional light sources
DirectionalLight {
direction -1 -1 -1
}
- The DirectionalLight node also has a field that sets the intensity
of the light, but leave this at the default value for this example.
- After the light is set, you need something for it to illuminate,
so create a scene. You will show only the box portion of your
test scene. Remember to use the diffuseColor field to set the
color of the box.
# The basic scene
Transform {
translation 1 .5 1
children [
Shape {
appearance Appearance {
material Material { diffuseColor 0.7 0.1 0.1 }
}
geometry Box { size 1 1 1 }
}
]
}
etc....
The same scene is shown again, this time with the DirectionalLight
added, in Figure 16.2.
Figure 16.2: Directional lighting for the entire scene. The headlight has been turned off.
Now look closely at the scene, particularly at the floor. What
do you notice? That's right-there are no shadows. In VRML, everything
is lit equally, regardless of whether there's another object between
it and the light source.
OK, try experimenting a little bit. Add another directional light,
this time coming from over your other shoulder, but place it within
the Transform node that holds the sphere. To increase the effect,
the intensity of the first light is turned down to 0.2. If you're
using the CosmoPlayer browser, it will light up all the objects,
but if you look at the world with the CyberPassage browser, it
will light up none of them. Which one is correct? Strictly speaking,
neither of them.
According to the VRML specification, the DirectionalLights are
limited in scope to the parent grouping node. In this case, there
should be light only on the sphere, nowhere else. The VRML specification
has a small rider saying that some low-end rendering engines won't
support lighting on a per object basis, so lights should be placed
as high as possible in the scene graph.
If you want to highlight a particular object, then DirectionalLights
aren't the technique to use. You should be using a SpotLight to
provide a focused beam of light aimed at a particular direction
from a given point. In this example, you want to highlight the
sphere, so add the SpotLight node into the Transform node where
the sphere is located:
- Start by adding a SpotLight within the children field of the
sphere's Transform node. This will highlight only the sphere.
- Then add values for the direction field, so that your light
knows where to point, and the coordinates of the location where
your spotlight will be placed.
Now if you look at the world, it's very dark. The SpotLight doesn't
seem to have taken effect. If you look closely at the SpotLight
definition, it has a default radius of 1.
You have placed the light at a distance of more than 10 meters
from the object, so the objects aren't within the SpotLight's
radius. Anything outside this radius results in no light, so you
need to increase the radius a bit:
- Add the radius field and give it a value of 30
to ensure that the sphere is within this radius.
- The final fields you specify are the beamWidth and cutOffAngle
fields. To get the feel of how spotlights work, you should experiment
with these field values. They control how focused the light is.
The wider these angles, the more diffuse the effects become. This
section of the scene should look like the code in Listing 16.2.
Listing 16.2. Using a SpotLight in place of a DirectionalLight.
Transform {
translation -1 .5 1
children [
SpotLight {
direction 1 -1 -1
location -5 5 5
radius 30
beamWidth 1.57
cutOffAngle 1.57
}
Shape {
appearance Appearance {
material Material { diffuseColor 0.1 0.7 0.1 }
}
geometry Sphere { radius 0.5 }
}
]
}
Figure 16.3 shows the results of the SpotLight on the sphere.
Remember that different browsers have different rendering engines,
so there may be some difference in the image quality between browsers.
Figure 16.3: Using a SpotLight instead of a DirectionalLight; this time, Sony's CyberPassage is used as the browser.
SpotLights are one of those tricky nodes that need a bit of playing
with to get the right effect. Generally, you won't use a SpotLight
without any other lighting source. While testing the examples
for this book, you've discovered that SpotLights aren't completely
implemented.
The world seen in Figure 16.3 included a directional light, but
with no headlight. Notice how dark the picture is. What really
needs to be added to the scene is some ambient light to brighten
the whole scene. The SpotLight can then be used to create highlights.
The last item of note about using SpotLights is that they aren't
constrained by the parent nodes. Normally, you would place a SpotLight
within a Transform if you wanted to keep it relative to some object.
If this isn't necessary, then you should place them at the top
of the file with the rest of the light sources.
When you're trying to create a scene normally, you want to model
a lightbulb that sends light in all directions. To do this, you
would use the PointLight node. Try placing a PointLight in the
middle of the four objects to show its effect:
- The best place for the PointLight node is at the top of the
file, so begin there.
- The PointLight node has many of the same fields as the other
lights, but you need to set only two for this example: radius
and location. The code is as simple as this:
PointLight {
radius 10
location 0 1 0
}
Figure 16.4 shows the results of the PointLight placed in the
middle of all the objects in your scene; notice that again, no
headlight is used.
Figure 16.4: A PointLight placed in the middle of the objects. Note the graded shadowing on the side of the cylinder and cube.
A radius of 10 is used to
make sure the lighting effect is used over the whole scene. You
should experiment with the radius to see how it affects the lighting.
As with the SpotLight, there's also an attenuation field to control
the drop-off of light intensity. The same comment about its use
also applies; that is, attentuation is not really usable because
most PC browsers aren't supporting it.
Putting Them All Together
Now that you've explored the various light source types, this
would be a good time to try combining them all into a single scene.
Using the same sample scene, put all the lights in; the results
are shown in Figure 16.5.
Figure 16.5: The result of turning on all the lights.
Note that the effects of the SpotLight are hardly visible; directional
lights contribute the majority of lighting to the scene. Look
at the cube for the best illustration of the two lighting sources.
The sphere, which showed most of the SpotLight effect, has had
this all but removed by the domination of the DirectionalLight.
One of the biggest problems in VRML 1.0 was that you couldn't
stop people from wandering through objects. This really became
a problem when you were building houses. There was no need to
put a front door in because people could walk straight through
the wall! This had to be fixed, so in Version 2.0 a Collision
node was added. It works like any of the other grouping nodes.
By simply adding the Collision node as a parent, you get instant
solid walls.
- Start with a simple object, such as a cylinder. You have already
added a bit of color to make it pretty, but that's it. Now try
walking toward it-you go straight through it!
- Add a Collision node, which works like all the other group
nodes. To turn on collision detection for the cylinder, just put
it in the children field of the Collision node. This is what the
code looks like:
Listing 16.3. The simple cylinder you can no longer walk through.
#VRML V2.0 utf8
#
# Using Collision Detection
Collision {
children [
Shape {
appearance Appearance {
material Material { emissiveColor 1 0 0 }
}
geometry Cylinder {}
}
]
}
Tip |
You can create collision detection on a group of separate objects by putting them all in the children field. You won't be able to walk through the objects, but you can walk around them. This effect can be used to create doorways, so that you can walk through the doorway, but not the walls.
|
- The Collision node also has some extra little tricks you can
use. The definition contains a field called proxy that
takes a single node for its value. Put a sphere in as the node
for this field. This proxy field builds an invisible "force
field." Its code is simple:
Listing 16.4. An expanded collision detection field, with a
sphere added around the cylinder.
#VRML V2.0 utf8
#
# Using Collision Detection
Collision {
children [
Shape {
appearance Appearance {
material Material { emissiveColor 1 0 0 }
}
geometry Cylinder {}
}
]
proxy Sphere { radius 2 }
}
Now you can no longer get anywhere near the cylinder. In the previous
example, you could walk up and touch it, but now you can't get
anywhere near it. This is because the proxy field defines the
shape of the collision detection field. Any drawing node can be
specified here, but obviously it makes no sense to define a complicated
shape since it has no visible geometry. The idea is to create
a simplified detection radius, not a more complex one.
Tip |
The proxy field can contain any type of node, allowing you to create any sort of collision boundary around your objects. For example, you could place a Group node around the object and put a collection of IndexedFaceSets in it to get exactly the behavior you want.
|
One thing guaranteed to slow down your VRML world is excessively
high levels of detail. However, when you're close to an object,
you want to see some detail. The problem is deciding what to put
in and what to leave out. By using the Level Of Detail (LOD) node,
you can get the best of both worlds.
LOD allows you to define a set of ranges and what you want the
object to look like in each range. In a way, this approximates
reality, as you gradually discern more detail the closer you get
to an object. This technique is helpful when you're using textures.
Texture mapping always slows down a world, so it's a good idea
not to use it until you're close to an object. Start with a simple
model: a cube that turns into a sphere.
- First, place the LOD node at the beginning of the file. Within
the LOD node, you can specify the range, which is the distance
at which the details show up, and a level field, where the details
are defined.
- With the LOD node in place, declare the range. Several ranges
can be specified. If the range field has two numbers, then there
are three levels of detail. You made the range value 5,
so there are only two levels: a cube and a sphere.
- The level field contains the details and starts with the highest
level of detail first. The final VRML file looks like this:
Listing 16.5. A quick LOD demo: A cube that turns into a sphere.
#VRML V2.0 utf8
#
# A simple LOD
LOD {
range [ 5 ]
levels [
# first level for greatest detail - a sphere
Shape {
appearance Appearance {
material Material { emissiveColor 0 0 1 }
}
geometry Sphere { radius .5 }
}
# second level minimum detail
Shape {
appearance Appearance {
material Material { emissiveColor 0 0 1 }
}
geometry Box { size .5 .5 .5 }
}
]
}
Warning |
When you view this file in a browser, you might notice that it always stays a cube-it never swaps to being a sphere. This is because the specifications allow the browser writers a lot of leeway to optimize the rendering speed. If the browser thinks it can handle a higher level of detail, then it will use that. The situation you get is that the cube is simpler to render than the sphere, so it gets used all the time.
|
Detail levels are always defined as being the most detailed first,
then fewer details as you go down the file. This doesn't stop
you from declaring the most detailed as the last item, but it
would sure confuse both your visitors and the browser. The specification
suggests you should declare as many levels as you need to get
smooth transitions, but this can create an awfully large source
file just for a simple smooth change. However, there are ways
around this; they will be covered in Chapter 18,
"Tricks to Optimize Your VRML Worlds for the Web."
Another way of getting the same effect is to create nested LOD
systems. A building might consist of one overall LOD, but within
the highest detail setting, there would be another LOD node controlling
the details of the windows and doors. A nested LOD structure uses
the same syntax as before. In each child, instead of putting a
grouping node like Shape in the previous one, you put in another
LOD node, as shown in Listing 16.6. Just because you have a nested
LOD at one range doesn't mean it needs to be in all of them.
Listing 16.6. Nested LOD outline example.
LOD {
range [ 5 ]
levels [
# first level for greatest detail - a sphere
Shape {
appearance Appearance {
material Material { emissiveColor 0 0 1 }
}
geometry Sphere { radius .5 }
}
# second level minimum detail
LOD {
levels [2.5]
# first level in nested LOD
Shape {
appearance Appearance {
material Material { emissiveColor 0 0 1 }
}
geometry Box { size .5 .5 .5 }
}
#Seconnd level in nested LOD
Shape {
appearance Appearance {
material Material { emissiveColor 0 0 1 }
}
geometry Cone {
bottomRadius .5
height 1
}
}
}
]
}
So far, you're viewing every file from the default position the
browser gives you. Often, this isn't convenient, so you might
like to define your own viewpoints; at other times, you might
want to offer a collection of different places to view the world
from. Using the Viewpoint node, you can do this and more.
- Your first task is to provide a different default viewing
position. Use the same model from the section on lighting to illustrate
this.
- The Viewpoint node uses the SFRotation
field type, the same one you saw in the rotation Transform node,
but this time it's combined with a position. This pair of values
defines where you are located and in what direction you are looking.
For your example, place the camera at (0,3,3) and then rotate
it down 45 degrees.
- You aren't limited in the number of viewpoints you can add
to a file, but the first one defined becomes the default viewpoint
you see when the file is first loaded. To distinguish all the
viewpoints in a file, the description field allows you to give
names to the different viewpoints:
Viewpoint {
position 0 3 3
orientation 1 0 0 -.785
description "default camera"
}
The world seen from the default viewpoint you set up is shown
in Figures 16.6a and 16.6b. The browser recognizes these extra
viewpoints and allows you to change the predefined viewpoint,
typically by clicking the right mouse button. In the CosmoPlayer
browser, click the right mouse button and select the Next Viewpoint
option from the pop-up menu to move to the next viewpoint position,
or you can use the Page Up/Page Down keys.
Figure 16.6a: The default entry view of your earlier model.
Figure 16.6b: The same scene now viewed form the second viewpoint.
Another handy field in the Viewpoint node is the jump field. If
the jump field is specified as false, the browser gives you an
animated sequence from your current position to the next viewpoint.
This is especially effective when you're giving guided tours.
The last field to experiment with is fieldOfView, which lets you
control the amount of the world that's visible at once. A small
value acts like a telephoto lens; a large value produces a wide-angle
lens effect. Most of the time, you use only the default value
because it gives a fairly good view of things.
Tip |
From this first view, you will notice quite a pronounced perspective on the objects. If you want to create an orthographic (that is, no perspective) camera effect, you can set the fieldOfView to a very small, almost non-zero, value (but definitely not zero) and set the distance fairly far away.
|
You've now been introduced to many of the effects used in VRML
worlds to make them more believable. Using them depends on what
sort of machine you expect people to be viewing your file with.
In the experience of the Terra Vista community, using the LOD
node had too many problems, both with its implementation (it didn't
like being used as a child of Transform) and also the performance
penalty it imposed.
Next Steps
You now have some reasonable-looking worlds, but there's still
a lot more for you to learn:
- To see a full demonstration of how to use VRML, go to the
next chapter, Chapter 17, "Real-Life
Examples: The VRML Art Gallery: A VRML World by Hand."
- If your world files are starting to get very large and slow,
head to Chapter 18, "Tricks to Optimize
Your VRML Worlds for the Web."
- Even more advanced VRML, with material on adding behaviors
to your world, can be found in Chapters 20,
"Interfacing VRML Worlds with Scripts," and 21, "Using
Java to Add Behaviors to VRML."
Q&A
Q: | I tried loading some of the examples in this chapter, but they look completely different, especially the ones on the lighting model.
|
A: | Usually this is because you have the headlight turned on. The headlight is a spotlight mounted just above where your virtual head should be and always points in front of you. This can be turned off either by clicking the right mouse button to get a pop-up menu in CosmoPlayer or using the View menu in CyberPassage.
|
Q: | What happens if I nest collision nodes?
|
A: | You've jumped ahead of everybody else! Nested collision nodes are allowed and are very useful when it comes to behavior programming in a later chapter. When you collide with something, each collision node produces its own event notification to the system, so it's useful and sometimes necessary to have nested collision nodes.
|
Q: | When I have multiple child primitives within a Collision node, what defines what can and cannot be collided with?
|
A: | Only the primitives themselves can be collided with. If you have two cylinders located some distance apart, then you can walk between them with no problems, but you still can't walk through the individual cylinders.
|