Skip to content

44. Evaluators, Curves and Surfaces

Dated: 14-07-2025

Evaluators

A Bézier curve1 is a vector valued function2 of one variable

\[ \vec C(u) = \begin{bmatrix} \vec X(u) & \vec Y(u) & \vec Z(u) \end{bmatrix} \]

where \(u \in [0, 1]\)

A Bézier surface1 patch is a vector valued function2 of two variables

\[ \vec S(u, v) = \begin{bmatrix} \vec X(u, v) & \vec Y(u, v) & \vec Z(u, v) \end{bmatrix} \]

To use an evaluator, first define the function C() or S(), enable it, and then use the glEvalCoord1() or glEvalCoord2() command instead of glVertex*().

One Dimensional Evaluators

This section presents an example of using one dimensional evaluators to draw a curve. It then describes the commands and equations that control evaluators.

A Simple Bézier Curve1

#include <GL/gl.h>
#include <GL/glu.h>
#include <stdlib.h>
#include <GL/glut.h>

GLfloat ctrlpoints[4][3] = {
    {-4.0, -4.0, 0.0}, {-2.0, 4.0, 0.0},
    { 2.0, -4.0, 0.0}, { 4.0, 4.0, 0.0}
};

void init(void) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
    glEnable(GL_MAP1_VERTEX_3);
}

void display(void) {
    int i;

    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glBegin(GL_LINE_STRIP);

    for (i = 0; i <= 30; i++)
        glEvalCoord1f((GLfloat) i/30.0);

    glEnd();

    /* The following code displays the control points as dots. */
    glPointSize(5.0);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);

    for (i = 0; i < 4; i++)
        glVertex3fv(&ctrlpoints[i][0]);

    glEnd();
    glFlush();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (w <= h)
        glOrtho(
            -5.0, 5.0, -5.0 * (GLfloat)h / (GLfloat)w,
             5.0 * (GLfloat)h / (GLfloat)w, -5.0, 5.0
        );
    else
        glOrtho(
            -5.0 * (GLfloat)w / (GLfloat)h,
             5.0 * (GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0
        );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();

    return 0;
}

cs602_i_44_1.png

Code result

Pay attention to the following statement from the code

glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
Argument Meaning
GL_MAP1_VERTEX_3 3 dimensional control points are provided and 3 dimensional vertices are produced
\(0.0\) Low value of parameter u
\(1.0\) High value of parameter u
\(3\) The number of floating point values to advance in the data between one control point and the next
\(4\) The order of of the spline which is degree + 1. Since it is cubic, therefore, degree is \(3\)
&ctrlpoints[0][0] Pointer to the first control point's data

Defining and Evaluating a One Dimensional Evaluator

The Bernstein polynomial of degree \(n\) (or order \(n + 1\)) is given by

\[B_i^n(u) = \binom{n}{i} u^i (1 - u)^{n - i}\]

If \(P_i\) is the set3 of control points (of any dimension \(n\) then)

\[C(u) = \sum_{i = 0}^n B_i^n(u) P_i\]

represents a Bézier curve1 as \(u\) varies from \(0.0\) to \(1.0\).
To represent the same curve but \(u \in [u_1, u_2]\), evaluate

\[C\left(\frac{u - u_1}{u_2 - u_1}\right)\]
  • void glMap1f(GLenum target, TYPE u1, TYPE u2, GLint stride, GLint order, const TYPE *points);
  • void glMap1d(GLenum target, TYPE u1, TYPE u2, GLint stride, GLint order, const TYPE *points);

  • target tells what the control points represent.
    • vertices
    • RGBA color data
    • normal vectors4
    • texture coordinates
  • u1 shows the lower range for variable \(u\).
  • u2 shows the upper range for variable \(u\).
  • stride tells the number of single or double precision values in each block of storage.
  • order tells the order, that is \(\text{order} = \text{degree} + 1\)
Parameter Meaning
GL_MAP1_VERTEX3 \(x, y, z\) vertex coordinates
GL_MAP1_VERTEX4 \(x, y, z, w\) vertex coordinates
GL_MAP1_INDEX color index
GL_MAP1_COLOR_4 R, G, B, A
GL_MAP1_NORMAL normal coordinates
GL_MAP1_TEXTURE_COORD_1 \(s\) texture coordinates
GL_MAP1_TEXTURE_COORD_2 \(s, t\) texture coordinates
GL_MAP1_TEXTURE_COORD_3 \(s, t, r\) texture coordinates
GL_MAP1_TEXTURE_COORD_4 \(s, t, r, q\) texture coordinates

If you have both defined and enabled

  • GL_MAP1_VERTEX_3
  • GL_MAP1_COLOR_4

then calls to glEvalCoord1() generate both

  • position
  • color

  • void glEvalCoord1f(TYPE u);
  • void glEvalCoord1d(TYPE u);
  • void glEvalCoord1fv(TYPE *u);
  • void glEvalCoord1dv(TYPE *u);

Causes evaluation of the enabled one dimensional maps.

Defining Evenly Spaced Coordinate Values in One Dimension

  • void glMapGrid1f(GLint n, TYPE u1, TYPE u2);
  • void glMapGrid1d(GLint n, TYPE u1, TYPE u2);

Defines a grid that goes from u1 to u2 in n steps, which are evenly spaced.

  • void glEvalMesh1(GLenum mode, GLint p1, GLint p2);

Applies the currently defined map grid to all enabled evaluators.
The mode can be

  • GL_POINT
  • GL_LINE

Two Dimensional Evaluators

In two dimensions, everything is similar to the one dimensional case, except that all the commands must take two parameters, \(u\) and \(v\), into account.
Mathematically, the definition of a Bézier surface1 patch is given by

\[ S(u, v) = \sum_{i = 0}^n \sum_{j = 0}^m B_i^n B_j^m (v) P_{ij} \]

Where \(P_{ij}\) are a set3 of \(m \times n\) control points and can represent

  • vertices
  • normals4
  • colors
  • texture coordinates

The procedure to use two dimensional evaluators is similar to the procedure for one dimension.

  1. Define the evaluator(s) with glMap2*().
  2. Enable them by passing the appropriate value to glEnable().
  3. Invoke them either by calling glEvalCoord2() between a glBegin() and glEnd() pair or by specifying and then applying a mesh with glMapGrid2() and glEvalMesh2().

Defining and Evaluating a Two Dimensional Evaluator

Use glMap2*() and glEvalCoord2*() to define and then invoke a two dimensional evaluator.

  • glMap2f(GLenum target, TYPE u1, TYPE u2, GLint ustride, GLint uorder, TYPE v1, TYPE v2, GLint vstride, GLint vorder, TYPE points);
  • glMap2d(GLenum target, TYPE u1, TYPE u2, GLint ustride, GLint uorder, TYPE v1, TYPE v2, GLint vstride, GLint vorder, TYPE points);

  • void glEvalCoord2f(TYPE u, TYPE v);
  • void glEvalCoord2d(TYPE u, TYPE v);
  • void glEvalCoord2fv(TYPE *values);
  • void glEvalCoord2dv(TYPE *values);

Causes evaluation of the enabled two dimensional maps. If either of the vertex evaluators is enabled

  • GL_MAP2_VERTEX_3
  • GL_MAP2_VERTEX_4

then the normal4 to the surface is computed analytically.

Two Dimensional Example: a Bézier Surface1

#include <GL/gl.h>
#include <GL/glu.h>
#include <stdlib.h>
#include <GL/glut.h>

GLfloat ctrlpoints[4][4][3] = {
    {
        {-1.5, -1.5,  4.0}, {-0.5, -1.5, 2.0},
        { 0.5, -1.5, -1.0}, { 1.5, -1.5, 2.0}
    },
    {
        {-1.5, -0.5, 1.0}, {-0.5, -0.5,  3.0},
        { 0.5, -0.5, 0.0}, { 1.5, -0.5, -1.0}
    },
    {
        {-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},
        { 0.5, 0.5, 3.0}, { 1.5, 0.5, 4.0}
    },
    {
        {-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
        { 0.5, 1.5,  0.0}, { 1.5, 1.5, -1.0}
    }
};

void display(void) {
    int i, j;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glPushMatrix ();
    glRotatef(85.0, 1.0, 1.0, 1.0);

    for (j = 0; j <= 8; j++) {
        glBegin(GL_LINE_STRIP);

        for (i = 0; i <= 30; i++)
            glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0);

        glEnd();
        glBegin(GL_LINE_STRIP);

        for (i = 0; i <= 30; i++)
            glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0);

        glEnd();
    }

    glPopMatrix();
    glFlush();
}
void init(void) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4,0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_FLAT);
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (w <= h)
        glOrtho(
            -5.0, 5.0, -5.0 * (GLfloat)h / (GLfloat)w,
             5.0 * (GLfloat)h / (GLfloat)w, -5.0, 5.0
        );
    else
        glOrtho(
            -5.0 * (GLfloat)w / (GLfloat)h,
             5.0 * (GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0
        );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();

    return 0;
}

cs602_i_44_2.png

Code Result

Defining Evenly Spaced Coordinate Values in Two Dimensions

  • void glMapGrid2f(GLint nu, TYPE u1, TYPE u2, GLint nv, TYPE v1, TYPE v2);
  • void glMapGrid2d(GLint nu, TYPE u1, TYPE u2, GLint nv, TYPE v1, TYPE v2);
  • void glEvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);

Defines a two dimensional map grid that goes from u1 to u2 in nu evenly spaced steps from v1 to v2 in nv steps (glMapGrid2*()) and then applies this grid to all enabled evaluators (glEvalMesh2()).

The mode parameter is glEvalMesh2() can be

  • GL_FILL
  • GL_POINT
  • GL_LINE
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdlib.h>
#include <GL/glut.h>

GLfloat ctrlpoints[4][4][3] = {
    {
        {-1.5, -1.5,  4.0}, {-0.5, -1.5, 2.0},
        { 0.5, -1.5, -1.0}, { 1.5, -1.5, 2.0}
    },
    {
        {-1.5, -0.5, 1.0}, {-0.5, -0.5,  3.0},
        { 0.5, -0.5, 0.0}, { 1.5, -0.5, -1.0}
    },
    {
        {-1.5, 0.5, 4.0}, {-0.5, 0.5, 0.0},
        { 0.5, 0.5, 3.0}, { 1.5, 0.5, 4.0}
    },
    {
        {-1.5, 1.5, -2.0}, {-0.5, 1.5, -2.0},
        { 0.5, 1.5,  0.0}, { 1.5, 1.5, -1.0}
    }
};

void display(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(85.0, 1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    glPopMatrix();
    glFlush();
}

void initlights(void) {
    GLfloat ambient[]       = {0.2, 0.2, 0.2, 1.0};
    GLfloat position[]      = {0.0, 0.0, 2.0, 1.0};
    GLfloat mat_diffuse[]   = {0.6, 0.6, 0.6, 1.0};
    GLfloat mat_specular[]  = {1.0, 1.0, 1.0, 1.0};
    GLfloat mat_shininess[] = {50.0};

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

void init(void) {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glEnable(GL_DEPTH_TEST);

    glMap2f(
        GL_MAP2_VERTEX_3, 0, 1, 3, 4,
        0, 1, 12, 4, &ctrlpoints[0][0][0]
    );

    glEnable(GL_MAP2_VERTEX_3);
    glEnable(GL_AUTO_NORMAL);
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    initlights();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if (w <= h)
        glOrtho(
            -5.0, 5.0, -5.0 * (GLfloat)h / (GLfloat)w,
             5.0 * (GLfloat)h / (GLfloat)w, -5.0, 5.0
        );
    else
        glOrtho(
            -5.0 * (GLfloat)w / (GLfloat)h,
             5.0 * (GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0
        );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();

    return 0;
}

cs602_i_44_3.png

code result

References


  1. Read more about bezier curves

  2. Read more about vector valued functions

  3. Read more about sets

  4. Read more about surface normals