基于opengl绘制五星红旗

基于opengl绘制五星红旗

趁着今天是国庆,就更新一下绘制五星红旗的博文吧~祖国万岁

flag

操作步骤

确定绘制方法

这个实验分为两步,第一步是画出旗面,第二则是根据五角星的位置,画出大小、角度不同的三角形;根据《国旗法》要求,国旗长宽比为3:2,这个画起来难度不大,我们将旗面四个点的位置坐标设置为(-0.9,0.6),(0.9, 0.6),(0.9,-0.6),(-0.9, -0.6)。接下来关键是画五角星。

我们来看一下,绘制图形所使用的函数:

1
WINGDIAPI void APIENTRY glVertex3f (GLfloat x, GLfloat y, GLfloat z);

由于该函数只能绘制凸多边形,因此我们选择的绘制方法是,将五角星分成多个扇形三角形,然后顺时针逐个绘制三角形,这样能方便我们循环完成,如图:

star1

计算相关参数

首先是得到各五角星中点的位置,首先是将国旗分为四个小长方形,大五角星的位置为左五右十,上五下五,其它各个中心点如图:

xy

绘制三角形

根据上面确定的绘制方法,我们需要将五角星分为十个扇形三角形,因此要绘制每个三角形,我们只需要三个参数——中心点,长半径的顶点,短半径的顶点,如图:

根据几何计算,这两个角大小分别为18°和36°

tan

因此要画出某一个的三角形,必须要得知中心点和其中的一个顶点。然后根据这两个点计算出长半径和短半径,如图,黑线代表长半径R,蓝线代表短半径r;

star2
  • 首先根据两点之间的距离公式计算出长半径R;
  • 由以上的角度可以计算出:
sincos

接下来,由于我们每个三角形已知的参数中心点和长半径顶点的坐标(也就是已知第一条长半径的旋转角度),因此我们需要据此计算出短半径的坐标位置:

sincos

而我们又可以得知两边的夹角为36°,因此只需要将将36°带入β中即可求出下一条边的旋转角,如下:

1
2
tempC = cos1*cos36 - sin1*sin36;
tempS = sin1*cos36 + cos1*sin36;

再根据旋转角和长度利用级坐标公式求出x和y的坐标值(其中midx和midy是五角星中点坐标)

nwdd

代码如下

1
2
3
4
5
6
7
8
if (i % 2 == 0) { //偶数下标是外顶点
point[i][0] = l_length*tempC + midx;
point[i][1] = l_length*tempS + midy;
}
else { //奇数下标是内顶点
point[i][0] = s_length*tempC + midx;
point[i][1] = s_length*tempS + midy;
}

绘制红旗和五角星

样例代码已经可以绘制出红旗,因此只需要将绘制五角星的过程封装成函数,并传入相关参数即可:

1
2
3
4
5
DrawStar(-0.60, 0.30, -0.60, 0.48); 
DrawStar(-0.30, 0.48, -0.24, 0.48);
DrawStar(-0.18, 0.36, -0.24, 0.36);
DrawStar(-0.18, 0.18, -0.18, 0.24);
DrawStar(-0.30, 0.06, -0.24, 0.06);

并且,在函数中确定绘制颜色为黄色,绘制形状为三角形;

1
2
glColor3f(1, 1, 0);
glBegin(GL_TRIANGLE_FAN);

实验数据记录和处理

五星红旗的四个坐标点分别为(-0.9,0.6),(0.9, 0.6),(0.9,-0.6),(-0.9, -0.6);

五角星中最大的一个坐标为(-0.60, 0.30),其余四个分别为(-0.30, 0.48),(-0.18, 0.36), (-0.18, 0.18), (-0.30, 0.06)

实验结果与分析

绘制结果

res

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "gl/glut.h"
#include<math.h>

const GLfloat PI = 3.1415926f;
const GLfloat cos36 = cos(PI*0.2);
const GLfloat sin36 = sin(PI*0.2);
const GLfloat cos72 = cos(PI*0.4);


void DrawStar(GLfloat midx, GLfloat midy, GLfloat vx, GLfloat vy)
{
GLfloat point[11][2]; //存下所有顶点的位置
GLfloat l_length = sqrt((midx - vx)*(midx - vx) + (midy - vy)*(midy - vy));//计算出顶点长半径的长度
GLfloat s_length = l_length * cos72 / cos36;//计算出顶点短半径的长度
GLfloat cos1 = (vx - midx) / l_length; //计算出第一个外接顶点与中心点的三角函数值
GLfloat sin1 = (vy - midy) / l_length;

GLfloat tempC, tempS;
point[0][0] = vx, point[0][1] = vy;

for (int i = 1; i < 11; i++)
{
tempC = cos1*cos36 - sin1*sin36;
tempS = sin1*cos36 + cos1*sin36;

if (i % 2 == 0) { //偶数下标是外顶点
point[i][0] = l_length*tempC + midx;
point[i][1] = l_length*tempS + midy;
}
else { //奇数下标是内顶点
point[i][0] = s_length*tempC + midx;
point[i][1] = s_length*tempS + midy;
}
cos1 = tempC;
sin1 = tempS;
}

glColor3f(1, 1, 0);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(midx, midy, 0.5);
for (int i = 0; i < 11; i++)
glVertex3f(point[i][0], point[i][1], 0.5); //绘制三角形
glEnd();
}

void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glBegin(GL_QUADS);

glVertex3f(-0.9, 0.6, 0.5);
glVertex3f(0.9, 0.6, 0.5);
glVertex3f(0.9, -0.6, 0.5);
glVertex3f(-0.9, -0.6, 0.5); //旗面长宽比为3:2
glEnd();

DrawStar(-0.60, 0.30, -0.60, 0.48);
DrawStar(-0.30, 0.48, -0.24, 0.48);
DrawStar(-0.18, 0.36, -0.24, 0.36);
DrawStar(-0.18, 0.18, -0.18, 0.24);
DrawStar(-0.30, 0.06, -0.24, 0.06);

glutSwapBuffers(); //交换缓冲区
}

int main (int argc, char *argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(10, 10);
glutInitWindowSize(400, 300);
glutCreateWindow("National Flag");

glutDisplayFunc(display);

glutMainLoop();

return 0;
}

心得

这是第一个图形学实验,让我好好回顾了一下三角函数的公式,总体花的时间不是特别多。其中犯的比较大的错误是在计算下一个顶点的旋转角的三角函数时,我没有替代原来的变量,而是在一次循环中直接在计算出正弦角后马上将新的正弦角带入计算余弦角,最后画出了一个不成形的五角星。好在最后找到了这个bug并成功修复。