和曲线类似,曲面也可以通过控制点来模拟。实际上在很多场合也使用多边形来模拟平滑曲面,但使用控制点需要的计算机系统资源开销相对要少很多。同时,控制点个数越多,就越能够逼近真实的曲面,所以选择好的算法和适当的控制点个数相当重要。
OpenGL实现曲面的方法和曲线类似,它使用二维的glMap2和glEvalCoord2来代替glMap1和glEvalCoord1。同样,glMap2是设置二维曲面的定义(定义网格的运算子),glEvalCoord2计算具体的坐标点。两个函数的原型如下
void glMap2d(
GLenum target,
GLdouble u1,
GLdouble u2,
GLint ustride,
GLint uorder,
GLdouble v1,
GLdouble v2,
GLint vstride,
GLint vorder,
const GLdouble *points
);
void glMap2f(
GLenum target,
GLfloat u1,
GLfloat u2,
GLint ustride,
GLint uorder,
GLfloat v1,
GLfloat v2,
GLint vstride,
GLint vorder,
const GLfloat *points
);
target 参数含义和glMap1函数中的一样,取值也基本相同,只是把其中的GL_MAP1_*换成GL_MAP2_*。
u1, u2, v1,v2 含义同glMap1的u1,u2,只是从一维扩展到了二维。ustride, vstride相当于glMap1中的stride,uorder,vorder相当于order,points仍然是控制点数据指针。
glEvalCoord根据glMap的定义计算拟合过程坐标值,其函数原型如下,其中glEvalCoord1针对的是一维的曲线,而glEvalCoord2针对的是二维曲面。
void glEvalCoord1d(
GLdouble u
);
void glEvalCoord1f(
GLfloat u
);
void glEvalCoord2d(
GLdouble u,
GLdouble v
);
void glEvalCoord2f(
GLfloat u,
GLfloat v
);
void glEvalCoord1dv(
const GLdouble * u
);
void glEvalCoord1fv(
const GLfloat * u
);
void glEvalCoord2dv(
const GLdouble * u
);
void glEvalCoord2fv(
const GLfloat * u
);
其中u,v分别表示介于glMap定义的[u1 ,u2]、[v1,v2]范围之间的u,v值,计算用于拟合曲线或曲面的坐标值。
当函数原型是glEvalCoord*v时,表示参数是一个向量,如果是一维的,则该向量只有一个值,表示u值,如果是二维的,则表示有两个值,表示u和v。
Bezier曲面是Bezier曲线交织而成的曲面,BEZIER 曲线具有包络性,即三次BEZIER 曲线是二次BEZIER 曲线的簇的包络。N 次BEZIER曲面是N-1 次BEZIER 曲面的包络。另外,BEZIER 曲线还具有可分割性。下面我们来通过控制点来实现一个Bezier曲面的绘制。
//先定义20个控制点,u方向5个,v方向4个
GLfloat CtrlPoints[4][5][3] =
{
{{-1.5, 0.0, -2.0}, {-0.7, 0.7, -2.0},{0.0, 5.0, -2.0},
{0.7, 0.7, -2.0}, {1.5, 0, -2.0}},
{{-1.5, 0.0, -1.0}, {-0.7, 0.7, -1.0},{0.0, 2.0, -1.0},
{0.7, 0.707, -1.0}, {1.5, 0, -1.0}},
{{-1.5, 0.0, 1.0}, {-0.7, 0.7, 1.0},{0.0, 2.0, 1.0},
{0.7, 0.7, 1.0}, {1.5, 0, 1.0}},
{{-1.5, 0.0, 2.0}, {-0.7, 0.7, 2.0},{0.0, 5.0, 2.0},
{0.7, 0.7, 2.0}, {1.5, 0, 2.0}}
};
void glMain()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glColor3f(0.0f, 0.0f, 0.0f);
glTranslatef(0.0f, -0.5f, -6.0f);
glRotatef(30, 1.0, 1.0, 1.0);
//设置曲面的控制点参数
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 5, 0, 1, 15, 4, &CtrlPoints[0][0][0]);
glEnable(GL_MAP2_VERTEX_3);
//设置曲面网格的u、v值
glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
//开始绘制曲面网管
for (int j = 0; j <= 10; j++)
{
glBegin(GL_LINE_STRIP);
for (i = 0; i <= 30; i++)
glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/10.0);
glEnd();
glBegin(GL_LINE_STRIP);
for (i = 0; i <= 30; i++)
glEvalCoord2f((GLfloat)j/10.0, (GLfloat)i/30.0);
glEnd();
}
SwapBuffers(g_hDC);
}
程序运行效果如图10-10所示,可以看到一个马鞍状的网格曲面。