Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
64 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Curves
 Curved Surfaces

 Printable version
 Discuss this article

Introduction

In this tutorial I will attempt to give you an understanding of a common way to make curved lines and surfaces. I will also give you some coding examples that will show you that it actually works.

I will assume that you know how to use c++ in the coding examples. The coding examples also contain some OpenGL function calls, but they are commented and not too hard to understand.

I will also assume that you know some basic math, but I'm starting off pretty easy by introducing parametric curves so you should be able to grasp it even if you don't have the greatest math knowledge.

I have tested the coding examples, and I have looked over the text several times, so they should be fairly bug free. Still, if you find anything that looks like its wrong, let me know. You can find my email address at the bottom of the page.

Parametric curves

You are probably familiar with curves like y = x² or f(x)=x². A parametric curve is similar to those, but the x- and y-values are calculated in separate functions. To do this we need another variable (I will call it a). So instead of f(x), we say X(a) and Y(a), where X(a) gives you an x-value for each value of a, and Y(a) gives you an corresponding y-value for the same value of a. Here is an example:

X(a) = a/2
Y(a) = a+5

If we substitute a with a number we can get a point that lies on the curve/line:

a = 6

X(6) = 6/2 = 3 = x
Y(6) = 6+5 = 11 = y

We now know that (3,11) is a point on the curve.

We can easily make a curve in 3D just by defining Z(a).

Bezier curves

Simple line

The simplest form is a straight line from a control point A, to a control point B.

I will use Ax to denote the x-coordinate of control point A, and Ay to denote the y-coordinate. The same goes for B.

I am introducing another variable, b, just to make it a bit more simple to read the functions. However, the variable b is only a in disguise, b is always equal to 1-a. So if a = 0.2, then b = 1-0.2 = 0.8. So when you see the variable b, think: (1-a).

This parametric curve describes a line that goes from point A, to point B when the variable a goes from 1.0 to 0.0:

X(a) = Ax·a + Bx·b
Y(a) = Ay·a + By·b
Z(a) = Az·a + Bz·b

If you set a = 0.5 (and thus also b = 0.5, since b = 1.0-a = 1.0-0.5 = 0.5) then X(a), Y(a) and Z(a) will give you the 3D coordinate of the point on the middle of the line from point A to point B.

Note that the reason this works is because a + b is always equal to one, and when you multiply something with one, it will stay unchanged. This makes the curve behave predictably and lets you place the control points anywhere in the coordinate system, since when a = 1.0 then b = 0.0 thus making the point equal to one of the control points, and completely ignoring the other one.

Quadratic

Ok, now we have done lines, but what about curves?

This: (a+b), where b = (1.0-a) is the key to everything. We know that a polynomial function is a curve, so why not try to make (a+b) a polynomial function? Let's try:

(a+b)² = a² + 2a·b + b²

Knowing that b=1-a we see that a² + 2·a·b + b² is still equal to one, since (a+b) = 1, and 1² = 1.

We now need three control points, A, B and C, where A and C are the end points of the curve, and B decides how much and in which direction it curves. Except for that, it is the same as with the parametric line.

X(a) = Ax·a² + Bx·2·a·b + Cx·b²
Y(a) = Ay·a² + By·2·a·b + Cy·b²
Z(a) = Az·a² + Bz·2·a·b + Cz·b²

Still, if you set a = 0.5, then a² = 0.25, 2·a·b = 0.5 and b² = 0.25 giving the middle point, B, the biggest impact and making point A and C pull equally to their sides. If you set a = 1.0, then a² = 1.0, a·b = 0.0 and b² = 0.0 meaning that result is the control point A itself, just the same as setting a = 0.0 will return the coordinates of control point C.

Cubic

We can also increase the number of control points using (a+b)³ instead of (a+b)², giving you more control over how to bend the curve.

(a+b)³ = a³ + 3·a²·b + 3·a·b² + b³

Now we need four control points A, B, C and D, where A and D are the end points.

X(a) = Ax·a³ + Bx·3·a²·b + Cx·3·a·b² + Dx·b³
Y(a) = Ay·a³ + By·3·a²·b + Cy·3·a·b² + Dy·b³
Z(a) = Az·a³ + Bz·3·a²·b + Cz·3·a·b² + Dz·b³

It still works the same way as the previous ones, as a goes from 1.0 to 0.0 (and thus b from 0.0 to 1.0) the functions will return coordinates on a smooth line from control point A to control point D, curving towards B and C on the way.

You can easily use (a+b) to the power of n and get n+1 control points (where n is any integer equal to, or greater than one). But remember that the more control points you have, the more work it is, both for you and for the computer. This might not seem like much when only talking about lines, but when it comes to surfaces it definitely has something to say.

Personally I like the cubic ones best. You can easily make circles with them, and you can control the direction of the curve independently at each control point.

Example of how to draw a cubic curve using OpenGL and C++:


this is how it will look
(excluding the red lines and the letters)

// Control points (substitute these values with your own if you like)
double Ax = -2.0; double Ay = -1.0; double Az = 1.0;
double Bx = -1.0; double By = 3.0; double Bz = 1.0;
double Cx = 1.0; double Cy = -3.0; double Cz = -1.0;
double Dx = 2.0; double Dy = 1.0; double Dz = -1.0;

// Points on the curve
double X;
double Y;
double Z;

// Variable
double a = 1.0;
double b = 1.0 - a;

// Tell OGL to start drawing a line strip
glBegin(GL_LINE_STRIP);

/* We will not actually draw a curve, but we will divide the curve into small
points and draw a line between each point. If the points are close enough, it
will appear as a curved line. 20 points are plenty, and since the variable goes
from 1.0 to 0.0 we must change it by 1/20 = 0.05 each time */

for(int i = 0; i <= 20; i++)
{
  // Get a point on the curve
  X = Ax*a*a*a + Bx*3*a*a*b + Cx*3*a*b*b + Dx*b*b*b;
  Y = Ay*a*a*a + By*3*a*a*b + Cy*3*a*b*b + Dy*b*b*b;
  Z = Az*a*a*a + Bz*3*a*a*b + Cz*3*a*b*b + Dz*b*b*b;
  
  // Draw the line from point to point (assuming OGL is set up properly)
  glVertex3d(X, Y, Z);

  // Change the variable
  a -= 0.05;
  b = 1.0 - a;
}

// Tell OGL to stop drawing the line strip
glEnd();

/* Normally you will want to save the coordinates to an array for later use. And
you will probably not need to calculate the curve each frame. This code
demonstrates an easily understandable way to do it, not necessarily the most
useful way. */




Next : Curved Surfaces