一、设计思路
二、源码
#include <GL/glew.h>//OpenGL扩展库
#include <GL/glut.h>//OpenGL工具库
#include <stdio.h>
#include <math.h>
// 定义Π
#define PI 3.14159265
// 定义漂移五角星的属性
float alpha = 0.0f; // 旋转角度
float D = 0.f; // 平移距离
float tx = 0.0f, ty = 0.0f; // 平移的坐标增量
float beita = 2*PI/10; // 平移轨迹与x轴夹角β,初始为36度
float drift_x = 210.f, drift_y = 370.f;// 圆心坐标
float drift_R = 20; // 外接圆半径R
// 定义缩放五角星的属性
float zoom_x = 310, zoom_y = 210;//圆心坐标
float zoom_R = 60;//外接圆半径
float zoom_tx = 1.f, zoom_ty = 1.f, zoom_tz = 1.f;//放缩参数
// 定义坐标结构体
struct Vertex {
double x;
double y;
};
// ####绘制五角星方法####
// 绘制黄色三角形函数
void drawYellowTriangle(Vertex Vertex_Fir, Vertex Vertex_Sec, Vertex Vertex_Thi) {
// 设置填充的颜色
glColor3f(255, 255, 0);
// 设置绘制类型
glBegin(GL_TRIANGLES);
// 第一个坐标
glVertex2f(Vertex_Fir.x, Vertex_Fir.y);
// 第二个坐标
glVertex2f(Vertex_Sec.x, Vertex_Sec.y);
// 第三个坐标
glVertex2f(Vertex_Thi.x, Vertex_Thi.y);
glEnd();
}
// 获取外圆五角星的五顶点(参数:大圆圆心坐标以及半径)
Vertex* getExternalVertex(Vertex Center_Vertex, double R) {
// 定义Vertex结构体数组,包括五个顶点和一个圆心
Vertex* externalVertex = new Vertex[6];
double alpha = 2 * PI / 5;
double thet = PI / 10;
double bet = 2 * PI / 10;
// 圆心坐标
externalVertex[0].x = Center_Vertex.x, externalVertex[0].y = Center_Vertex.y;
// 其余五个顶点
externalVertex[1].x = Center_Vertex.x, externalVertex[1].y = Center_Vertex.y + R;
externalVertex[2].x = Center_Vertex.x + R * cos(thet), externalVertex[2].y = Center_Vertex.y + R * sin(thet);
externalVertex[3].x = Center_Vertex.x + R * sin(bet), externalVertex[3].y = Center_Vertex.y - R * cos(bet);
externalVertex[4].x = Center_Vertex.x - R * sin(bet), externalVertex[4].y = Center_Vertex.y - R * cos(bet);
externalVertex[5].x = Center_Vertex.x - R * cos(thet), externalVertex[5].y = Center_Vertex.y + R * sin(thet);
return externalVertex;
}
// 获取内圆五角星的五顶点(参数:小圆圆心坐标以及半径)
Vertex* getInternalVertex(Vertex Center_Vertex, double r) {
// 定义Vertex结构体数组,包括五个顶点和一个圆心
Vertex* internalVertex = new Vertex[6];
double alpha = 2 * PI / 5;
double thet = PI / 10;
double bet = (PI - alpha) / 2;
// 圆心坐标
internalVertex[0].x = Center_Vertex.x, internalVertex[0].y = Center_Vertex.y;
// 其余五个顶点
internalVertex[1].x = Center_Vertex.x + r * cos(bet), internalVertex[1].y = Center_Vertex.y + r * sin(bet);
internalVertex[2].x = Center_Vertex.x + r * cos(thet), internalVertex[2].y = Center_Vertex.y - r * sin(thet);//
internalVertex[3].x = Center_Vertex.x, internalVertex[3].y = Center_Vertex.y - r;//
internalVertex[4].x = Center_Vertex.x - r * cos(thet), internalVertex[4].y = Center_Vertex.y - r * sin(thet);
internalVertex[5].x = Center_Vertex.x - r * cos(bet), internalVertex[5].y = Center_Vertex.y + r * sin(bet);
return internalVertex;
}
// ####绘制红旗方法####
// 绘制红色矩阵函数
void drawRedRect(Vertex* vertex) {
// 设置绘制类型
glShadeModel(GL_FLAT);
glBegin(GL_POLYGON);
// 设置填充的颜色,默认以第一个点的颜色进行纯色填充
glColor3f(1, 0, 0);
// 第一个坐标
glVertex2f(vertex[0].x, vertex[0].y);
// 第二个坐标
glVertex2f(vertex[1].x, vertex[1].y);
// 第三个坐标
glVertex2f(vertex[2].x, vertex[2].y);
// 第四个坐标
glVertex2f(vertex[3].x, vertex[3].y);
glEnd();
}
// 设置背景颜色
void setBackgroundColor(void) {
glClearColor(0.2, 0.3, 0.3, 0.5);
}
// 绘制国旗
void draw(void) {
// 清除清缓存
glClear(GL_COLOR_BUFFER_BIT);
// ### 一、绘制红色矩形 ###
Vertex* rectVertex=new Vertex[4];
// 上边界y=410;下边界y=10;左边界x=10,有边界x=610
rectVertex[0].x = 10, rectVertex[0].y = 410;//左上角(高400)
rectVertex[1].x = 10, rectVertex[1].y = 10;//左下角
rectVertex[2].x = 610, rectVertex[2].y = 10;//右下角(宽600)
rectVertex[3].x = 610, rectVertex[3].y = 410;//右上角
drawRedRect(rectVertex);
// ### 二、绘制五角星 ###
// # <1>缩放五角星 #
// 1. 定义圆心、大小圆半径
Vertex Center_Vertex;
Center_Vertex.x = zoom_x, Center_Vertex.y = zoom_y;//圆心以显示窗口的左下角为原点,向右x轴正方向,向上y轴正方向
double R = zoom_R;
double r = 23;
// 2. 获取内外顶点
Vertex* externalVertex = getExternalVertex(Center_Vertex, R);
Vertex* internalVertex = getInternalVertex(Center_Vertex, r);
// 3.几何变换(持续缩放)
//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();//几何变换的边界起点
glTranslatef(310, 210, 0);//移动回初始位置
glScalef(zoom_tx, zoom_ty, zoom_tz);
//glScalef(1, 1, zoom_tz);
glTranslatef(-310, -210, 0);//移动回原点
// 3. 绘制黄色五角星
for (int i = 1; i < 6; i++) {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i]);
if (i < 5) {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i + 1]);
}
else {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[1]);
}
}
glPopMatrix();//几何变换的边界终点(只对边界内的图形进行几何变换)
// # <2>漂移五角星 #
// 1. 定义圆心、大小圆半径
Center_Vertex.x = drift_x, Center_Vertex.y = drift_y;//圆心以显示窗口的左下角为原点,向右x轴正方向,向上y轴正方向
R = drift_R;
r = 7.6;
// 2. 获取内外顶点
externalVertex = getExternalVertex(Center_Vertex, R);
internalVertex = getInternalVertex(Center_Vertex, r);
// 3.几何变换(旋转一定角度)
//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();//几何变换的边界起点
glTranslatef(tx, ty, 0);//平移
glTranslatef(210, 370, 0);//移动回初始位置
glRotatef(alpha, 0, 0, 1);//绕z轴旋转,即xoy平面以原点为中心逆时针旋转
glTranslatef(-210, -370, 0);//移动回原点
// 4. 绘制黄色五角星
for (int i = 1; i < 6; i++) {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i]);
if (i < 5) {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[i + 1]);
}
else {
drawYellowTriangle(Center_Vertex, internalVertex[i], externalVertex[1]);
}
}
glPopMatrix();//几何变换的边界终点(只对边界内的图形进行几何变换)
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}
// 这里有个问题,tx和ty都是累加计算的结果,那么对于tx和ty的修改是对β角度以及累计方式的修改,对于β角度的修改已经没有问题,现在需要思考如何对累计方式进行修改
// 对于累计方式的修改有如下思路:由于累计方式只有两种累加和累减,所以我们只需要定义一个变量来控制是进行累加还是累减
// 累计方式变量,为1时累加,为-1时累减
float flag_tx = 1.f;// tx的累计变量,为1时tx为累加算法,为-1时tx为累减算法
float flag_ty = 1.f;// ty的累计变量
// 放缩五角星累计方式改变变量
float zoom_flag = 1;
void collisionDetection() {
// 检查是否与缩放五角星发生碰撞(dist<(zoom_R+drift_R)即发生碰撞)
float dist = sqrt(pow((zoom_x - (drift_x + tx)), 2) + pow(zoom_y - (drift_y + ty), 2));//(利用勾股定理计算两个圆心坐标的直线距离)
// 碰撞检测......
if (dist < (zoom_R*(zoom_tx) + drift_R)) {// 使用变化后的zoom_R,即zoom_R*(1+ zoom_tx)
// 发生碰撞,则不可进行原计划位移,将已加坐标增量减去
tx -= flag_tx * D*cos(beita);
ty -= flag_ty * D*sin(beita);
// 更新碰撞后的运动方向
// 五角星方向(沿反方向)
flag_tx = -1;
flag_ty = -1;
// 放缩方向(进行缩小)
zoom_flag = -1;
zoom_tx += zoom_flag * 0.001;
zoom_ty += zoom_flag * 0.001;
}
// 检查是否与边界发生碰撞(如果五角星之间发生碰撞,那么五角星就不可能与边界发生碰撞)
// 在tx,ty的基础上添加外接圆半径R的距离
float txx = tx + flag_tx * (drift_R)*cos(beita);
float tyy = ty + flag_ty * (drift_R)*sin(beita);
if (((drift_x + txx) < 10) || ((drift_x + txx) > 610) || ((drift_y + tyy) < 10) || ((drift_y + tyy) > 410)) {
// 发生碰撞,则不可进行原计划位移,将已加坐标增量减去
tx -= flag_tx * D*cos(beita);
ty -= flag_ty * D*sin(beita);
// 更新碰撞后的运动方向
if (beita < PI / 2) {
beita = PI - beita;
if (drift_x + txx > 610 || drift_y + tyy < 10) {
flag_tx = 1;
flag_ty = 1;
}
else if (drift_x + txx < 10 || drift_y + tyy>410) {
flag_tx = -1;
flag_ty = -1;
}
}
else if (beita > PI / 2) {
beita = PI - beita;
if (drift_x + txx > 610 || drift_y + tyy > 410) {
flag_tx = -1;
flag_ty = -1;
}
else if (drift_x + txx < 10 || drift_y + tyy < 10) {
flag_tx = +1;
flag_ty = +1;
}
}
else if (beita == 0) {
beita = beita;
if (drift_x + txx > 610) {
flag_tx = -1;
flag_ty = +1;
}
else if (drift_x + txx < 10) {
flag_tx = +1;
flag_ty = +1;
}
}
else if (beita == PI / 2) {
beita = beita;
if (drift_y + tyy > 410) {
flag_tx = +1;
flag_ty = -1;
}
else if (drift_y + tyy < 10) {
flag_tx = +1;
flag_ty = +1;
}
}
// 更换为碰撞后的新坐标增量
tx += flag_tx * D*cos(beita);
ty += flag_ty * D*sin(beita);
}
}
void change()
{
// #### 漂移五角星 ####
// #### 使旋转角度持续发生变化 ####
alpha += 0.3; // 每次旋转两度
if (alpha > 360) alpha -= 360; //当旋转达到360度后减去360度,变为0度
// #### 使位移的tx、ty持续发生变化 ####
//根据每次旋转的角度0.2度和外接圆半径drift_R计算平移距离
D = 0.2 / 360 * 2 * PI*drift_R;
//根据平移距离D、轨迹与x轴夹角β计算坐标的tx,ty增量
tx += flag_tx*D*cos(beita);
ty += flag_ty*D*sin(beita);
// #### 放缩五角星 ####
// 放缩比例大于2倍时开始缩小
if (zoom_tx > 1) {
zoom_flag = -1;
}
// 小于0.1倍时,开始放大
else if(zoom_tx < 0.1) {
zoom_flag = 1;
}
zoom_tx += zoom_flag * 0.001;
zoom_ty += zoom_flag * 0.001;
// 碰撞检测函数
collisionDetection();
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);//设置图形数据范围
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main() {
// 窗口的坐标是电脑屏幕左上角为原点,向右为x轴正方向;向下为y轴正方向
glutInitWindowPosition(10, 10); //定义窗口位置与大小
glutInitWindowSize(1200, 700);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); //初始化显示模式
glutCreateWindow("五角星乱动"); //定义窗口名称
setBackgroundColor(); // 设置背景色
glutDisplayFunc(draw); //图形绘制函数
glutReshapeFunc(reshape); //窗口重绘函数
glutKeyboardFunc(keyboard);//键盘交互
glutIdleFunc(change);//键盘交互(实现无限循环的关键)
glutMainLoop(); //无限循环函数
return 0;
}