计算机图形学 - 实验:Bezier Curve

本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,并且有所改动

实验思路

代码思路

  1. void binomialCoeffs()函数求二项式系数,传入点的数量n和记录数组;
  2. void computerBezPt()函数计算被贝塞尔曲线沿曲线路径的坐标位置;传入比例系数,点坐标,控制点数,控制点坐标,二项式系数。再函数中为了保险起见,先将点坐标赋值为0,再通过遍历每个控制点,在循环中每次即可算出受每个控制点影响后的点的xy坐标
  3. void Draw_Bezier()函数中,通过调用binomialCoeffs()来根据传入的参数算出二项式系数,再通过调用computerBezPt()来算出每个放置像素点的坐标,在循环中算出坐标后,再放置像素,通过GL_LINE_STRIP绘制模式即可连成一条曲线
  4. highightCtrlPt()函数来生成较大尺寸的点,也就是小矩形,来表示控制点,只需要用GL_POLYGON绘制模式,对中心点的xy坐标依次加减一定的长度即可。
  5. display()函数中,第一个循环用于生成控制点的突出显示,通过调用highightCtrlPt()函数实现;用glLineStipple()函数,绘制起点、控制点和终点之间的虚线;第三个循环用于绘制贝塞尔曲线,通过调用Draw_Bezier()函数实现

问题及解决方案

  1. highightCtrlPt ()中,使用GL_POLYGON绘制模式,绘制填充多边形时,放置像素点的顺序是顺时针或者逆时针,也就是按照放置点的顺序来构成多边形并进行填充
  2. 设置线型函数glLineStipple()的原型是void glLineStipple( GLint factor, GLushort pattern );pattern:是由10组成的16位序列(0x3F07 转二进制0011111100000111),从这个模式的低位开始,一个一个像素地进行处理,如果模型中对应的位是1,就绘制这个像素,否则不绘制。factor:为重复因子,它与10的连续子序列相乘,如果模式中出现3个1,并且factor是2,那么他就扩展为6个连续的1

实现代码

核心代码及关键步骤注释

void binomialCoeffs(int n, int* c) {
	int k, j;
	for (k = 0; k <= n; k++) {
		c[k] = 1;
		for (j = n; j >= k + 1; j--)
			c[k] *= j;
		for (j = n - k; j >= 2; j--)
			c[k] /= j;
	}
}
void computerBezPt(float u, CPoint2D* bezPt, int nctrl, CPoint2D* ctrlPts, int* c) {
	int k, n = nctrl - 1;
	float bezBlendFcn;
	bezPt->x = bezPt->y = 0.0;
	for (k = 0; k < nctrl; k++) {
		bezBlendFcn = c[k] * pow(u, k) * pow(1 - u, n - k);
		bezPt->x += ctrlPts[k].x * bezBlendFcn;
		bezPt->y += ctrlPts[k].y * bezBlendFcn;
	}
}
void highightCtrlPt(int x, int y) {
	glBegin(GL_POLYGON);
	glVertex2f(x + 5, y);
	glVertex2f(x, y + 5);
	glVertex2f(x - 5, y);
	glVertex2f(x, y - 5);
	glEnd();
}

全部代码

// ====== Computer Graphics Experiment #7 ======
// |              Bezier curve                 |
// =============================================
//
// Requirement:
// (1) Implement algorithm to draw Bezier curve.
// (2)Implement interactive method to specify
//     Bezier curve control points by mouse clicking.
// (3) Implement display callback function

#include <GL/glut.h>
#include <math.h>
#include <stdio.h>
#include <windows.h>

// 2D point class
class CPoint2D {
public:
	float x, y;
};

int n_cp; // Number of control points
int ic_cp; // Control point counter
int i_cp; // Index of control point being moved
CPoint2D* control_points; // Coordinates of control points

// Program window size
int pw_width, pw_height;

int running_state;
// 0 --- Setting up control points.
// 1 --- Normal state.
// 2 --- Moving control point.
void binomialCoeffs(int n, int* c) {
	int k, j;
	for (k = 0; k <= n; k++) {
		c[k] = 1;
		for (j = n; j >= k + 1; j--)
			c[k] *= j;
		for (j = n - k; j >= 2; j--)
			c[k] /= j;
	}
}
void computerBezPt(float u, CPoint2D* bezPt, int nctrl, CPoint2D* ctrlPts, int* c) {
	int k, n = nctrl - 1;
	float bezBlendFcn;
	bezPt->x = bezPt->y = 0.0;
	for (k = 0; k < nctrl; k++) {
		bezBlendFcn = c[k] * pow(u, k) * pow(1 - u, n - k);
		bezPt->x += ctrlPts[k].x * bezBlendFcn;
		bezPt->y += ctrlPts[k].y * bezBlendFcn;
	}
}
// Draw Bezier curve
void Draw_Bezier(int nctrl, CPoint2D* cp, int m)
// nctrl  --- Number of control points
// cp --- Array of control points
// m  --- Number of subdivision
{
	// Write your code here
	int i;
	float u;
	CPoint2D bezier;
	int c[nctrl];
	binomialCoeffs(nctrl - 1, c);
	glBegin(GL_LINE_STRIP);
	glVertex2f(cp[0].x, cp[0].y);
	for (i = 0; i < m; i++) {
		u = (float)i / (float)m;
		computerBezPt(u, &bezier, nctrl, cp, c);
		glVertex2f(bezier.x, bezier.y);
	}
	glVertex2f(cp[nctrl - 1].x, cp[nctrl - 1].y);
	glEnd();
}
void highightCtrlPt(int x, int y) {
	glBegin(GL_POLYGON);
	glVertex2f(x + 5, y);
	glVertex2f(x, y + 5);
	glVertex2f(x - 5, y);
	glVertex2f(x, y - 5);
	glEnd();
}
// Display callback function
void display(void) {
	glClear(GL_COLOR_BUFFER_BIT);

	// Draw control points
	// Write your code here
	for (int i = 0; i < ic_cp; i++) {
		highightCtrlPt(control_points[i].x, control_points[i].y);
	}
	// Draw control graph
	// Write your code here
	glLineStipple(1, 0x0f0f);
	glBegin(GL_LINE_STRIP);
	for (int i = 0; i < ic_cp; i++) {
		glVertex2f(control_points[i].x, control_points[i].y);
	}
	glEnd();
	// Draw Bezier curve
	// Write your code here
	glLineStipple(1, 0xffff);
	if (ic_cp == n_cp)
		Draw_Bezier(n_cp, control_points, 1000);
	glutSwapBuffers();
}

// Mouse callback function
void mouse_func(int button, int state, int x, int y) {
	float dis;

	if (button == GLUT_LEFT_BUTTON) {
		if ((state == GLUT_DOWN) & (running_state == 0) & (ic_cp < n_cp)) {
			control_points[ic_cp].x = x;
			control_points[ic_cp].y = pw_height - y;
			ic_cp++;
			if (ic_cp == n_cp)
				running_state = 1;
			glutPostRedisplay();
		} else if ((state == GLUT_DOWN) & (running_state == 1)) {
			for (int i = 0; i < n_cp; i++) {
				dis = abs((x - control_points[i].x) * (x - control_points[i].x) + (pw_height - y - control_points[i].y) * (pw_height - y - control_points[i].y));
				if (dis < 10) {
					i_cp = i;
					running_state = 2;
					break;
				}
			}
		} else if ((state == GLUT_DOWN) & (running_state == 2)) {
			control_points[i_cp].x = x;
			control_points[i_cp].y = pw_height - y;
			glutPostRedisplay();
		} else if ((state == GLUT_UP) & (running_state == 2)) {
			running_state = 1;
		}
	}
}
// Mouse motion callback function
void motion_func(int x, int y) {

	if (running_state == 2) {
		control_points[i_cp].x = x;
		control_points[i_cp].y = pw_height - y;
		glutPostRedisplay();
	}
}
// Initialization function
void init(void) {
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glEnable(GL_LINE_STIPPLE);
}

// Reshape callback function
void reshape(int w, int h) {
	pw_width = w;
	pw_height = h;

	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, w, 0.0, h);
}

// Keyboard callback function
void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case 27:
		exit(0);
	}
}

// Main program entrance
int main(int argc, char* argv[]) {
	// Input number of control points
	printf("Number of control points = ");
	scanf("%d", &n_cp);
	if (n_cp < 2)
		return 1;
	control_points = new CPoint2D[n_cp];
	running_state = 0;
	ic_cp = 0;

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
	glutInitWindowSize(800, 800);
	glutCreateWindow("Test Bижzier Curve");
	init();
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse_func);
	glutMotionFunc(motion_func);
	glutDisplayFunc(display);
	glutMainLoop();

	delete[] control_points;
	return 0;
}

posted @ 2022-01-10 15:13  ChisocDust  阅读(325)  评论(0)    收藏  举报