OpenGL绘制平面五角星并为中心着色
北京航空航天大学计算机学院 2020春季计算机图形学课程第一次作业,使用OpenGL绘制平面的五角星,并为中心着色,目标结果如下图:
基本熟悉OpenGL绘图流程之后,我们按照要求绘制相应的五角星。思路是:首先我们找到五角星的五个顶点,实际上就是正五边形的顶点,然后将正五边形顺序的顶点映射到五角星顺序的顶点,通过OpenGL函数中指定绘制顶点相互连接最终成环即可绘制出基本的五角星。观察要求的图示,还需要知晓五角星中间所围成小五边形的顶点坐标,利用绘制三角扇来为中心着色。最后是五角星的五个顶点是圆形的点,通过多边形逼近圆形来绘制五个顶点。
其中五角星的角度是可以旋转的,不加旋转的五角星并没有水平的边,若想要五角星呈现我们平时所见到的“正”的姿态,需要在直接绘制的正五边形顶点基础上逆时针转18度。
在计算五角星中心的正五边形顶点时,需要用到分别已知两直线上的两点,求两直线交点,该公式在博主的另一篇博客里有详细解析。
代码和其注释如下:
#include<GL/glut.h>
#include<math.h>
#define PI 3.1415926
#define SIZE 600
double pentagram_vertex[5][2];
double pentagon_vertex[5][2];
void get_pentagram()
{
// 五角星看作在圆上内接的五边形的顶点连成的,此处设置外接圆半径[0, 1]
double r = (SIZE * 0.5);
// 五角星可以在外接圆上旋转,此处设置旋转角度[0, 360)
double rotate = 18;
// 依次通过外接圆计算正五边形的五个顶点横纵坐标
double tmp[5][2];
for (int i = 0; i < 5; i++) {
tmp[i][0] = r * cos(((72 * (double)i + rotate) / 360) * (2 * PI));
tmp[i][1] = r * sin(((72 * (double)i + rotate) / 360) * (2 * PI));
}
// 将正五边形顺序的五个顶点对应到五角星顺序的五个顶点(0, 1, 2, 3, 4) -> (0, 2, 4, 1, 3)
for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
int k = j % 5;
pentagram_vertex[i][0] = tmp[k][0];
pentagram_vertex[i][1] = tmp[k][1];
}
// 五角星的边相交得到中间的小五边形顶点,计算以便绘图时的着色
double x[4], y[4];
for (int i = 0; i < 5; i++) {
int v[4];
for (int j = 0; j < 4; j++)
v[j] = (i + j) % 5;
for (int j = 0; j < 4; j++)
x[j] = pentagram_vertex[v[j]][0], y[j] = pentagram_vertex[v[j]][1];
tmp[i][0] = ((x[2] - x[3]) * (x[1] * y[0] - x[0] * y[1])
- (x[0] - x[1]) * (x[3] * y[2] - x[2] * y[3])) /
((x[2] - x[3]) * (y[0] - y[1]) - (x[0] - x[1]) * (y[2] - y[3]));
tmp[i][1] = ((y[2] - y[3]) * (y[1] * x[0] - y[0] * x[1])
- (y[0] - y[1]) * (y[3] * x[2] - y[2] * x[3])) /
((y[2] - y[3]) * (x[0] - x[1]) - (y[0] - y[1]) * (x[2] - x[3]));
}
for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
int k = j % 5;
pentagon_vertex[i][0] = tmp[k][0];
pentagon_vertex[i][1] = tmp[k][1];
}
}
void initial()
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(-(SIZE * 0.6), (SIZE * 0.6), -(SIZE * 0.6), (SIZE * 0.6));
get_pentagram();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
// 下面的段落绘制五角星中心所围成小正五边形的着色
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_TRIANGLE_FAN);
for (int i = 0; i < 5; i++)
glVertex2d(pentagon_vertex[i][0], pentagon_vertex[i][1]);
glEnd();
// 下面的段落绘制五角星的线
glColor3f(0.0, 0.0, 0.0);
// 启用抗锯齿(使线平滑)
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 设置线的宽度(0, 10]
glLineWidth(5);
glBegin(GL_LINE_LOOP);
for (int i = 0; i < 5; i++)
glVertex2d(pentagram_vertex[i][0], pentagram_vertex[i][1]);
glEnd();
// 下面的段落绘制五角星的五个顶点(实心圆)
glColor3f(0.0, 0.0, 0.0);
// 设置实心圆的半径
double radius = 8;
// 设置用来拟合圆形的多边形边个数
int sections = 200;
for (int i = 0; i < 5; i++) {
glBegin(GL_TRIANGLE_FAN);
glVertex2d(pentagram_vertex[i][0], pentagram_vertex[i][1]);
for (int j = 0; j <= sections; j++)
glVertex2d(pentagram_vertex[i][0] + radius * cos(j * 2 * PI / sections),
pentagram_vertex[i][1] + radius * sin(j * 2 * PI / sections));
glEnd();
}
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(SIZE, SIZE);
glutCreateWindow("Pentagram");
glutDisplayFunc(&display);
initial();
glutMainLoop();
return 0;
}
绘制结果与要求的图示大致相同了: