相对于直线的生成,曲线的生成要难一些。对于参数曲线,通常是根据其切向量、法向量、弧长、曲率等使用插值、拟合来逼近曲线。当拟合后的若干小线段和曲线本身之间的误差很小以至于人眼看不出来时,我们就认为拟合的曲线就是我们需要的结果。对于非参数曲线,则通过控制点,应用不同的规则来拟合曲线,例如Hermit曲线,Bezier曲线等。
OpenGL中定义曲线的函数是glMap1,其原型如下
void glMap1d(
GLenum target,
GLdouble u1,
GLdouble u2,
GLint stride,
GLint order,
const GLdouble *points
);
void glMap1f(
GLenum target,
GLfloat u1,
GLfloat u2,
GLint stride,
GLint order,
const GLfloat *points
);
target参数表示用于生成曲线的控制点的类型以及points应该提供的数据量。target的具体取值及其含义如下表10-1所示。
表10-1 glMap1参数含义
target取值 | 含义 |
GL_MAP1_VERTEX_3 | 每一个控制点包含了3个浮点数,来表示x、y、z。OpenGL内部以glVertex3 命令来生成这些点。 |
GL_MAP1_VERTEX_4 | 每一个控制点包含了4个浮点数,来表示x、y、z、w。OpenGL内部以glVertex4命令来生成这些点。 |
GL_MAP1_INDEX | 每一个控制点只有1个浮点数,来表示颜色索引值。OpenGL内部以glIndex命令来实现。但是当前的颜色索引值不会因为这些内部使用的glIndex命令而改变。 |
GL_MAP1_COLOR_4 | 每一个控制点包含了4个浮点数,来表示红、率、蓝、Alpha值。OpenGL内部以glColor4来实现。但是当前的颜色值并不会因为这些内部使用glColor4命令而改变。 |
GL_MAP1_NORMAL | 每一个控制点包含了3个浮点数,来表示法向量的x、y、z分量。OpenGL内部以glNormal来实现。不过这不会影响当前法向量。 |
GL_MAP1_TEXTURE_COORD_1 | 每一个控制点只有1个浮点数,来表示s纹理坐标。OpenGL内部以glTexCoord1 命令来实现。但是不影响当前的纹理坐标值。 |
GL_MAP1_TEXTURE_COORD_2 | 每一个控制点有2个浮点数,来表示s、t纹理坐标。OpenGL内部以glTexCoord2命令来实现。但是不影响当前的纹理坐标值。 |
GL_MAP1_TEXTURE_COORD_3 | 每一个控制点有3个浮点数,来表示s、t和r纹理坐标。OpenGL内部以glTexCoord3命令来实现。但是不影响当前的纹理坐标值。 |
GL_MAP1_TEXTURE_COORD_4 | 每一个控制点有4个浮点数,来表示s、t、r和q纹理坐标。OpenGL内部以glTexCoord4命令来实现。但是不影响当前的纹理坐标值。 |
u1, u2 表示在glEvalCoord1函数中的参数u的范围。stride表示points数据中相邻两个控制点之间的浮点数或者双精度数的个数,这样就允许将控制点嵌入到任意的数据结果中,唯一的约束就是要求这些特定的控制点必须占用相邻的内存空间。例如如果控制点为4个,每个控制点的格式为(x,y,z),则stride就是3。
order表示控制点的个数。points就是控制点的数据指针。
OpenGL提供了一种使用多项式或有理多项式来产生顶点、法向量、纹理坐标和颜色值。这些值计算出来后就用于进一步的应用,就好像这些数据是由glVertex, glNormal, glTexCoord和glColor等命令生成是一样,只是不改变当前的设置的值而已。
OpenGL可以描述所有的多项式和有理多项式样条,最高支持到32次多项式。这就意味这OpenGL可以实现几乎所有的在计算机图形学中用到的样条,如B样条,Bezier曲线,Hermite样条等等。
OpenGL是基于Bernstein多项式来定义样条的,定义 为
其中Ri 是一个控制点, 是第i次的Bernstein多项式,其阶次为n ,顺序为第n + 1项。
glMap1函数用于定义多项式的种类,确定生成什么样的数据。glEvalCoord1函数用于根据控制点生成曲线的坐标并且进行绘制。
下面我们来实现一条Bezier曲线的绘制。
//控制点数据
GLfloat CtrlPoints[5][3] =
{
{-5.0, -3.0, 0.0},
{-2.0, 4.0, 0.0},
{2.0, -4.0, 0.0},
{2.0, 4.0, 0.0},
{5.0, 3.0, 0.0}
};
void glMain()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glTranslatef(0.0f, 0.0f, -10.0f);
//对点进行反走样处理
glEnable(GL_BLEND);
glEnable(GL_POINT_SMOOTH);
//每设置曲线的生成方式,控制点数据
glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 5, &CtrlPoints[0][0]);
glEnable(GL_MAP1_VERTEX_3);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= 100; i++)
//计算每一个绘图点
glEvalCoord1f((GLfloat) i/100.0);
glEnd();
//把控制点也显示出来
glPointSize(5.0);
glBegin(GL_POINTS);
for (i = 0; i < 5; i++)
glVertex3fv(&CtrlPoints[i][0]);
glEnd();
SwapBuffers(g_hDC);//交换前后缓冲区
}
程序运行效果如图10-9所示,大的黑园点为控制点。