OpenGL Coordinates from Bezier Curves

2019-01-22 16:51发布

问题:

Basically, I need to get all the coordinates drawn from a Bezier curve implementation in OpenGL. Specifically, I need the coordinates to move a sphere object (baseball) in my scene along a curved trajectory path. This is what I use to draw my curve:

GL2 gl = drawable.getGL().getGL2();    
float ctrlpoints[][] = new float[][]{
            {0.0f, 0.0f, 60f},
            {0.0f, 3.0f, 45.0f},
            {0.0f, 2.0f, 15.0f},
            {0.0f, 1.0f, 0f}};
    FloatBuffer ctrlpointBuf = FloatBuffer.allocate(ctrlpoints[0].length * ctrlpoints.length);
        for (int i = 0; i < ctrlpoints.length; i++) {
            for (int j = 0; j < 3; j++) {
                ctrlpointBuf.put(ctrlpoints[i][j]);
            }
        }
        ctrlpointBuf.rewind();

        gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, numControlPoints, ctrlpointBuf);
        gl.glEnable(GL2.GL_MAP1_VERTEX_3);

        gl.glColor3f(1.0f, 1.0f, 1.0f);
        gl.glBegin(GL2.GL_LINE_STRIP);
        for (int i = 0; i <= 30; i++) {
            gl.glEvalCoord1f((float) i / (float) 30.0);
        }
        gl.glEnd();

Does any one have any idea how to get the points out of this implementation?

回答1:

A Bezier curve is quite easy to compute. First of all is separable, that means that you can compute it one coordinate at a time (first x, then y, then z...). For a given coordinate the following is a function that uses the definition:

double bezier(double A,  // Start value
              double B,  // First control value
              double C,  // Second control value
              double D,  // Ending value
              double t)  // Parameter 0 <= t <= 1
{
    double s = 1 - t;
    double AB = A*s + B*t;
    double BC = B*s + C*t;
    double CD = C*s + D*t;
    double ABC = AB*s + BC*t;
    double BCD = BC*s + CD*t;
    return ABC*s + BCD*t;
}

Note that in the above function the parameter t is not the arc-length parameter for the curve but a generic parameter that goes from t=0 (where the point is at the beginning of the curve) to t=1 (where the point is at the end of the curve).

An interactive version of the above picture where you can drag A, B, C, D and AB points is available here. It's implemented with html/js/canvas and tested only on Chrome, Firefox, Safari.

If you need to move your objects at a controlled specific speed in XYZ an easy way is to compute an approximated polyline (for example by sampling the curve for 100 values of t) and then walk at constant speed on the resulting polyline.

The true arc-length parametrization for a Bezier cubic (i.e. using a parameter that is the length measured along the curve) is rather annoying to compute (IIRC there is no closed form solution for the integral).



回答2:

I think the following line in bezier() should read

double ABC = AB*s + CD*t;

instead of

double ABC = BC*s + CD*t;

A quick test with a .c program gives these results. Note, the curve coordinates start at 10.00 instead of at 20.00 with the above unmodified function.

~/sujith/cc > gcc oglBezier.c 
~/sujith/cc > ./a.out
Start. A=10.000000, B=20.000000, C=40.000000, D=5.000000, t=0.000000
Bezier pt= 10.000000
Bezier pt= 10.495490
Bezier pt= 10.981920
Bezier pt= 11.459230
Bezier pt= 11.927360
Bezier pt= 12.386250
Bezier pt= 12.835840
Bezier pt= 13.276070
Bezier pt= 13.706880
Bezier pt= 14.128210
Bezier pt= 14.540000
Bezier pt= 14.942190
Bezier pt= 15.334720
Bezier pt= 15.717530
Bezier pt= 16.090560
Bezier pt= 16.453750
Bezier pt= 16.807040
Bezier pt= 17.150370
Bezier pt= 17.483680
Bezier pt= 17.806910
Bezier pt= 18.120000
Bezier pt= 18.422890
Bezier pt= 18.715520
Bezier pt= 18.997830
Bezier pt= 19.269760
Bezier pt= 19.531250
Bezier pt= 19.782240
Bezier pt= 20.022670
Bezier pt= 20.252480
Bezier pt= 20.471610
Bezier pt= 20.680000
Bezier pt= 20.877590
Bezier pt= 21.064320
Bezier pt= 21.240130
Bezier pt= 21.404960
Bezier pt= 21.558750
Bezier pt= 21.701440
Bezier pt= 21.832970
Bezier pt= 21.953280
Bezier pt= 22.062310
Bezier pt= 22.160000
Bezier pt= 22.246290
Bezier pt= 22.321120
Bezier pt= 22.384430
Bezier pt= 22.436160
Bezier pt= 22.476250
Bezier pt= 22.504640
Bezier pt= 22.521270
Bezier pt= 22.526080
Bezier pt= 22.519010
Bezier pt= 22.500000
Bezier pt= 22.468990
Bezier pt= 22.425920
Bezier pt= 22.370730
Bezier pt= 22.303360
Bezier pt= 22.223750
Bezier pt= 22.131840
Bezier pt= 22.027570
Bezier pt= 21.910880
Bezier pt= 21.781710
Bezier pt= 21.640000
Bezier pt= 21.485690
Bezier pt= 21.318720
Bezier pt= 21.139030
Bezier pt= 20.946560
Bezier pt= 20.741250
Bezier pt= 20.523040
Bezier pt= 20.291870
Bezier pt= 20.047680
Bezier pt= 19.790410
Bezier pt= 19.520000
Bezier pt= 19.236390
Bezier pt= 18.939520
Bezier pt= 18.629331
Bezier pt= 18.305761
Bezier pt= 17.968751
Bezier pt= 17.618241
Bezier pt= 17.254171
Bezier pt= 16.876481
Bezier pt= 16.485111
Bezier pt= 16.080001
Bezier pt= 15.661091
Bezier pt= 15.228321
Bezier pt= 14.781631
Bezier pt= 14.320961
Bezier pt= 13.846251
Bezier pt= 13.357441
Bezier pt= 12.854471
Bezier pt= 12.337281
Bezier pt= 11.805811
Bezier pt= 11.260001
Bezier pt= 10.699791
Bezier pt= 10.125121
Bezier pt= 9.535931
Bezier pt= 8.932161
Bezier pt= 8.313751
Bezier pt= 7.680641
Bezier pt= 7.032771
Bezier pt= 6.370081
Bezier pt= 5.692512
Bezier pt= 5.000002

Test program oglBezier.c:

#include <stdio.h>


double bezier(double A,  // Start value
              double B,  // First control value
              double C,  // Second control value
              double D,  // Ending value
              double t)  // Parameter 0 <= t <= 1
{
    double s = 1 - t;
    double AB = A*s + B*t;
    double BC = B*s + C*t;
    double CD = C*s + D*t;
    double ABC = AB*s + CD*t;
    double BCD = BC*s + CD*t;
    return ABC*s + BCD*t;
}

main()
{
        double a,b,c,d,t;

        a = 10.0f;
        b = 20.0f;
        c = 40.0f;
        d = 5.0f;
        t = 0.0f;
        printf("Start. A=%f, B=%f, C=%f, D=%f, t=%f\n", a,b,c,d,t);

        while(1)
        {   
                if(t>1.0f)
                        break;

                printf("Bezier pt= %f\n", bezier(a,b,c,d,t));

                t += 0.01f;
        }   

        return 1;
}


回答3:

In case anyone's interested, this is how I finally implemented pitching a baseball sphere in my scene using a trajectory drawn with a bezier curve. I used 6502's very illuminating function to compute xyz coordinates for the ball at each frame. The starting value is wherever the ball is on the curve as the frame is being drawn. The ending value and the control points are the exact same for drawing the entire curve. The thing that took me a while to figure out was what arguments to give the parameter t.

I finally figured out that it was the values from 0, the START of the curve, to 1, the END of the curve. So, a baseball pitched from the pitchers mound will be t=0 at 60.5 feet from home plate, and t=1 at 0 feet from home plate. So t might be computed as simply as

t += 1.0 / 60.5;

I drew the whole curve first as a GL_LINE_STRIP, and then I computed the coordinates for the ball at each frame. When I ran my program, the ball exactly followed the curving line trajectory. Thanks to all who gave answers and made comments.



回答4:

I took the formula, corrected it, and created a test app on github at git://github.com/rmd6502/BezierLicious.git . I strongly recommend against using anything I did there in a production app - consider it for research purposes only!!



回答5:

You should take a look at De Casteljau's algorithm. It allows you to recursively refine your curve. Just abort after a few steps and use the resulting vertices. You could also evaluate the control points directly along the parametric position and use those. It's not that hard to evaluate Bézier-Splines directly (you could do it in the vertex shader for instance!) The advantage of De Casteljau's algorithm is that you never have to compute (high) powers, though for your case both should be fine (and the direct evaluation is definitely easier to implement/test.)