Mastering the Triangle last updated 2/8/2005.

From Total Beginner to Animating a Triangle

So that you don't get confused it may be wise to have your computer set to have the file extension visible.
To do this:
     click on "Start"
     select "Settings"
     click on "Folder Options"
     click on the "View" tab.
     uncheck the "Hide file extensions for known file types" if it is NOT all ready clear.
     click "OK"

We want VrmlPad installed on our computer. It is a tiny program and is very fast to install. It should only take a few minutes at the most from start of download to completing the installation.
Download the installation file for VrmlPad  from
You only need the small 456 KB version for what we want to do here. Save it to your desktop or where ever you want it. Once you have it downloaded the installation file install it by double clicking on it. there is no need to restart your computer after installation in order to use it so we can complete this task 100% while staying on the internet :)

We want to create a folder to put our example files in
To do this:

     Open up a new internet explorer or windows explorer window.
     Go to C:\My Documents
     Right click on the white in that folder so as to avoid clicking on any icon
     Select new
     Select folder
     Name it VrmlExamples
     Open the new folder

We want to be able to take advantage of being able to easily open any vrml file we see by right clicking on it and selecting edit
To do this:

    Open vrml pad then click on tools in the menu then select options
    Check "Associate VrmlPad with vrml files"
    Under that select "As Secondary Edit Action"
    Click OK

Lets now start writing the code for our triangle in vrmlPad.

The first line

   #VRML V2.0 utf8

must always be right at the begining of a VRML file. This line lets programs know that this file is a vrml file. There after where ever you see text starting with a "#" you know that the text until a new line begins is just a comment. Comments are only for us humans as reminders they have no effect on how the vrml file behaves

Nodes and Fields

Think of a node as a building block used in VRML. Nodes often contain other nodes and fields. Fields are data variables used for setting properties in a node. For example a field can specify the speed, color or location of a node. Nodes and fields that belong to a node are members of that node. All members of a node, be they fields or nodes all have a name. The name of a node must not be confused with the type of node it is. In all built in VRML nodes, names always begin with a lower case letter. When we want to use a node that is a member of a node we must make an instance of the node or else the node does not exist. To make an instance of a member node we write the name of the member then immediately after we must write what type of node it is. Since names for nodes are often the same word as the type of node this can be confusing for beginners. They see two words one following the other exactly the same except for one starting with a lower case and the other starting with an upper case letter.

We are going to make a 3 sided polygon or a tringle that is one meter high and 1 meter to the left and right. To make any shape in vrml we must have an instance of a Shape node.
To do this we add the code:


Notice in vrmlPad we see a syntax error until we have finished adding a closing curly bracket. This is because the code for the instance is not complete until we close it. Once we have the complete code needed for the instance of a Shape node our code is legal and can therefore be opened in our viewer blaxxun contact with out any errors. Before we open any file in our viewer we must first save our file in our editor vrmlPad by pressing CTRL+S. Leaving vrmlPad open now open our file in contact by going to the folder where we saved our file in windows explorer or internet explorer and double clicking or if in single click mode single click on our saved vrml file. What we should now see contact is a blank screen but if we have it right we should see no errors reported. We dont see any shape because we have not even got an instance of our Shape node's member geometry. Until we make an instance of a node it does not exist. We have a choice in what type of node we make geometry an instance of. Because we want to make a polygon we must make it an instance of an IndexedFaceSet type node.
We do this by inserting between the Shape node's curly brackets the code:

    geometry IndexedFaceSet {

After inserting our code vrmpPad should show no errors and our code should now looks like:

    Shape {
        geometry IndexedFaceSet {

Notice how I have the Shape's member geometery come out to the right a tab space. Having our code arranged like this helps us see what node and field belongs to what node. VrmlPad has a very helpfull feature that can make our lines come in the right number of tab spaces to the right for us
To use this feature:

    Press CTRL+A to highlight all of the text
    Then click CTRL+SHIFT+F or click on "Edit" select "Selection" and select "Format"

To make a polygon we need 2 lists of numbers, the coords and the index.

First we will deal with the coords (a coord can also be refered to as a coordinate, corner, point or vertex) they specify the locations of the polygon's corners.

We place our coords in an instance of the IndexedFaceSet's member named coord.
To make an instance of coord as type Coordinate we insert between the IndexedFaceSet's curly brackets the following code:

    coord Coordinate {

    so that our code free of any error now looks like:

    Shape {
        geometry IndexedFaceSet {
            coord Coordinate {

Data values in VRML always reside in fields never nodes alone. A Coordinate node contains a field called "point". "point" is a MFVec3f field type and it's here where we add our list of coords. In our code we must write in the field's name and it's square brackets before we write in the actual values
To do this insert between the Coordinate's curly brackets the following code:

    point [

Between the field's square brackets we insert our x, y and z values to specify the locations of the coordinates that make up the points in our polygon

    x defines in meters how much left or right the coordinate is
    y defines in meters how much up or down the coordinate is
    z defines in meters how much the coordinate goes into the screen

A list of points in an IndexedFaceSet ALWAYS starts with point 0 then point 1 and so on

this does not mean that point 0 will always be at this location in a polygon or that point 0 is always to the lower left in a polygon, it can be anywhere we like. It is refered to as "point 0" because it's the first point in the list of points.

here in this example we just so happen to have point 0 = the left base by having it's value set to:

    -1 0 0

point 0 specifies the location of the left base of our triangle because the first number which is the x component of our corner is -1 (1 meter to the left) and the y component is 0 in height (our base is 0 meters high). All of our z components (how far we go in and out of the screen) will be 0 so that our polygon will lie flat on a z plane

so that point 1 our second coord specifies the right base we want it to be the same as point 0 but with the x component positive as follows:

    1 0 0

so that point 2 our third coord is to the top middle of our triangle we have the x component set to zero so is neither left or right and the y set to a height of 1 meters as follows:

    0 1 0

After adding our coords our code for our Coordinate node should now look like:

    coord Coordinate {
        point [
            -1 0 0
             1 0 0
             0 1 0

With all the coords specified alone we still have nothing to be seen. We have to join the points in order to have a polygon. We specify how we join the points in the IndexedFaceSet's field coordIndex. The order of the points that make up the triangle must be in anti-clockwise order or the polygon will not be rendered by contact. By having our IndexedFaceSet's field coordIndex data as 0 1 2 we specify that the triangles points are joined in the order of 0, 1 and 2. Another way to put it is the coordIndex "indexes" or you can use the word "references" the points in the order 0 1 and 2

If we specified them in the order of 2 1 0 we would still have a legit triangle. To see the triangle like this though we would have to turn the triangle around. We can see the triangle like this because turning the triangle around makes the order of the points run in the opposite direction.

We will write in the field's name coordIndex and it's square brackets before we write in the actual values.
To do this insert between the IndexedFaceSet's curly brackets the following code:

    coordIndex [

At this point we should still see no errors reported in vrmlPad at the bottom of the window. It does not matter whether the IndexedFaceSet's member coordIndex is placed above or below its other member (coord), the syntax will still be correct. This does NOT mean though that it's ok to put it INSIDE any of the IndexedFaceSet's members as it's a member of IndexedFaceSet not it's members. Remember the brackets tells us which member belongs to which node.

After adding our coordIndex our IndexFaseSet should look like

    geometry IndexedFaceSet {
        coord Coordinate {
            point [
                -1 0 0
                 1 0 0
                 0 1 0
        coordIndex [

or like:

    geometry IndexedFaceSet {
        coordIndex [
        coord Coordinate {
            point [
                -1 0 0
                 1 0 0
                 0 1 0

The important thing to make sure in our code here, is that our coordIndex field comes one tab space to the right from where our IndexedFaceSet (its name being geometry) and its closing bracket is placed. Remember, to verify all nodes and fields are in their right place press CTRL+A then press CTRL+SHIFT+F in vrmlPad to align them all in accordance to how we have placed our brackets. If after doing this and some of our code is not aligned to the right, the right number of tab spaces, we know that we have something wrong in how we have our brackets placed.
Now we have our field name added and its brackets lets add the data so that our coordIndex now looks like

    coordIndex [
        0 1 2

press CTRL+S in vrmlPad to save then refresh in contact and we should see our triangle :)

We can verify the code from our file to my working example by first downloading my example to our folder by right clicking on the link crudeTriangle.wrl and selecting "Save Target As".

Viewing Our Triangle From Both Sides

To examine our triangle from a different angle put contact into examine mode.
To do this:

    Click on the 3D scene and press CTRL+SHIFT+E
    Or right click on the 3d scene and select movement and then select examine

Drag the mouse over the 3d scene to rotate the triangle. Notice how you can't ever see the other side of the triangle like I have explained above. Why we can only see the triangle on one side is done on purpose. The computer's rendering process is speeded up if only triangles in anti-clockwise order are attempted because most of the time shapes are made so that we never want to see triangles in any other order. However we can make our triangle so that it can be seen from both sides if we set the IndexedFaceSet's field solid to FALSE. Again it does not matter whether the member solid is placed in a line above or below the other members (coord and coordIndex), the syntax will still be correct. Just make sure its placed with in the right brackets so that its a member of the IndexedFaceSet. Remember the brackets tells us which member belongs to which node.
Insert the line of code:

    solid FALSE

Save and refresh and see how we can now see our triangle from both sides.

Adding Shade to Our Triangle

Our triangle has no shading. Notice when we view our triangle it's always the same bright white in appearance no matter how sharp the angle we view it.

If we were to make a shape out of triangles like this it would be rather flat looking. To give our triangle shade from different angles we must make an instance of the Shape's member appearance as an Appearance type node. Then we want to make an instance of the Appearance nodes member material as a Material node type.
In short the following lines of code:

    appearance Appearance {
        material Material {

inserted into the shape node gives our triangle shade.

Now the sharper we rotate our triangle the darker it is.

We can now easily make our triangle any color we want by setting the Material node's diffuseColor field to any color we desire. The diffuseColor field is a SFColor type. A SFColor field is made up of r, g, b components.

    r for red
    g for green
    b for blue

The components can be of any floating point value from 0 to 1

0 is completely off and 1 is full on

so if we set our diffuseColor field to 1 0 0 we will have a red triangle

To do this insert betwen the Material's curly brackets

    diffuseColor 1 0 0

save and refresh to view our red triangle :)

Our file should now resemble my working example triangle.wrl

We can make the red darker by decreasing the value of the red component for example

    .7 0 0 will be a darker red and
    .4 0 0 will be a darker red again

If we keep decreasing the red component we will arrive at 0 0 0 which is black because all values are completely off.

If we want to make our red lighter in color we increase the other 2 components g and b values equally,

for example

    1 .4 .4 will give us a pink color and
    1 .8 .8 will give us a much lighter pink

If we keep increasing the other 2 components g and b we will arrive at 1 1 1 which is white because all values are full on

Here are a few other color combinations you may want to try but please feel free to try any of your own combinations

    0 1 0 #green
    0 0 1 #blue
    1 1 0 #yellow
    1 1 .7 #light yellow
    .3 .3 0 #dark yellow
    .5 0 .5 #purple
    0 1 1 #cyan
    .5 .5 .5 #grey

We can now feel happy about our abilities, this triangle is now sophisticated enough to be worn proudly as an avatar in it's own right

Adding Another Triangle to our IndexedFaceSet to Make a Diamond

To add another triangle we do not have to add another 3 coord points only one is required. This is because we use the index to specify how the points are joined together. The index can use a point from any where in the coord list we like when joining points together to make a polygon. This is one of the great advantages in having both coords and an index as opposed to only having coords to specify polygons because we can have polygons share the same coord points within an IndexedFaceSet. Typically one point in an IndexedFaceSet is used by 2 triangles. This results in our computers only having half the amount of vertices (coords) to process.

To make our next point in the opposite location of our top middle corner we want a coord the same but with the y component as -1

so our coords should now look like:

    -1  0  0  #point 0 = left
     1  0 
0  #point 1 = right
     0  1  0  #point 2 = top
     0 -1  0  #point 3 = bottom

We can now join 3 points in the index to make a whole new triangle. So that contact can know where the last polygon finishes and where the next one begins we MUST add a -1 after our last index of the last polygon before we can add another set of indices to make our next polygon. Our indexCoord field should now look like:

    coordIndex [
        0 1 2 -1
        1 0 3

In stead of having 2 triangles to make a diamond polygon we can instead make just one 4 sided polygon be simply changing the index values to 0 3 1 2 so that our coordIndex would look like:

    coordIndex [
        0 3 1 2

if we did that. But I want to continue keeping our coordIndex arranged as triangles otherwise we will come unstuck later on when we want the z components not to be all the same. There is no render speed gained using one 4 sided polygon instead of 2 triangles because the computer will break any polygon that has more than 3 sides down to triangles internally in order to render them anyway.

To make a square instead of a diamond

we could do this keeping our coordIndex unmodified if we make our coords:

    -1 -1  0  #lower left corner
     1  1  0  #upper right corner
    -1  1  0  #upper left corner
     1 -1  0  #lower right corner

Try varying the x y and z components to make different shapes. Playing with triangles is the most important thing to do if you want to feel at home with them.

If we have our coords set to

    -1  0  0  #point 0 = left
     1  0  0  #point 1 = right
     0  1 -1  #point 2 = top
     0 -1 -1  #point 3 = bottom

we will have the top and bottom points in our diamond with different z values to the mid left and mid right points. Notice how you can see a sharp crease between the 2 triangles if we move the triangle about in examine mode.

Often when making shapes we want them to look smooth. This can be achieved by having lots of very tiny triangles, however this is very inefficient for real-time 3D graphics. A much more efficient way is to use far less triangles and make them appear smooth by shading the triangles so as to give the elusion that the triangles are curved rather than them looking flat like they really are. In VRML this is easily done by setting IndexedFaceSets field creaseAngle to a value greater than what ever angle we want seen smooth.
So by inserting the line of code between the IndexedFaceSet's curly brackets:

    creaseAngle 3.14

our crease angle is now set to 1 PI radians (180 degrees)

Color Per Vertex
So far we have been able to make any shape any color we like but what if we want to make our shape different colors in different places? This is easily achieved if we utilize the VRML color per vertex coloring scheme. If using contact 4.4 or below with Direct X you may not be able to see any shading sadly when using color per vertex. Please see my page to see 2 screen shots I took to show the difference. So that we can have all vertices colored we must first make the IndexedFaceSet's member color an instance of the Color node type so that it exists.
To do this insert between the brackets of the IndexedFaceSet the following line of code:

   color Color {

We insert the data values inside the Colors member color (remember in VRML all data values must be inserted into fields never directly into nodes) The color's member color is a MFColor type field. MFColor fields are used for containing a list of SFColor values ( r g b values). Because we have 4 points in our shape we will add 4 r g b values. Although you can have the list of colors indexed by a separate index to the coords using the colorIndex instead of the coordIndex we will here share the same index the coords use. Using the coordIndex for both the list of coords and list of colors simply means that point 0 will be colored by color 0, point 1 will be colored by color 1 and so on.
To get our shape so that our left corner is red, our right yellow, our top blue and our bottom green insert between the color node brackets the following code for the color field remembering not to confuse our color node of Color type with its field member also called color:

    color [
        1 0 0 #color 0 r g b values = red
        1 1 0 #color 1 r g b values = yellow
        0 0 1 #color 2 r g b values = blue
        0 1 0 #color 3 r g b values = green

Save and refresh so that we can view our lovely colored triangles :)

We no longer need our diffuseColor set to any value as its been overridden by our Color node so we can remove this line now. Our file should now be similar to my working example colorPerVertex.wrl

We could keep making our shape more complex by adding more triangles but unless write code that generates our triangles, editing one vertex at a time and editing the indexCoords would most likly prove to be a tedious chore using only a text editor. This is better done using a graphical user interface. Seamless3D is such a program that can edit color per vertex triangles with ease by hand.

Translating our Triangle 2 meters to the right
if we want to move our object to a location or rotate it we must put our object with in a Transform node
translate simply means placing our object where we want it to be by specifying coordinates
because we are in 3d we need 3 numbers to represent our coordinates: x, y, and z
x = how much left and right
y = how much up or down
z = how  far into the screen

so if we want to place our triangle to be 2 meters to the right we want our  x y and z coordinates to be  2 0 0
the code we need for such a translation is simply

translation 2 0 0

because a translation belongs to a Transform node we have to first add the line Transform {
and because our shape (triangle) is a child of our transform node we must insert the word "children"  in front of the word Shape
and because we have now added a curly bracket we must close our bracket in the appropriate place.

Rotate On the Y Axis One 8th a Revolution
now lets experiment with rotations with in our transform node

in VRML a rotation must have 4 numbers for it's value
x, y, z and angle

the x y and z specify the axis of the rotation and the angle specifies how much we are going to rotate it on that axis

although the values in the x y and z can be a wide range of values specifying any axis there is possible, most of the time we only need 3 different axis for rotating objects
1 0 0 = a rotation on the x axis
0 1 0 = a rotation on the y axis
0 0 1 = a rotation on the z axis

the angle can be any value we like but keep in mind 6.283185307 (two pi radians) rotates one full revolution
so if we divided this number by 8 we will have 0.785398163 or one 8th of a revolution

rotateOnTheYAxisOne8th.wrl is identical to translate2MetersToTheRight.wrl except we have the line
rotation 0 0 1   0.785398163
in our transform node instead of the translation one

For more reading on vrml rotations please go to my tutorial Understanding Vrml Rotations

Translation Animation
in order to do animation's we have to introduce 4 new things at once
DEF statments, TimeSensors, Interpolators and ROUTEs
DEF statements short for define allow us to name a node so that we can diferentiate one node from another
ROUTEs are used for specifying where the data is coming from and where it is going
TimeSensors are timers something essential for aniamations. The data they transmit detirmine what point in time we are at in in our animation
Interpolators are used for specifying how values evolve  to another at a given point in time. Rotation Animation
rotAnimation.wrl There is always a rigid order in how objects are transformed.
It can make a big difference knowing the fact that with in a transform node a rotation is always performed before a translation to have a translation and then a rotation so as to make a object orbit we must add another transform node
orbit.wrl It is essential you play arround with the last 3 examples to get a good understanding of what DEF statments, TimeSensors, Interpolators and ROUTEs are.

this completes our beeline path to being able to animate a triangle

The Triangle
There is so much more you can do with triangles than you can with shapes like spheres, cylinders, cones and boxes. With the triangle you can make any shape you like if you use enough of them. In fact generally speaking all real time computer generated 3d shapes are broken down into triangles because computers work with them efficiently, so there is nothing inefficient starting off with them in the first place from a computers point of view at least. You may find that it is worth understanding a few things about triangles.

What is unique about the triangle?
It is the polygon that has the smallest number of corners possible. Some may of claimed to have seen a 2 cornered polygon but I have yet to see one:)
One of the important things about having only 3 points is that unlike any other polygon it does not matter where you position any of the corners in 3d space, so long as none of the corners share the same point you have a legal polygon. That is, the surface of the triangle is all ways flat. If you want to construct any other polygon to lie flat on a plane of any angle imaginable you may run into problems if you don't use a bit of complicated mathematics to make sure each corner is in the right place, other wise your polygon may not be flat which means it is not really a polygon. Such real problems are bypassed with triangles. You can make a rectangle or a hexagon using triangles but you can't do the reverse. Triangles don't just look great their real virtue lies in the fact that they are both extremely versatile and simple. Master the triangle and and you know a lot about computer 3d graphics and with just a little bit of imagination you can do all sorts of great things with them!

Keeping every thing simple
If you think that a lot of my examples look boring I make no apology for this, as one of the things that often frustrates me when I am trying to learn from an example is not being able to find the stuff that I really want to know in a sea of fancy code. I have learnt that many concepts commonly reserved for advanced users need not be if everything is kept simple. So keeping my examples simple has been one of my main priorities even if at the expense of dullness. I leave the fancy code for you to write :)

Two Triangles
Notice by adding only one more point (vertex) to the triangle.wrl example we can have a another whole triangle. (as opposed to adding another three) This is one of the reasons for having both Coordinate points and indices (coordIndex) more than one triangle can share the same point making less resources and often faster programming techniques.

Two Smooth Triangles using creaseAngle
The triangles are exactly the same as those in the twoTriangles.wrl example except a creaseAngle of 3.14 radians (1 PI or 180 degrees) has been added.
Often when making shapes we want them to look smooth. this can be achieved by having lots of very tiny triangles, however this is very inefficient for real-time 3D graphics. A much more efficient way is to use far less triangles and make them appear smooth by shading the triangles so as to give the elusion that the triangles are curved rather than them looking flat like they really are. In vrml this is easily done by adding a creaseAngle that is more than any angle you want to look smooth. When the computer generates the shade it generates normals internally by calculating what each triangle's angle is and averaging out the angles. creaseAngle is simple to use and usually does an adequate job but there are times when you may want to have more control over how the triangles are shaded. Instead of letting the computer generating the normals you can directly specify your own normals in the vrml code.

Two Triangles with Normals
The triangles are exactly the same as those in the twoTriangles.wrl example in that they are in the same location and are at exactly the same angle. they are just as flat too though they look curved. The curve is an optical illusion thanks to the normals that have been added. Normals allow us to manipulate shade in a triangle enabling shapes to look smooth with a lot less triangles. To get an interactive visual clue of what normals do click on my Normals Demo.
The 4 red sticks that poke out of the triangles corners represent the normals. I have added the smaller green and blue cross sticks to add a better visual clue to the normals orientation. The 3 sliders only control the normal at point0 (the left point). Click on the red parts of the sliders and drag them to control the direction the normal points to. When you first drag a slider the plugin should pop up it's console with a readout of the normals. If you see the console display any strange looking numbers with an e in them you can consider them as zeros. The numbers are in fact very small numbers close to zero. The computer is programmed to display such small numbers in scientific format. The normals 3 numbers x, y and z are the location of the normals end relative to it's base. Notice that none of the numbers ever get any greater than 1 or less than -1 this is because a normal is 1 unit in length (a normal is a 1 unit vector). The first slider rotates the normal on it's x axis the second slider rotates it on it's y axis and the third on it's z axis. To reset a slider to 0 position click on it's cyan bar. See how when the normal points towards the screen the area around the normals base is at it's brightest. This is because the direction of the light has been set in the wrl file to point into the screen. That is the direction of the light points in parallel along the z axis. If you point the normal so that it points away from the direction of the light it gets darker. Instead of correcting the normal using the sliders try dragging on the 3dscene so as to get the normal to point back into the screen. The area around Point0 should go just as bright as it was when it faced us face to face. This is because the orientation of the normal not the orientation of the triangles dictates how the area around the normals base will be shaded when rendered by the computer. Note Normals do not control the light they control how the area at their base is affected by the light. The smooth transition in shade from one normal to another is known as Normal interpolation. This is what is responsible for getting our smooth curved look in our triangles when they are really flat.

Two Textured Triangles with Normals
texture.png (needed if you want to save it to your local drive)
In this example we have added a texture. All that is added is the texture coordinates and a url that references a .png file (a common .jpg file could have been used instead)
If you can figure out what all the numbers mean in this example you will know a lot about triangles and there will be a lot you will be able to do with them.

Generating Two Triangles using vrmlscript
This example is exactly the same as the two twoTexturedTrianglesWithNormals.wrl
except that it that all the triangle coordinates, normals, texture coordinates and indices are generated in vrmlscript.

Generating Land using vrmlscript
This example demonstrates the power of generating triangles in vrmlscript as opposed to hand coding the data. The equivalent file written in only VRML code is about 55k in size when gzipped, the vrmlscript version is only just over 2k when gzipped, a significant size reduction. The fact is a few vrmlscript functions can generate as many triangles as you like.
To use this example you don't need to understand how all the functions work. All you need to understand is what the 4 parameters you pass are when using one of my tug functions.
Look at my function GenLand(). This is the function that contains all my tug function calls.
To experiment first remove all except one of the tug functions and try passing different numbers.
The 4 tug parameters are as follows:
    // the first  parameter specifies the x location of the centre of the tug
    // the second parameter specifies how high to tug the hill
    // the third parameter specifies the z location of the centre of the tug
    // the last parameter specifies the radius (how wide the hill is)

when you have tried a few examples try adding more tug functions one at a time so you don't loose your barings for where the one you are working on is. If you can't easily figure out where one of the tug functions is tugging the land try temporally tugging it up a very high amount so you can visually locate where it is. Then put the height parameter back to a value you want.

Animating Triangles
This example is the same as twoTexturedTrianglesWithNormals.wrl except animation code has been added to make the triangles rotate on the x axis.

Morphing 2 Triangles

Download | Forum | Tutorials | Worlds | Avatars | Links | Thyme | Email


Copyright © 2000-2005 Graham Perrett