Please send suggestions or corrections for this page to the author at sbuss@ucsd.edu.

This page documents how to use the GlGeomShapes functions. These are a collection of classes that render geometric shapes, including spheres, cylinders, cones, tori, and Bezier patches, within the framework of Modern OpenGL. They were developed to support programming examples for Math 155AB at UCSD, and more generally to use with software for a possible second edition of the book 3D Computer Graphics: A mathematical approach with OpenGL, Cambridge University Press, 2003.

Currently supported geometric shapes, and the required source files, are:

Spheres
With a specified number of "slices" and "stacks", GlGeomSphere.[cpp,h] and GlGeomBase.[cpp,h].
Cylinders
With a specified number of "slices", "stacks" and "rings", GlGeomCylinder.[cpp,h]. and GlGeomBase.[cpp,h]
Cones
With a specified number of "slices", "stacks" and "rings", GlGeomCone.[cpp,h]. and GlGeomBase.[cpp,h]
Tori
With a specified number of "slices" and "rings", GlGeomTorus.[cpp,h] and GlGeomBase.[cpp,h].
Cubes
With a specified mesh resolution, GlGeomCube.[cpp,h] and GlGeomBase.[cpp,h].
Bezier patches
With a specified resolution and ordinary or homogeneous coordinates, GlGeomBezier.[cpp,h] and GlGeomBase.[cpp,h].
Utah teapot
Made from 32 Bezier patches, mesh resolution can by varied. GlGeomTeapot.[cpp,h], GlGeomBezier.[cpp,h] and GlGeomBase.[cpp,h].
More to come?

The shapes all share a similar interface. The routines take care of:

  1. Creating an array suitable for loading into VBO and EBO for geometric shapes such as spheres, cylinders, etc., in a standard size and shape centered at the origin.
  2. Optionally generating normals and texture coordinates.
  3. Loading the geometric data, including vertex positions, normals and texture coordinates into a VBO and EBO.

All of these things are taken care of by the GlGeom routions. Your calling code is still responsible for setting a modelview matrix so that the object is rendered at the desired position, size and orientation (instead of being rendered in a standard position at the origin).

The basic usage for all the geometric shapes is as follows:

For examples, see SolarModern for how to render and animate spheres, or see GlGeomShapesTester for examples of how to use all the supported shapes.

I. InitializeAttribLocations

All GlGeom shapes have a member function InitializeAttribLocations which will create the needed VAO, VBO and EBO needed for OpenGL to render the shape. To build the VBO, the program needs to know how to layout the vertex data in an array; the parameters to InitializeAttribLocations are:

    InitializeAttribLocations(unsigned int pos_loc, unsigned int normal_loc, unsigned int texcoords_loc);

The three arguments specify the layout for the (a) the vertex position, (b) the vertex normal, and (c) the vertex texture coordinates. These layout values can be omitted, or set to UINT_MAX, to indicate that the shader program does not need that value. In this case, the values are not stored in the VBO.

For example, the GLSL shader code may contain lines such as the following:

    layout (location = 0) in vec3 aPos;	       // Position in attribute location 0
    layout (location = 2) in vec3 aNormal;     // Normal Vector in attribute location 2
    layout (location = 3) in vec2 vTexCoords;  // Color in attribute location 3    

The corresponding needed call to InitializeAttribLocations is:

    InitializeAttribLocations(0,2,3);

If only some of these vertex attributes are needed, you can use calls such as

    InitializeAttribLocations(0);            // Only position is generated.
    InitializeAttribLocations(0,2);          // Position and normal values are generated.
    InitializeAttribLocations(0,UINT_MAX,3); // Only position and texture coordinates are generated.

InitializeAttribLocations regenerates all the VAO, VBO and EBO data each time it is invoked. Therefore, it is recommended that it be called only once during initialization. If you have multiple shaders that use different layout values, then you should create different GlGeom objects for each shader.

II. Spheres

A sphere is rendered as a mesh of triangles; the mesh resolution is controlled by setting the numbers of "slices" and "stacks". The slices and stacks split the sphere similarly to lines of latitude and longitude on a globe. The "slices" are vertical cuts (similar to the slices of an orange). The "stacks" are horizontal cuts. In practice, we usually use equal numbers of slices and stacks.

To create a sphere, your program must use #include "GlGeomSphere.h", and then use a constructor, such as:

    GlGeomSphere mySphere(8,8);   // Create a sphere with 8 slices and 8 stacks. 

An equivalent way to do the same thing is:

    GlGeomSphere mySphere;   // Create a sphere with default number of slices and stacks.
    mySphere.Remesh(8,8);    // Remesh to have eight slices and eight stacks.    

The first parameter is the number of slices; the second is the number of stacks. Remesh() can called repeatedly, and can be called either before or after calling InitializeAttribLocations(). After creating and meshing the sphere, and calling InitializeAttribLocations(), your program calls Render() as desired to render the object.

    mySphere.Render();

The Render function issues a glDrawElements() command to render the sphere as triangles. The sphere is rendered as a unit sphere, centered at the origin, with one pole at <0,1,0> and the other at <0,-1,0>. It can be repositioned, resized, etc. by the shader program; usually this is done by using a modelview matrix.

Texture coordinates for a sphere use a spherical projection. Texture coordinates all lie in [0,1]×[0,1]. The front, center point has s, t texture coordinates equal to (0.5,0.5). Points near the north pole have t texture coordinate close to 1; points near the sorth pole have t texture coordinate close to 0;

There are a couple specialized methods for rendering a single slice or a single stack. mySphere.RenderSlice(i) renders the i-th slice, and RenderStack(j) renders the j-th stack. In 155A, students have used RenderSlice to render petals of a flower.

III. Cylinders

A cylinder is rendered as a mesh of triangles; the mesh resolution is controlled by setting the numbers of "slices" and "stacks" and "rings". The "slices" are vertical cuts (similar to slices of cake). The "stacks" are horizontal cuts (similar to cake layers). The "rings" are the number of concentric bands on the two end faces of the cylinder (this has no analogy with normal cake cutting!).

To create a cylinder, your program must use #include "GlGeomCylinder.h", and then use a constructor, such as:

    GlGeomCylinder myCylinder(8,2,3);   // Create a cylinder with 8 slices, 2 stacks and 3 rings. 

An equivalent way to do the same thing is:

    GlGeomCylinder myCylinder;   // Create a cylinder with default number of slices, stacks, and rings.
    myCylinder.Remesh(8,2,3);    // Remesh to have eight slices, two stacks and three rings.    

Remesh() can called repeatedly, and can be called either before or after calling InitializeAttribLocations().

A low resolution GlGeomCylinder can be used to create a rectangular prism (i.e., a box) by using four (4) slices, one (1) stack and one (1) ring.

The entire cylinder (top, bottom and side) can be rendered with the Render member function:

    myCylinder.Render();          // Render: renders entire cylinder

This draws the cylinder using the OpenGL glDrawElements() command, rendering the cylinder as triangles. The cylinder is rendered as having height 2 and radius 1, positioned with its center at the origin and with y-axis as its central axis. This can be changed by using a modelview matrix (for instance) in the shader program.

There are also special render functions for rendering the top face, the bottom face, and the side of the cylinder.

    myCylinder.void RenderTop();
    myCylinder.void RenderBase();
    myCylinder.void RenderSide();

It is convenient to use these last three methods, instead of Render(), when putting a texture on the cylinder. This is because rendering different texture maps on different faces requires rendering the faces separately. Each of the three faces uses texture coordinates in the domain [0,1]×[0,1]: in other words, they share texture coordinates. The top and bottom faces rectangularly map the texture coordinates onto the circular region inscribed in [0,1]×[0,1].

The side uses texture coordinates that range over [0,1]×[0,1]. The side uses s texture coordinates are equal to 0.5 for the center front of the cylinder; and equal to 0 and 1 on the back of the cylinder (where the s coordinate wraps). The side uses texture coordinate t equal to 1 where it touches the top face, and equal to 0 where it touches the bottom face.

IV. Cones

A cone is rendered as a mesh of triangles; the mesh resolution is controlled by setting the numbers of "slices" and "stacks" and "rings". The "slices" are vertical cuts (similar to slices of cake). The "stacks" are horizontal cuts (similar to cake layers). The "rings" are the number of concentric bands on the circular base.

To create a cone, your program must use #include "GlGeomCone.h", and then use a constructor, such as:

    GlGeomCone myCone(8,2,3);   // Create a cone with 8 slices, 2 stacks and 3 rings. 

An equivalent way to do the same thing is:

    GlGeomCone myCone;       // Create a cone with default number of slices, stacks, and rings.
    myCone.Remesh(8,2,3);    // Remesh to have eight slices, two stacks and three rings.    

Remesh() can called repeatedly, and can be called either before or after calling InitializeAttribLocations().

The entire cone (side and base) can be rendered with the Render member function:

    myCone.Render();          // Render: renders entire cone

This draws the cone using the OpenGL glDrawElements() command, rendering the cone as triangles. The cone is rendered as having height 1 and base radius 1. The central axis of the cone is on the y-axis. The apex of cone is at the point (0,1,0); the base is a unit circle lying in the x-z plane centered at the origin (0,0,0). As usual, the position and scaling of the cone can be changed by using a modelview matrix (for instance) in the shader program.

There are also special render functions for rendering the bottom face and the side of the cone.

    myCone.void RenderBase();
    myCone.void RenderSide();

It is convenient to use these last two methods, instead of Render(), when putting a texture on the cone. This is because rendering different texture maps on different faces requires rendering the faces separately. The side and the base both use texture coordinates in the domain [0,1]×[0,1]: in other words, they share texture coordinates. The base face rectangularly maps the texture coordinates onto the circular region inscribed in [0,1]×[0,1].

The side uses texture coordinates that range over [0,1]×[0,1]. The side uses s texture coordinates are equal to 0.5 for the center front of the cylinder; and equal to 0 and 1 on the back of the cylinder (where the s coordinate wraps). The side uses texture coordinate t equal to 0 where it touches the base, and equal to 1 at the apex. The apex vertex has texture coordinates s=0.5 and t=1.

V. Tori

A torus is rendered as a mesh of triangles; the mesh resolution is controlled by setting the numbers of "rings" and "sides". The torus is always created as having major radius equal to 1, but the minor radius can be set by your program. The torus is oriented horizonally: the center of the torus is at the origin, it goes around the y-axis, so the inner circular path of the torus lies in the x-z plane. (As usual, the default position and size, etc., can be changed, say by using a modelview matrix in the shader program.)

The "rings" are vertical cuts (similar to way a doughnut might be sliced into small bites). The "sides" are wedges running around the torus at right angles to the rings (similar to the way a single ring might be cut into yet smaller pieces).

To create a torus, your program must use #include "GlGeomTorus.h", and then use a constructor, such as:

    GlGeomTorus myTorus(10,6,0.4);   // Create a torus with 10 rings, 6 sides and minor radius 0.4 

The minor radius defaults to 0.5 if no value is given. An equivalent way to do the same thing is:

    GlGeomTorus myTorus;         // Create a torus with default values.
    myTorus.Remesh(10,6,0.4);    // Remesh to have 10 rings, 6 sides and minor radius 0.4    

If the minor radius is not specified, Remesh() will leave it unchanged. Remesh() can called repeatedly, and can be called either before or after calling InitializeAttribLocations().

The member function Render() renders the entire torus. Texture coordinates are set so that t coordinates wrap from 1 to 0 at the inner circular seam of the torus. The s coordinates are equal to 0.5 at the front of the torus (where x equals 0 and z is positive), and they wrap from 1 to 0 at the back of the torus (where x equals 0 and z is negative).

    myTorus.Render();

There are a couple specialized methods for rendering a single slice or a single stack; these are myTorus.RenderRing(i) to render the i-th ring, and myTorus.RenderSideStrip(j) to render the j-th strip.

VI. Cubes

A cube is rendered as a mesh of triangles. By default each face of the cube is a pair of triangles (so the mesh resolution is equal 1), but this can be changed to havea higher mesh resolution. The cube is axis-aligned, centered at the origin, and has side length equal to one. Thus, the six corner vertices of the cube are at (± 1/2,±1/2, ±1/2) (As usual, the default position and size, etc., can be changed, say by using a modelview matrix in the shader program.)

The mesh resolution is equal to the number of segments each edge of the cube is split into.

To create a cube, your program must use #include "GlGeomCube.h", and then use a constructor, such as:

    GlGeomCube myCube();   // Create a cube with mesh resolution equal to 1. 

The mesh resolution can be set with the constructor by calling GlGeomCube myCube(meshRes); or it can be changed by calling

    myCube.Remesh(8);    // Set each face to be a 8x8 mesh of (triangulate) squares.    

The member function Render() renders the entire cube. Texture coordinates are set separately for each face. When viewing any side face the texture coordinates range from 0 to 1 from the bottom left viewed corner to the upper right viewed corner. The texture coordinates for the top face and bottom face are set to the the second (t value) increases in the positive z-axis direction.

    myCube.Render();

There a methods for rendering a single face of the cube. Call myCube.RenderFace(i) to render just the i-th face. The face numbers range from 0 to 5, and specify (respectively) the front face, the right face, the back face, the left face, the top face and the bottom face.

(Thanks are due to Derrick Yao for creating GlGeomCube.)

VII. Bezier Patches

The GlGeomBezier class supports rendering a set of Bezier patches. The patches may be rational Bezier patches. The patches can be of any order (any degree), and meshed differently in the u and v directions.

To create a collection of Bezier patches in a GlGeomBezier object, your program must use #include "GlGeomBezier.h", and then use a constructor, such as:

    GlGeomBezier myPatches();   // Create a object that holds Bezier patches 

It is also possible to use GlGeomBezier myPatches(uRes,vRes); to create a collection of Bezier patches that will be rendered with mesh resolution uRes×vRes. The default mesh resolution is 8× 8. Alternately, a call to myPatches.Remesh(uRes,vRes) can be used to change the mesh resolution, and this can be done either before or after the call to InitializeAttribLocations().

The constructor does not actually create any patches. To finish forming the patches, you load the control points for a complete set of Bezier patchs into the GlGeomBezier object by calling

    myBezier.LoadControlPts(uOrder, vOrder, numCoordinates, numPatches, controlPointsArray); 

The first two parameters uOrder and vOrder give the order of the control patches; recall that the order of a Bezier curve or patch is equal to the degree of the curve or patch plus one. For instance, patches of order 4×4, and thus of degree 3 ×3 are a popular choice. The value numCoordinates must equal either 3 or 4, and this is the number of coordinates in each control point. If numCoordinates is equal to 3, then each control point consists of x, y, z values specifying a point in 3-space. If numCoordinates is equal to 4, then each control point consists of x, y, z, w values that are the homogeneous coordinates for a point in 3-space. In the latter case, the Bezier patches are rational Bezier patches.

The parameter numPatches is the total number of Bezier patches to be uploaded to the GlGeomBezier object. Each patch has uOrder*vOrder many control points; each control point consists of numCoordinates floating point numbers (of type double). herefore, the total of double's in the array of control points is equal the product uOrder*vOrder*numCoordinates*numPatches. The parameter controlPointsArray is of type double* it is a pointer to the array of double precision floating point numbers defining all the constrol points of the Bezier patches. The GlGoemBezier object creates a copy of the array of control points, thus it is not necessary for the C++ code to maintain the array of control points after calling LoadControlPts.

The member function Render() renders the entire set of Bezier patches:

    myBezier.Render();

Texture coordinates are set so that each patch has texture coordinates ranging over [0,1]×[0,1]. (There is currently no provision for the shader program to know which patch is being rendered.)

There are a couple specialized methods for rendering individual patches from the GlGeomBezier oject. These are myBezier.RenderPatch(i) to render the i-th ring where 0 ≤ i < numPatches, and myBezier.RenderPatch(i) myBezier.RenderPatches(i,n) to render n consecutive patches beginning with patch i.

For examples on using GlGeomBezier objects, see the source code in GlGeomShapesTester and in GlGeomTeapot.[c,h] for the Utah teapot, which is discussed next.

VIII. Utah Teapot

The Utah teapot is a commonly used geometric shape in computer graphics, constructed from Bezier patches. It consists of a body, a top (or lid), a handle and a spout. The teapot is rendered upright with the body of the teapot centered on the y-axis; the center of the base is placed at the origin. The spout is facing in the direction of the positive z-axis. The Bezier patches have degree 3×3; that is, they have order 4×4.

To create a teapot, your program uses #include "GlGeomTeapot.h", and then use a constructor, such as:

    GlGeomTeapot myTeapot();   // Create teapot

By default the Bezier patches making up the teapot have mesh resolution of 8 by 8, so each patch is rendered into 8 by 8 subpatches. The default can be changed with the construction, for instance,

    GlGeomTeapot myTeapot(10,10);   // Create teapot with patches of mesh resolution 10 by 10.

The Remesh() method can also be used to change the mesh resolution of the patches, namely, calling myTeapot.Remesh(10,10). The Remesh() method can be called either before or after calling InitializeAttribLocations.

After InitializeAttribLocations has been called, the teapot can be rendered by calling Render():

    myTeapot.Render();   // Render the teapot

There are also methods for rendering and of the four parts of the teapot, these are RenderBody(), RenderLid(), RenderHandle(), and RenderSpout().