Planning the Spontaneous

It's more than just a blueprint.


Posted by Robert Chow on 20/10/2009

So there are two types of curves available that OpenGL offer.  These are NURBS (Non-unified-rational-B-splines) and Bézier Curves, or around here, known as the French-guy curve (if anyone knows how to correctly pronounce Bézier, do enlighten us!)  After playing around with NURBS, I decided it was too complex.  I didn’t understand a single parameter whatsoever, and I wasn’t really too prepared to spend hours trying to find out.  So I decided to launch into Bézier curves.

Bézier Curves

These are relatively easy to do in OpenGL.  There are just a few restrictions you have to understand.  The best way to understand these is to see how these curves are calculated.  Hello wikipedia.

There is this really useful animation on wikipedia that describes how a Bézier curve is drawn.  Assuming you’re not going to look for it yourself, I’ve kindly included it here too.

Cubic Bézier Curve Animation.  This animation describes how the curve is calculated using control points and time t, where 0 <= t <= 1.  This image has been taken fromézier_curve.

As you can see, this curve is drawn using 4 control points, P0-3.  At time t, intermediate points are calculated length t along each of the lines connecting the control points.  The same is done with these intermediate points, and again, until we result with just a single point.  This dictates where, at time t, the next point of the Bézier curve is drawn.  The order of the curve depends on how many control points there are.  Here there are 4 control points, resulting in a cubic curve.  If there were 3, it would be a quadratic curve, and if there were 5, the curve would have an order of 4.

So now we know we need control points, and how the curve is drawn, we can do this now in OpenGL.

controlPoints = new float[] { -4.0f, -0.0f, 0.0f, -5.0f, 4.0f, 0.0f, 0.0f, 4.0f, 0.0f, 4.0f, -0.0f, 0.0f };
Gl.glMap1f(Gl.GL_MAP1_VERTEX_3, 0f, 1.0f, 3, 4, controlPoints);

for (int i = 0; i <= 30; i++)
Gl.glEvalCoord1f((float)i / 30.0f);

The control points array are our control points, with each point including a x, y and z co-ordinate, thus 4 points * 3 co-ordinates results in an array of size 12.  This, and the next two lines, only need to be initialised the once.  The first of these next two lines takes the control points and uses them to create the Bézier curve.  The first parameter tells the function that we want to map 3 components to one point – ie. x, y and z.  The second and third parameters tells the function what linear mapping to use – for now, use the values 0 and 1.  The 3 represents the stride between the points – in this case it is 3, because we have x, y, and z components of one point, before we immediately start another.  The second to last parameter defines the number of control points we have, and last but by no means least, is the array storing the control points.  The third line simply just enables the mapping of this function.

The second section is where we actually draw the curve.  Using an accuracy of 30, we are in effect drawing 30 straight lines from t=0 to t=1.  The call in the for loop does all the work for us – it takes the value of t, evaluates that to where it corresponds in the Bézier curve, and then maps that vertex to the device context.  As we initialised the drawing of a line, it is included as the next point of the line.

Add a little bit of extra infromation such as the control points onto the screen, and you get something similar to this.

OpenGL Bézier CurveOpenGL Bézier Curve.  This curve was created using the code above.  I have tried to replicate a similar shape to the one in the animation.

This is all very good and all, but in reality, it’s quite slow.  It’s calculating the curve every single frame.  It shouldn’t need to do that.  Unfortunately OpenGL doesn’t give you the option of saving the computed vertices, so you have to do it every time.  The advantages of being able to save the vertices are, not only having to recalculate the same values over and over again, but of course, you can then store these values in, let’s say an array, and transfer them to the graphics card to be stored and used there.  Sound familiar?  Vertex Buffer Objects of course.  Hello wikipedia again.

Bézier Curves (Manual)

The algorithm for creating a cubic Bézier curve is quite simple.  It’s as follows:

B(t) = (1 – t)3P0 + 3(1 – t)2tP1 + 3(1 – t)t2P2 + t3P3, t ∈ [0,1]

Alright, it’s not that simple, but easy enough to read it.  You don’t have to understand how it comes to the conclusion of what B(t) is, just as long as you can translate it into code.  And that looks a little like this.

int accuracy = 30;
Vertices = new Vertex[accuracy + 1];
for (int i = 0; i <= accuracy; ++i)
float t = (float)i / accuracy;
Vertices[i] =(((float)Math.Pow((1.0f -t), 3) * P0)
+ (3.0f * (float)Math.Pow((1.0f -t), 2) * t * P1)
+ (3.0f * (1.0f -t) * (float)Math.Pow(t, 2) * P2)
+ ((float)Math.Pow(t, 3) * P3));

Where P0, P1, P2 and P3 are the control points.  These themselves are stored as type Vertex, where I have included operater overload functions to make my life a little easier.  Convert the vertices array into a float array, and you can use as you please – directly in immediate mode, or store them for use as a vertex buffer array.  The only time you will need to recalculate the vertices is when any of the control points change position.

Being able to understand how Bézier curves work and to then able to replicate that myself has given me a huge advantage.  As a result of this, I have been able to bring together quite a snazzy demo.

Bézier Curves in Bézier Curves

This takes a single Bézier curve, cubic, and you are then able to manipulate this any way you want using the handles, or control points as you may.  Want more curvature?  Done.  The curve can be expanded so there are multiple cubic Bézier curves within the initial curve.  The only thing I had to worry about was to make sure that all the subcurves join smoothly together – this is a call for handle dependency.  If a handle is controlling the weight of a point directly attached the curve, then it will also affect the other handles attached to this point.  This is what ensures the smoothness of the curve.  I won’t show the code, but here are a few screenshots.

Bézier Curve DemoBézier Curve Demo.  From top to bottom:  initial Bézier curve the demo produces;  the left-hand size control point and its handle is altered to produce a bowl shape; the curve is split into two; the new control point is raised upwards – in doing so, so are it’s handles;  one of the handles corresponding to the new middle point is adjusted – as a result, the opposing handle attached to the same control point mirrors the action – this ensures the 2 subcurves join together smoothly.  Each handle that is connected to a control point acts like a weight, whereby its weight can be measured by the distance between the handle and its control point.

Just to finish, I couldn’t but resist to show off.

Bézier Curve NameBézier Curve Name.  This has been produced using just the one line – it has many subcurves within it.  The grey lines show the weightings at each control point.



5 Responses to “DrawCurve(Bézier);”

  1. Bézier is pronounced “Bay-zee-ay”. Thought I’d give about the only useful thing I could! Good stuff though!

    Btw, I’m being a copycat and have also got a blog – but on stuff that you probably won’t be interested in. Hersh thinks I’m becoming the re-incarnation of Ian Hislop! Have a read – you might learn something!

  2. Stunning .. very stunning topic. I’m going to blog about it likewise!

  3. […] you recall from DrawCurve(Bézier);, near the end I was able to draw a master curve, encapsulating the bézier sub-curves.  To […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: