Sign in to follow this  
VitaliBR

Cubic Bézier

Recommended Posts

Hi!

I'm trying to create a cubic Bezier curve in 3D. (x, y, z)

First, I made a grid floor using the following code:

[code]
// Draw a white grid "floor"
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
float sizefloor = sqrtf(MatrizM);
for (GLfloat i = 0.0; i <= sizefloor; i += 0.25)
{
glVertex3f(i, 0, sizefloor);
glVertex3f(i, 0, 0.0);
glVertex3f(sizefloor, 0, i);
glVertex3f(0.0, 0, i);
}
glEnd();
[/code]

see the grid floor:
[img]http://i40.tinypic.com/24fmux3.png[/img]

I'm trying to create a curve that leave the point (x=0.5,y=0,z=0) , and arrive at the point (x=2.5, y=0, z=0)
I did as follows:
[img]http://i44.tinypic.com/24g7kna.jpg[/img]

[code]
GLfloat ctrlpoints[4][4][3] = {
{{0.5, 0.0, 0.0}, {0.5, 0.5, 0.0}, //row 1 column 1
{2.5, 0.5, 0.0}, {2.5, 0.0, 0.0}} //row 1 column 6
};


//init..
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);

//function render...
glColor3f(1.0, 1.0, 1.0);
glPushMatrix();
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= 30; i++) {
glEvalCoord2f((GLfloat)i/30.0, (GLfloat)1/5.0);
}
glEnd();
glPopMatrix();


[/code]

but it is not getting the right spot, as I define the array {2.5, 0.0, 0.0}

and does not come from the point set {0.5, 0.0, 0.0}


What am I doing wrong? [img]http://public.gamedev.net//public/style_emoticons/default/sad.png[/img]

I did some testing, I saw that maybe I'm missing function glEvalCoord2f, but I'm not sure

Share this post


Link to post
Share on other sites
The evaluators in OpenGL are completely insane, and highly in-efficient. Use maths, and the problem is substantially easier....


[code]
template<typename T>
T lerp(const T& a, const T& b, float t)
{
return a + (b-a)*t;
}
template<typename T>
T evalCubicBezier(const T& a, const T& b, const T& c, const T& d, float t)
{
const T AB = lerp(a, b, t);
const T BC = lerp(b, c, t);
const T CD = lerp(c, d, t);
const T ABBC = lerp(AB, BC, t);
const T BCCD = lerp(BC, CD, t);
return lerp(ABBC, BCCD, t);
}[/code]

Share this post


Link to post
Share on other sites
[quote name='RobTheBloke' timestamp='1329920930' post='4915497']
The evaluators in OpenGL are completely insane, and highly in-efficient. Use maths, and the problem is substantially easier....


[code]
template<typename T>
T lerp(const T& a, const T& b, float t)
{
return a + (b-a)*t;
}
template<typename T>
T evalCubicBezier(const T& a, const T& b, const T& c, const T& d, float t)
{
const T AB = lerp(a, b, t);
const T BC = lerp(b, c, t);
const T CD = lerp(c, d, t);
const T ABBC = lerp(AB, BC, t);
const T BCCD = lerp(BC, CD, t);
return lerp(ABBC, BCCD, t);
}[/code]
[/quote]


Interesting, but why do you think the evaluators in OpenGL are bad?

I looked at your code I did as follows:
[code] // Control points (substitute these values with your own if you like)
double Ax = 0.0; double Ay = 0.0; double Az = 0.0;
double Bx = 0.0; double By = 1.0; double Bz = 0.0;
double Cx = 2.0; double Cy = 1.0; double Cz = 0.0;
double Dx = 2.0; double Dy = 0.0; double Dz = 0.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();
[/code]

I'll save the coordinates to an array for later use. and thus do not need to calculate the curve of each frame.
Will I have to draw curves 9000, do you think will be very slow?

Share this post


Link to post
Share on other sites
[quote name='VitaliBR' timestamp='1330006266' post='4915864']
Interesting, but why do you think the evaluators in OpenGL are bad?[/quote]

On the SGi machines back in the mid 90's they were seen as pretty radical, and SGi optimised the hell out of their implementation (a 200Mhz SGi could wipe the floor with a 1Ghz PC + Geforce 1 when using evaluators). There has never been a Geforce/Quadro/Ati card that has ever provided hardware support for the functionality, and there never will be. On the geforce3, Nvidia added nvEvaluators in hardware, but they were had the same failings as glEvaluators, and support was quietly dropped from the drivers.

glEvaluators forces you into a complete re-evaluation of the curve/surface at every possible opportunity. As soon as your scene progresses to something relatively complex, the CPU overhead really starts hurting! It's just insane that you can draw exactly the same curve, with the same tessellation each frame, and still be forced to re-evaluate the data. Re-evaluating only when the LOD changes makes much more sense these days (and upload the data into a VBO).

Basically my distaste for glEvaluators can be summed up with this:

[code]
struct CubicBezier
{
public:

void setLod(int lod)
{
m_lod = lod;
m_blends.resize( (lod+1) * 4 );
m_outPoints.resize( lod + 1 );

float increment = 1.0f / lod;
for(int i=0, j=0; i<=lod; ++i, j+=4)
{
float t = increment * i;
float invt = 1.0f - t;

float B0 = invt*invt*invt;
float B1 = 3.0f*invt*invt*t;
float B2 = 3.0f*invt*t*t;
float B3 = t*t*t;

m_blends[j ] = B0;
m_blends[j + 1] = B1;
m_blends[j + 2] = B2;
m_blends[j + 3] = B3;
}
}

void tesselate()
{
for(int i=0, j=0; i<=m_lod; ++i, j+=4)
{
m_outPoints[i] =
m_points[0]*m_blends[j ] +
m_points[1]*m_blends[j+1] +
m_points[2]*m_blends[j+2] +
m_points[3]*m_blends[j+3];
}
}

private:

Vector3 m_points[4];
std::vector<float> m_blends;
std::vector<Vector3> m_outPoints;
int m_lod;
};
[/code]

This separation of blending function calculation, and tessellation, doesn't make too much sense for a cubic bezier curve (because it's not an overly complicated process). However if you switch to a more complex curve type (i.e. the cox-de-boor evaluation of a NURBS curve is a massive killer!), or if you transition to using bezier patches (tri strip indices, uv coords, colours, etc, can all be calculated in the setLod method), you can get some seriously insane speed improvements for animated curves and surfaces....

Basically there are tonnes and tonnes and tonnes of optimisations possible in C++/OpenGL, but they all vanish as soon as you use glEvaluators. Some of the tricks I've used in the past have involved creating 'lod change' queues which tries to schedule changes in lod for the curves over a number of frames (i.e. If 500 curves need to have their lods changed, you can do 50 per frame. 10 frames later it'll all be done, but the frame rate stays constant).


[quote]
Will I have to draw curves 9000, do you think will be very slow?
[/quote]
It depends on what form of tessellation you want to use. If it's as done here and in your code, then it will be fine (you could generate blending function values for specific lod values and share them between curves if it gets too hairy). If it's based upon viewing distance, and the 't' values use non linear increments, then it may get a bit interesting. Using a geometry shader may help*.

[i]* with a caveat... most GPUs have an upper limit of the amount of vertices you can generate in a geometry shader. IIRC it's about 128 verts for vertex+colour. [/i]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this