Выбрать главу

Ok, it will be very hard to understand Beziers without at least a basic understanding of the math behind it, however, if you just don't feel like reading this section or already know the math, you can skip it. First I will start out by describing the Bezier curve itself then move on to how to create a Bezier Patch.

Odds are, if you've ever used a graphics program you are already familiar with Bezier curves, perhaps not by that name though. They are the primary method of drawing curved lines and are commonly represented as a series of points each with 2 points representing the tangent at that point from the left and right. Here's what one looks like:

This is the most basic Bezier curve possible (longer ones are made by attaching many of these together (many times without the user realizing it)). This curve is actually defined by only 4 points, those would be the 2 ending control points and the 2 middle control points. To the computer, all the points are the same, but to aid in design we often connect the first and the last two, respectively, because those lines will always be tangent to the endpoint. The curve is a parametric curve and is drawn by finding any number of points evenly spaced along the curve and connecting them with straight lines. In this way you can control the resolution of the patch (and the amount of computation). The most common way to use this is to tesselate it less at a farther distance and more at a closer distance so that, to the viewer, it always appears to be a perfectly curved surface with the lowest possible speed hit.

Bezier curves are based on a basis function from which more complicated versions are derived. Here's the function:

t + (1 – t) = 1

Sounds simple enough huh? Well it really is, this is the Bezier most basic Bezier curve, a 1st degree curve. As you may have guessed from the terminology, the Bezier curves are polynomials, and as we remember from algebra, a 1st degree polynomial is just a straight line; not very interesting. Well, since the basis function is true for all numbers t, we can square, cube, whatever, each side and it will still be true right? Well, lets try cubing it.

(t + (1-t))^3 = 1^3

t^3 + 3*t^2*(1-t) + 3*t*(1-t)^2 + (1-t)^3 = 1

This is the equation we use to calculate the most common Bezier, the 3rd degree Bezier curve. This is most common for two reasons, a) it's the lowest degree polynomial that need not necesarily lie in a plane (there are 4 control points) and b) the tangent lines on the sides are not dependant on one another (with a 2nd degree there would be only 3 control points). So do you see the Bezier curve yet? Hehe, me neither, that's because I still need to add one thing.

Ok, since the entire left side is equal to 1, it's safe to assume that if you add all the components they should still equal one. Does this sound like it could be used to descide how much of each control point to use in calculating a point on the curve? (hint: just say yes ;-) ) Well you're right! When we want to calculate the value of a point some percent along the curve we simply multiply each part by a control point (as a vector) and find the sum. Generally, we'll work with 0 <= t <= 1, but it's not technically necesary. Confused yet? Here's the function:

P1*t^3 + P2*3*t^2*(1-t) + P3*3*t*(1-t)^2 + P4*(1-t)^3 = Pnew

Because polynomials are always continuous, this makes for a good way to morp between the 4 points. The only points it actually reaches though are P1 and P4, when t = 1 and 0 respectively.

Now, that's all well and good, but how can I use these in 3D you ask? Well it's actually quite simple, in order to form a Bezier patch, you need 16 control points (4*4), and 2 variables t and v. What you do from there is calculate a point at v along 4 of the parallel curves then use those 4 points to make a new curve and calculate t along that curve. By calculating enough of these points, we can draw triangle strips to connect them, thus drawing the Bezier patch.

Well, I suppose that's enough math for now, on to the code!

#include <windows.h> // Header File For Windows

#include <math.h> // Header File For Math Library Routines

#include <stdio.h> // Header File For Standard I/O Routines

#include <stdlib.h> // Header File For Standard Library

#include <gl\gl.h> // Header File For The OpenGL32 Library

#include <gl\glu.h> // Header File For The GLu32 Library

#include <gl\glaux.h> // Header File For The Glaux Library

typedef struct point_3d { // Structure For A 3-Dimensional Point ( NEW )

 double x, y, z;

} POINT_3D;

typedef struct bpatch { // Structure For A 3rd Degree Bezier Patch ( NEW )

 POINT_3D anchors[4][4]; // 4x4 Grid Of Anchor Points

 GLuint dlBPatch; // Display List For Bezier Patch

 GLuint texture; // Texture For The Patch

} BEZIER_PATCH;

HDC hDC=NULL; // Private GDI Device Context

HGLRC hRC=NULL; // Permanent Rendering Context

HWND hWnd=NULL; // Holds Our Window Handle

HINSTANCE hInstance; // Holds The Instance Of The Application

DEVMODE DMsaved; // Saves The Previous Screen Settings ( NEW )

bool keys[256]; // Array Used For The Keyboard Routine

bool active=TRUE; // Window Active Flag Set To TRUE By Default

bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default

GLfloat rotz = 0.0f; // Rotation About The Z Axis

BEZIER_PATCH mybezier; // The Bezier Patch We're Going To Use ( NEW )

BOOL showCPoints=TRUE; // Toggles Displaying The Control Point Grid ( NEW )

int divs = 7; // Number Of Intrapolations (Controls Poly Resolution) ( NEW )

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc

The following are just a few quick functions for some simple vector math. If you're a fan of C++ you might consider using a point class (just make sure it's 3d).

// Adds 2 Points. Don't Just Use '+' ;)

POINT_3D pointAdd(POINT_3D p, POINT_3D q) {

 p.x += q.x; p.y += q.y; p.z += q.z;

 return p;

}

// Multiplies A Point And A Constant. Don't Just Use '*'

POINT_3D pointTimes(double c, POINT_3D p) {

 p.x *= c; p.y *= c; p.z *= c;

 return p;

}

// Function For Quick Point Creation

POINT_3D makePoint(double a, double b, double c) {

 POINT_3D p;

 p.x = a; p.y = b; p.z = c;

 return p;

}

This is basically just the 3rd degree basis function written in C, it takes a variable u and an array of 4 points and computes a point on the curve. By stepping u in equal increments between 0 and 1, we'll get a nice approximation of the curve.

// Calculates 3rd Degree Polynomial Based On Array Of 4 Points

// And A Single Variable (u) Which Is Generally Between 0 And 1

POINT_3D Bernstein(float u, POINT_3D *p) {

 POINT_3D a, b, c, d, r;

 a = pointTimes(pow(u,3), p[0]);