计算机图形学 - 实验:Bezier Curve
本博客基于课程"计算机图形学",教材使用为计算机图形学(第4版) [Computer Graphics with OpenGL, Fourth Edition],部分代码模板便来自于此教材,并且有所改动
实验思路
代码思路
void binomialCoeffs()函数求二项式系数,传入点的数量n和记录数组;void computerBezPt()函数计算被贝塞尔曲线沿曲线路径的坐标位置;传入比例系数,点坐标,控制点数,控制点坐标,二项式系数。再函数中为了保险起见,先将点坐标赋值为0,再通过遍历每个控制点,在循环中每次即可算出受每个控制点影响后的点的x和y坐标void Draw_Bezier()函数中,通过调用binomialCoeffs()来根据传入的参数算出二项式系数,再通过调用computerBezPt()来算出每个放置像素点的坐标,在循环中算出坐标后,再放置像素,通过GL_LINE_STRIP绘制模式即可连成一条曲线- 用
highightCtrlPt()函数来生成较大尺寸的点,也就是小矩形,来表示控制点,只需要用GL_POLYGON绘制模式,对中心点的x和y坐标依次加减一定的长度即可。 - 在
display()函数中,第一个循环用于生成控制点的突出显示,通过调用highightCtrlPt()函数实现;用glLineStipple()函数,绘制起点、控制点和终点之间的虚线;第三个循环用于绘制贝塞尔曲线,通过调用Draw_Bezier()函数实现
问题及解决方案
- 在
highightCtrlPt ()中,使用GL_POLYGON绘制模式,绘制填充多边形时,放置像素点的顺序是顺时针或者逆时针,也就是按照放置点的顺序来构成多边形并进行填充 - 设置线型函数
glLineStipple()的原型是void glLineStipple( GLint factor, GLushort pattern );pattern:是由1或0组成的16位序列(0x3F07转二进制0011111100000111),从这个模式的低位开始,一个一个像素地进行处理,如果模型中对应的位是1,就绘制这个像素,否则不绘制。factor:为重复因子,它与1和0的连续子序列相乘,如果模式中出现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
#include
#include
#include
// 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;
}