OpenGL Programming/Modern OpenGL Tutorial 07

The teapot is a famous model among 3D developers.

You can find various versions of the model as vertices, but did you know that the original version was actually made of (3,3) Bézier surfaces? Bézier surface are described by control points, and we can evaluate the surface with any level of precision to produce a set of vertices.

See:
 * The Origins of the Teapot (PDF) - Computer Graphics & Applications - January 1987 (vol. 7 no. 1) - a venerable article, with Bézier data ("patches")!
 * The History of The Teapot - more historical details

So how about we create a high-definition version of the teapot? :)

= Math to code =

From the Wikipedia article (see below for explanations):


 * A given Bézier surface of order (n, m) is defined by a set of (n + 1)(m + 1) control points ki,j. [...]


 * A two-dimensional Bézier surface can be defined as a parametric surface where the position of a point p as a function of the parametric coordinates u, v is given by:


 * $$\mathbf{p}(u, v) =

\sum_{i=0}^n \sum_{j=0}^m B_i^n(u) \; B_j^m(v) \; \mathbf{k}_{i,j} $$


 * evaluated over the unit square, where



B_i^n(u) = {n \choose i} \; u^i (1-u)^{n-i} $$


 * is a Bernstein polynomial, and


 * $$ {n \choose i} = \frac{n!}{i! (n-i)!} $$


 * is the binomial coefficient.

OK, well actually this is pretty simple.
 * the big "E" means "sum" from value a to value b, step 1 (i.e.: it's a for loop that does additions)
 * our data is formed of 4x4 points, so our Bézier surface order is (3,3) - that's a 3D surface
 * we create a grid of vertices, and we index them by (u,v); u and v are between 0 and 1 (equivalent to t in Bézier curves, we can say it's a percentage of completion along an axis)

What's complicated is all the code we'll need around the maths ;)

Getting the control points
The PDF article presents the data as a big set of control point vertices, and then several patches of 4x4 vertex indices.

We want our control points somehow like this (we'll use a vertex structure for clarity):

Moreover, we want this array for each of the 28 patches in the teapot.

We note that the data provided in the article is not directly usable:
 * we don't have directly the vertices, but instead we have indices to the vertices
 * the indices start at 1, instead of 0 as in C/C++

We'll store the data as a C array, then we'll write a function to convert it to the format we want. We do it in a first, separate step, because doing everything as once would make our code look complicated.

The vertices:

The indices:

Now our function to get the set of control points:

Computing the vertices
Now we'll evaluate the Bézier surface, with a resolution of 10x10 (or any other precision you want).

For each 4x4 patch, we compute each point in our 10x10 grid (so with u and v progressing with 1/10 steps):

For a vertex at (u,v) we compute the "EE" sum from the equation ("formula") above: Note: we can optimize the code by computing  only once per   loop: move it between the two   lines at the beginning.

The bernstein_polynomial and binomial_coefficient functions are tedious, but straighforward:

Note : the article presents Pascal code to do the job. You may have noticed that the authors hard-coded the equation for n=m=3. We didn't use that method, because it makes the code actually less easy to compare with the equation, and does not really make the code clearer nor shorter.

To be able to declare our functions in any order, we need to pre-declare them at the top of our file:

Drawing the vertices
Now that we have our grid of vertices, we can draw each of its squares, using the elements technique :

We can draw it as usual:

We've got our flying teapot!

= Making precision arbitrary =

Currently, we can modify the resolution and the Bernstein degree by modifying the s.

If we want to change these values dynamically (for instance, change the resolution when the user click a +/- button), we won't be able to use static arrays anymore, due to a limitation in C/C++. We chose to use static arrays, because it makes the code easier to understand. To make the change, you'll need to either: This is left as an exercise to the reader ;)
 * Create an array of pointers to arrays of floats (multi-dimensional)
 * Use a single-dimension array and use maths to compute the right index. For instance, in a 4x4 array, array[2][3] is equivalent to array[2*4+3] - that's just what we did for the  array.

Limit
When we make the precision very high, for instance 49x49 (67228 vertices and 774144 elements), some vertices seem to merge. Remember that we use GL_UNSIGNED_SHORT to index vertices? This means we can only address up to 65536 vertices. If we want to draw more, we need to split the teapot into several arrays of vertices.

Going further
In The History of The Teapot page, you'll find a link to an archive with Bézier patches for other teaset elements, notably a spoon and a cup. Display them around the teapot!

Note: there is a built-in teapot shipped in GLUT, with a static resolution ( - made of vertices instead of Bézier surfaces). We didn't use it in this tutorial, because it's not fun, because it's old-style/1.x OpenGL, and also because we want to use GLUT as little as possible: you may not have GLUT available for mobile development, for instance.

This tutorial does not discuss normals. They are necessary to compute lighting (feel free to contribute a new section).