使用OPENGL绘制一个带轨迹的小球
2016-07-13 16:50
453 查看
[-]
使用OPENGL绘制一个带轨迹的小球
大概的思路
代码及其注释
因为这是一篇教程,主要为了帮助OPENGL初学者了解一种绘制轨迹/拖影的方法,所以接下来就直接把代码贴上来,大家可以边看注释便敲代码。项目使用 freeglut工具库编写,win32 console应用程序。编译过程中如果出项函数未声明等问题,可以修改包含的头文件路径,因为很可能我们的OPENGL头文件路径不同!!
[cpp] view
plain copy
struct tracks{
int nums; // 节点数量
tracknode *head; // 头节点,每个新节点插入到头节点之后
};
每个节点保存对应拖影的位置,颜色,透明度,等信息,以及指向下个节点的指针。这是节点的数据结构
[cpp] view
plain copy
struct tracknode{
int listnums; //绘制物体的显示列表数量
GLuint *showlist; // 绘制物体的现实列表
float color; // 当前颜色值
point3f poi;
tracknode *next; // 指向下一个
};
ps:可能你会经常在各种库中见到 point3f ,point3F或者point32F之类名字的数据结构,这些都是用来保存三维坐标的数据结构。但是这里我们没有使用那些库(OPENGL里也没有名字叫做point3f的数据结构),因此我们需要自己定义三维点的数据结构,如下
[cpp] view
plain copy
struct point3f{
float val[3];
};
以上代码你会在接下的“长篇”中再次见到,事先说明好有个深刻印象。
需要的数据结构定义好之后我们就需要来实现拖影透明的效果了。没错,可以使用混合来实现透明的效果。
plain copy
// 显示小球的运动轨迹
//
//
//
#include "stdafx.h"
#include <GL\glew.h>
#include <GL\GLAUX.H>
#include <GL\freeglut.h>
#pragma comment(lib, "glew32.lib")
#define MAX_RANGE 2.0f // 左右移动范围
#define SPEED 0.08f // 移动速度
struct point3f{
float val[3];
};
struct tracknode{
int listnums; //绘制物体的显示列表数量
GLuint *showlist; // 绘制物体的现实列表
float color; // 颜色值
point3f poi;
tracknode *next; // 指向下一个
};
struct tracks{
int nums; // 节点数量
tracknode *head; // 头节点,每个新节点插入到头节点之后
};
int time = 0;
GLuint list; //小球的显示列表
tracks t; // major data!!!!!!!!!!!!
BOOL isleft = true; //小球默认向左移动
// 定义一些材质颜色
const static float red_material[4] = {1.0f, 1.0, 1.0f, 1.0f};
void setMaterial(const float diffuseMaterial[4],const float shininessMaterial);
// 将 src 节点的内容复制给 dst 节点(不包括 this->next)
void copytracknode(const tracknode *src, tracknode *dst);
void init();
void drawtrack();
void display();
void reshape(int, int);
void keyboard(unsigned char, int, int);
void releaseTracks(tracks *t); // 释放释放
int _tmain(int argc, _TCHAR* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowPosition(300, 200);
glutInitWindowSize(600, 400);
glutCreateWindow("轨迹");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
releaseTracks(&t);
return 0;
}
void setMaterial(const float diffuseMaterial[4],const float shininessMaterial)
{
const float specularMaterial[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 镜面光
const float emissionMaterial[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 发光颜色
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, diffuseMaterial);
glMaterialfv(GL_FRONT, GL_SPECULAR, specularMaterial);
glMaterialfv(GL_FRONT, GL_EMISSION, emissionMaterial);
glMaterialfv(GL_FRONT, GL_SHININESS, &shininessMaterial);
}
void copytracknode(const tracknode *src, tracknode *dst)
{
dst->listnums = src->listnums;
dst->showlist = new GLuint[dst->listnums];
memcpy_s(dst->showlist, sizeof(GLuint)*dst->listnums, src->showlist, sizeof(GLuint)*src->listnums);
dst->color= src->color;
memcpy_s(dst->poi.val, sizeof(float)*3, src->poi.val, sizeof(float)*3);
}
void init()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glShadeModel(GL_SMOOTH);
// 启用光照
float positionFarawayLight[] = {1.0, 1.0f, 3.0f, 0.0f};
float ambient[] = {0.2f, 0.2f, 0.2f, 1.0f}; // 弱弱的白色环境光
float diffuseLight[] = {1.0f, 1.0f, 1.0f, 1.0f}; // 散射光
float specularLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, positionFarawayLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// 创建小球的初始化列表
list = glGenLists(1);
glNewList(list, GL_COMPILE);
glutSolidSphere(0.2, 20, 20);
glEndList();
// 初始化 struct tracks
t.head = new tracknode;
t.nums = 1;
t.head->listnums = 1;t.head->color= 1.0f;
t.head->showlist = new GLuint[t.head->listnums];
memset(t.head->poi.val, 0, sizeof(float)*3);
t.head->poi.val[0] = -0.1f;
t.head->showlist[0] = list;
t.head->next = nullptr;
// 启用混合
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
void drawtrack()
{
tracknode *ite = nullptr;
ite = t.head;
while( ite )
{
for(int y=0;y < ite->listnums;++y)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glColor4f(0.0f, 0.0f, 0.0f, ite->color);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTranslatef(0.0f, 0.0f, -2.0f);
glTranslatef(ite->poi.val[0], ite->poi.val[1], ite->poi.val[2]);
glDepthMask(GL_FALSE); // 设置深度缓冲区只读
if( t.head == ite )
{
setMaterial(red_material, 30.0);
}else{
float white_material[4] = {ite->color, ite->color, ite->color, 0.5};
setMaterial(white_material, 30.0);
}
glCallList(ite->showlist[y]);
glPopMatrix();
glDepthMask(GL_TRUE); // 设置深度缓冲区可读写
}
ite = ite->next;
}
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// 开始绘制小球
drawtrack();
glFlush();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w >= h)
{
glOrtho(
-MAX_RANGE*((double)w/(double)h), MAX_RANGE*((double)w/(double)h),
-MAX_RANGE, MAX_RANGE,
0.1, 100.0f
);
}else{
glOrtho(
-MAX_RANGE, MAX_RANGE,
-MAX_RANGE*((double)h/(double)w), MAX_RANGE*((double)h/(double)w),
0.1, 100.0
);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
tracknode *pre = t.head;
tracknode *current = t.head->next;
tracknode *newnode1;
/*tracknode *newnode2;
tracknode *newnode3;*/
switch(key)
{
case 'm':
case 'M':
// 为了让小球动起来,需要调整小球的位置,并将当前位置加入 struct track
// 第一步加入新的节点
// 每个节点采用头插法插入头节点之后的位置
newnode1 = new tracknode;
copytracknode(t.head, newnode1);
newnode1->color= t.head->color - 0.1f;
newnode1->next = t.head->next;
t.head->next = newnode1;
++t.nums;
// 删除 color为0的节点
// 不出意外被删除的点都位于最后的位置
while(current)
{
if( current )
{
current->color-= 0.03f;
}
if( 0.1f >= current->color)
{
delete current;
pre->next = nullptr;
--t.nums;
break;
}
pre = pre->next;
current = current->next;
}
// 第二部改变头节点的位置
// 为了方便显示与计算让小球的x坐标在 -MAX_RANGE~+MAX_RANGE范围内不断变化
if( isleft )
{
t.head->poi.val[0] -= (float)SPEED;
if( t.head->poi.val[0] <= -(float)MAX_RANGE )
{
isleft = !isleft;
}
}else{
t.head->poi.val[0] += (float)SPEED;
if( t.head->poi.val[0] >= (float)MAX_RANGE )
{
isleft = !isleft;
}
}
break;
default:
break;
}
glutPostRedisplay();
}
void releaseTracks(tracks *t)
{
tracknode *current = t->head;
tracknode *next = current->next;
// 删除显示列表(所有节点共用所有显示列表,所以只删除一次)
for(int x=0;x < current->listnums;++x)
{
glDeleteLists(current->showlist[x], 1);
}
while( current )
{
delete current;
current = next;
next = next->next;
}
}
以上就是全部绘制代码,在当前代码条件下我们可以根据修改小球移动速度(SPEED)和拖影衰减控制变量(tracknode.color)来达到不同效果。但是细心的话会发现我们还无法绘制出像彗星尾巴那样更加”浓郁“的尾迹(当然要达到这种效果有其他更好的方法,比如使用贴图并总是改变其方向时期朝向观察者或者使用粒子来绘制),为了绘制更加“浓郁”的尾迹可以在相应按键消息的时候一次增加多个位置具有相对微小变化的拖影。可能有些人喜欢在响应绘制函数中做上面的事情,但是出于可控性和效率的考虑不推荐这样做,更好的做法是设置定时器,然后在定时器中进行以上操作,或者干脆另起一个线程来负责以上的任务。
使用OPENGL绘制一个带轨迹的小球
大概的思路
代码及其注释
使用OPENGL绘制一个带轨迹的小球
程序绘制一颗白色小球,通过按下 M/m 键,小球会不断的在窗口中左右移动,并显示出漂亮的尾迹。因为这是一篇教程,主要为了帮助OPENGL初学者了解一种绘制轨迹/拖影的方法,所以接下来就直接把代码贴上来,大家可以边看注释便敲代码。项目使用 freeglut工具库编写,win32 console应用程序。编译过程中如果出项函数未声明等问题,可以修改包含的头文件路径,因为很可能我们的OPENGL头文件路径不同!!
大概的思路
先来介绍一下大概思路:小球的投影可以简单看作一连串的透明小球组成的,随着小球的移动,队列中靠近末尾的小球会变得更加透明知道消失。为了保存所有拖影及其数据我们使用单链表 tracks,这是链表的数据结构:[cpp] view
plain copy
struct tracks{
int nums; // 节点数量
tracknode *head; // 头节点,每个新节点插入到头节点之后
};
每个节点保存对应拖影的位置,颜色,透明度,等信息,以及指向下个节点的指针。这是节点的数据结构
[cpp] view
plain copy
struct tracknode{
int listnums; //绘制物体的显示列表数量
GLuint *showlist; // 绘制物体的现实列表
float color; // 当前颜色值
point3f poi;
tracknode *next; // 指向下一个
};
ps:可能你会经常在各种库中见到 point3f ,point3F或者point32F之类名字的数据结构,这些都是用来保存三维坐标的数据结构。但是这里我们没有使用那些库(OPENGL里也没有名字叫做point3f的数据结构),因此我们需要自己定义三维点的数据结构,如下
[cpp] view
plain copy
struct point3f{
float val[3];
};
以上代码你会在接下的“长篇”中再次见到,事先说明好有个深刻印象。
需要的数据结构定义好之后我们就需要来实现拖影透明的效果了。没错,可以使用混合来实现透明的效果。
代码及其注释
[cpp] viewplain copy
// 显示小球的运动轨迹
//
//
//
#include "stdafx.h"
#include <GL\glew.h>
#include <GL\GLAUX.H>
#include <GL\freeglut.h>
#pragma comment(lib, "glew32.lib")
#define MAX_RANGE 2.0f // 左右移动范围
#define SPEED 0.08f // 移动速度
struct point3f{
float val[3];
};
struct tracknode{
int listnums; //绘制物体的显示列表数量
GLuint *showlist; // 绘制物体的现实列表
float color; // 颜色值
point3f poi;
tracknode *next; // 指向下一个
};
struct tracks{
int nums; // 节点数量
tracknode *head; // 头节点,每个新节点插入到头节点之后
};
int time = 0;
GLuint list; //小球的显示列表
tracks t; // major data!!!!!!!!!!!!
BOOL isleft = true; //小球默认向左移动
// 定义一些材质颜色
const static float red_material[4] = {1.0f, 1.0, 1.0f, 1.0f};
void setMaterial(const float diffuseMaterial[4],const float shininessMaterial);
// 将 src 节点的内容复制给 dst 节点(不包括 this->next)
void copytracknode(const tracknode *src, tracknode *dst);
void init();
void drawtrack();
void display();
void reshape(int, int);
void keyboard(unsigned char, int, int);
void releaseTracks(tracks *t); // 释放释放
int _tmain(int argc, _TCHAR* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutInitWindowPosition(300, 200);
glutInitWindowSize(600, 400);
glutCreateWindow("轨迹");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
releaseTracks(&t);
return 0;
}
void setMaterial(const float diffuseMaterial[4],const float shininessMaterial)
{
const float specularMaterial[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 镜面光
const float emissionMaterial[4] = {0.0f, 0.0f, 0.0f, 1.0f}; // 发光颜色
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, diffuseMaterial);
glMaterialfv(GL_FRONT, GL_SPECULAR, specularMaterial);
glMaterialfv(GL_FRONT, GL_EMISSION, emissionMaterial);
glMaterialfv(GL_FRONT, GL_SHININESS, &shininessMaterial);
}
void copytracknode(const tracknode *src, tracknode *dst)
{
dst->listnums = src->listnums;
dst->showlist = new GLuint[dst->listnums];
memcpy_s(dst->showlist, sizeof(GLuint)*dst->listnums, src->showlist, sizeof(GLuint)*src->listnums);
dst->color= src->color;
memcpy_s(dst->poi.val, sizeof(float)*3, src->poi.val, sizeof(float)*3);
}
void init()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glShadeModel(GL_SMOOTH);
// 启用光照
float positionFarawayLight[] = {1.0, 1.0f, 3.0f, 0.0f};
float ambient[] = {0.2f, 0.2f, 0.2f, 1.0f}; // 弱弱的白色环境光
float diffuseLight[] = {1.0f, 1.0f, 1.0f, 1.0f}; // 散射光
float specularLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, positionFarawayLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
// 创建小球的初始化列表
list = glGenLists(1);
glNewList(list, GL_COMPILE);
glutSolidSphere(0.2, 20, 20);
glEndList();
// 初始化 struct tracks
t.head = new tracknode;
t.nums = 1;
t.head->listnums = 1;t.head->color= 1.0f;
t.head->showlist = new GLuint[t.head->listnums];
memset(t.head->poi.val, 0, sizeof(float)*3);
t.head->poi.val[0] = -0.1f;
t.head->showlist[0] = list;
t.head->next = nullptr;
// 启用混合
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
void drawtrack()
{
tracknode *ite = nullptr;
ite = t.head;
while( ite )
{
for(int y=0;y < ite->listnums;++y)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glColor4f(0.0f, 0.0f, 0.0f, ite->color);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTranslatef(0.0f, 0.0f, -2.0f);
glTranslatef(ite->poi.val[0], ite->poi.val[1], ite->poi.val[2]);
glDepthMask(GL_FALSE); // 设置深度缓冲区只读
if( t.head == ite )
{
setMaterial(red_material, 30.0);
}else{
float white_material[4] = {ite->color, ite->color, ite->color, 0.5};
setMaterial(white_material, 30.0);
}
glCallList(ite->showlist[y]);
glPopMatrix();
glDepthMask(GL_TRUE); // 设置深度缓冲区可读写
}
ite = ite->next;
}
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// 开始绘制小球
drawtrack();
glFlush();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w >= h)
{
glOrtho(
-MAX_RANGE*((double)w/(double)h), MAX_RANGE*((double)w/(double)h),
-MAX_RANGE, MAX_RANGE,
0.1, 100.0f
);
}else{
glOrtho(
-MAX_RANGE, MAX_RANGE,
-MAX_RANGE*((double)h/(double)w), MAX_RANGE*((double)h/(double)w),
0.1, 100.0
);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
tracknode *pre = t.head;
tracknode *current = t.head->next;
tracknode *newnode1;
/*tracknode *newnode2;
tracknode *newnode3;*/
switch(key)
{
case 'm':
case 'M':
// 为了让小球动起来,需要调整小球的位置,并将当前位置加入 struct track
// 第一步加入新的节点
// 每个节点采用头插法插入头节点之后的位置
newnode1 = new tracknode;
copytracknode(t.head, newnode1);
newnode1->color= t.head->color - 0.1f;
newnode1->next = t.head->next;
t.head->next = newnode1;
++t.nums;
// 删除 color为0的节点
// 不出意外被删除的点都位于最后的位置
while(current)
{
if( current )
{
current->color-= 0.03f;
}
if( 0.1f >= current->color)
{
delete current;
pre->next = nullptr;
--t.nums;
break;
}
pre = pre->next;
current = current->next;
}
// 第二部改变头节点的位置
// 为了方便显示与计算让小球的x坐标在 -MAX_RANGE~+MAX_RANGE范围内不断变化
if( isleft )
{
t.head->poi.val[0] -= (float)SPEED;
if( t.head->poi.val[0] <= -(float)MAX_RANGE )
{
isleft = !isleft;
}
}else{
t.head->poi.val[0] += (float)SPEED;
if( t.head->poi.val[0] >= (float)MAX_RANGE )
{
isleft = !isleft;
}
}
break;
default:
break;
}
glutPostRedisplay();
}
void releaseTracks(tracks *t)
{
tracknode *current = t->head;
tracknode *next = current->next;
// 删除显示列表(所有节点共用所有显示列表,所以只删除一次)
for(int x=0;x < current->listnums;++x)
{
glDeleteLists(current->showlist[x], 1);
}
while( current )
{
delete current;
current = next;
next = next->next;
}
}
以上就是全部绘制代码,在当前代码条件下我们可以根据修改小球移动速度(SPEED)和拖影衰减控制变量(tracknode.color)来达到不同效果。但是细心的话会发现我们还无法绘制出像彗星尾巴那样更加”浓郁“的尾迹(当然要达到这种效果有其他更好的方法,比如使用贴图并总是改变其方向时期朝向观察者或者使用粒子来绘制),为了绘制更加“浓郁”的尾迹可以在相应按键消息的时候一次增加多个位置具有相对微小变化的拖影。可能有些人喜欢在响应绘制函数中做上面的事情,但是出于可控性和效率的考虑不推荐这样做,更好的做法是设置定时器,然后在定时器中进行以上操作,或者干脆另起一个线程来负责以上的任务。
相关文章推荐
- 【终端快捷键】Linux terminal 终端常用快捷键
- PopUpWindow使用详解(一)——基本使用
- drop、delete和truncate三者的区别
- Suse linux 11 SP3安装VMWare Tools时问题的解决
- Linux基础(4)之文本处理
- 刚开始添加Open CV的时候遇到问题
- Openjudge-NOI题库-和为给定数
- linux系统编程手册 定时器和休眠
- Hadoop的发行版本介绍
- org.apache.struts.action.InvalidCancelException异常解决方法
- Linux grep使用详解
- Jcrop使用方法
- nginx 编译安装
- Linux 常用命令
- 大型网站架构学习
- Shell脚本中执行sql语句操作mysql的5种方法
- GitBash+GitLab+Eclipse使用攻略非常详细版
- Handler、Looper、Message源码解析
- OpenCV学习笔记(三):Canny边缘检测和滚动条制作
- 一天一条Linux指令-ln