如何修改OpenGL代码,用GL_MAP1_VERTEX_3生成6条贝塞尔曲线?
问题
尝试用OpenGL创建自定义曲线形状,但当前代码仅能在指定控制点之间生成一条曲线。现有OpenGL代码如下:
#include <GL/glut.h> #include <stdlib.h> GLfloat controlPoints[18][3] = { {0.0, 8.0, 0.0}, { -1.5, 3.0, 0.0}, //2 {-5.5, 4.0, 0.0}, {-5.5, 4.0, 0.0}, {-2.5, 0.0, 0.0}, //4 {-6.0, -4.0, 0.0}, {-6.0, -4.0, 0.0}, {-1.5, -3.0, 0.0}, //6 {0.0, -8.0, 0.0}, {0.0, -8.0, 0.0}, {1.0, -3.0, 0.0}, //8 {6.0, -5.0, 0.0}, {6.0, -5.0, 0.0}, {3.0, 0.0, 0.0}, //10 {6.5, 4.5, 0.0}, {6.5, 4.5, 0.0}, {1.5, 3.0, 0.0}, //12 {0.0, 8.0, 0.0} }; void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); for (int i = 0; (i + 3) < 3; i += 3) { glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints[i][0]); } glEnable(GL_MAP1_VERTEX_3); } void display(void) { int i; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); glBegin(GL_LINE_STRIP); { for (int i = 0; i <= 18; i++) { glEvalCoord1f((GLfloat)i / 18.0); } } glEnd(); glBegin(GL_LINE_STRIP); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); glPointSize(6.0); glColor3f(0.0, 0.0, 1.0); glBegin(GL_POINTS); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) { glOrtho(-10.0, 10.0, -10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0); } else { glOrtho(-10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0, -10.0, 10.0); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); break; } } 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); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }
当前仅能生成一条曲线,需要基于这18个控制点生成6条曲线,询问如何修改init函数实现;若无法用GL_MAP1_VERTEX_3实现,是否可以用GL_LINE_STRIP完成?
解决方案
方法一:使用GL_MAP1_VERTEX_3生成多条贝塞尔曲线
你的控制点按每3个新点+1个衔接点的方式分组,每组4个控制点对应一条3次贝塞尔曲线,共6组。原init函数的循环条件错误(i + 3 < 3永远不成立),且glMap1f是设置当前的映射参数,不能一次性设置多组,需要在display中循环为每组控制点设置映射,再生成曲线。
修改后的init和display函数:
void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); glEnable(GL_MAP1_VERTEX_3); // 只需要启用一次 } void display(void) { int i, j; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); // 生成6条贝塞尔曲线 for(j = 0; j < 18; j += 3) // 每组3个新控制点,加上衔接点共4个 { // 设置当前组的控制点映射 glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints[j][0]); glBegin(GL_LINE_STRIP); { // 对每条曲线采样20个点,保证平滑度 for(i = 0; i <= 20; i++) { glEvalCoord1f((GLfloat)i / 20.0); } } glEnd(); } // 绘制控制点连线(原代码保留) glBegin(GL_LINE_STRIP); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); // 绘制控制点(原代码保留) glPointSize(6.0); glColor3f(0.0, 0.0, 1.0); glBegin(GL_POINTS); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); glFlush(); // 确保绘制命令执行 }
方法二:使用GL_LINE_STRIP绘制近似贝塞尔曲线
如果不想用OpenGL的 evaluator,可以手动实现贝塞尔曲线的点计算,然后用GL_LINE_STRIP连接采样点来模拟曲线。
首先添加一个计算贝塞尔曲线上点的函数:
void bezierPoint(GLfloat t, GLfloat* p, GLfloat* cp) { GLfloat u = 1 - t; GLfloat u2 = u * u; GLfloat u3 = u2 * u; GLfloat t2 = t * t; GLfloat t3 = t2 * t; // 3次贝塞尔曲线公式 p[0] = u3 * cp[0] + 3 * u2 * t * cp[3] + 3 * u * t2 * cp[6] + t3 * cp[9]; p[1] = u3 * cp[1] + 3 * u2 * t * cp[4] + 3 * u * t2 * cp[7] + t3 * cp[10]; p[2] = u3 * cp[2] + 3 * u2 * t * cp[5] + 3 * u * t2 * cp[8] + t3 * cp[11]; }
然后修改display函数,循环计算每组控制点的采样点并绘制:
void display(void) { int i, j; GLfloat p[3]; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); // 绘制6条近似贝塞尔曲线 for(j = 0; j < 18; j += 3) { glBegin(GL_LINE_STRIP); { for(i = 0; i <= 20; i++) { GLfloat t = (GLfloat)i / 20.0; bezierPoint(t, p, &controlPoints[j][0]); glVertex3fv(p); } } glEnd(); } // 绘制控制点连线和控制点(原代码保留) glBegin(GL_LINE_STRIP); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); glPointSize(6.0); glColor3f(0.0, 0.0, 1.0); glBegin(GL_POINTS); { for (i = 0; i < 18; i++) { glVertex3fv(&controlPoints[i][0]); } } glEnd(); glFlush(); }
说明
- 方法一利用OpenGL内置的 evaluator 生成标准贝塞尔曲线,代码更简洁;
- 方法二手动计算贝塞尔点,灵活性更高,不需要依赖OpenGL的 evaluator 特性。
内容的提问来源于stack exchange,提问作者sali
相关产品推荐
相关产品推荐

