开头先提一下本例子用的opengl库是比较老的glut(听说在90年代就停止更新了),可能对一些用glew的朋友不是太友好。不过我们老师大概也是觉得重点在于学习原理和绘制方法,就没有在意太多细节吧。
今天在做粒子系统实验的时候,想起来前阵子在AE里做过星空拖尾的效果。于是感慨这次实验的内容就做星轨了。

然后自己在opengl里试着做了做,又丑又卡(。


画到这里就已经花了很长时间(不过是完整分辨率)。而且有的粒子太大了;有的地方有锯齿。而且现在就这么慢,后期的效果就别想了(我开始佩服起ae的stardust插件了)。
总体思路和stardust插件是一样的。由于本次只是一次实验性编码,目的只在于实现星轨和简单后期效果(不考虑UI,不考虑交互和输入输出事件),而且很多地方会比stardust本身拙劣得多(而且我也没有看过stardust插件的源码)。
明显这是一个具有很大提升空间的实验例子。先说一下实现这个例子的基本思路:
其实和在AE里面用stardust插件做的步骤差不多。
1、先画出来一个基本的粒子系统
2、把基本粒子系统的每一个粒子当做成父粒子,自成一个子粒子系统
3、通过对子粒子系统的位置更新和大小更新实现拖尾效果。
4、子粒子装在一个环形队列里进行管理。
5、子粒子的位置通过记录父粒子每帧走过的位置来实现;子粒子的大小通过它在队列里的位置关系来实现。
6、在4,5两点看的懵很正常,我觉得自己讲的也不是很清楚。
(这里建议读者已经可以开始看代码了,改进后得到的最终的代码我放在最后,没改进的我就不放了)

我将列出以下几个可以改进的地方:
1、改进各个参数(宏和全局变量)的设置,使整体更好看
2、优化绘制方式,不绘制累赘的粒子
3、改变纹理贴合的方式。opengl里绘制三角形比正方形快(具体原因不明)
4、尽可能消除锯齿
5、消除轨迹上的一些断续现象
6、一些粒子大又丑

针对第3点:

改为绘制在三角形上。

一样是指明4个顶点与材质绑定。Opengl会用三角形来帮我们画(其实笔者以前用这种三角形拼合的方法画其他正方形的时候出现了不少奇怪的问题,比如材质不显示、三角形颠倒等,但是本例子里使用的是中心对称的贴图,所以无伤大雅)。

我们会明显感觉绘制速度快了不少。

通过不断修改参数,针对1、2、6做了一些优化(大概就是把粒子远离摄像机,使更多的粒子进入了视野,然后再减少父粒子生成数目)。上面一些大丑的情况就是某些粒子离摄像机太近造成的。剩下的就是修改粒子数、子粒子数、粒子尺寸、粒子生命之类的东西了。

针对第4点:由于星轨是有一颗一颗的子粒子有序排列生成的,而且每颗子粒子使用的是我自己贴上去的材质,所以不采用glHint的方式。网上找了集中抗锯齿的方法都是针对glew与glfw库的,这里就不去实现了。
这里需要注意一点,材质没有处理好的时候有很大的几率导致锯齿,比如这种情况:

仔细看会发现,发光和图片边缘撞上了(当然我们采用的是png带有透明通道的图片,记得在代码中打开混合)。

经过一点修改,我们得到了如下效果:

锯齿依然是挺糟心的,整体也不是特别好看;但是有一点值得一提,那就是快了很多。

接下来的一个改进办法是可选的。如果读者仅仅只是为了视觉效果,便可以尝试。我们发现中间的粒子轨迹很短,而边缘的粒子轨迹很长。但他们所包含的子粒子数目是一样的。也就是说,离中心比较近的父粒子,我们没有绘制那么多子粒子的必要。我们可以根据父粒子距离中心点的距离(我们在这里比较平方和,不涉及开方运算。因为开方运算带来的损失很大),动态改变他的子粒子数目。

需要一提的是,我们并不是直接改变子粒子的数目。这样你只会发现你的轨迹变得更短了,而并没有减少你的粒子重叠程度。原因就在于角速度和粒子数目的关系。
所以在绘制每一帧的时候,我们可以对子粒子数组作插值,只绘制其中的一部分,减少重叠程度。

这种做法看上去不是那么稳健,若只用于视觉效果,则值得一试。
我们只需要这样:

在对子系统进行装入的时候就可以这么处理了。值得一提的是,我们不仅仅可以指定偶数,还能指定其他数(通过取模运算自由设定)。我们还需要注意限制距离的大小,如果太大的话,你会发现边缘的轨道出现了一些断续现象,并不是那么好看。
我们还需要在绘制的地方补上一句:

这样就不会绘制我们所不需要的粒子了。
最后的效果。你可能看不出什么差别,但我们少绘制了不少东西。

以上便是所有内容。其实还有很多改进的地方,因为实在是太、太、太丑了。
可以加上一些后期处理或者一些发光的效果,就像AE里面做的一样。但这也是学习opengl的shader之后的事儿了。而且到那时,我将抛弃glut,采用glew和glfw来实现一些效果。
本博客一气呵成写的,没有检查错字。若有,还请各位海涵。
最后上代码,挺乱的其实,不过思路都在上面提到了。各位大佬有改进建议之类的请提出来,在下感激不尽!

// lesson1test.cpp: 定义控制台应用程序的入口点。
// stardust#include "stdafx.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include "GL/SOIL.h"#define MAX_PARTICLES 800         // Number of particles to create
#define MAX_PARTICLESONS 500
#define MAX_FILTER 4
#define PI              3.14159f
#define RHO             50
#define LIFE            60.0f
#define TIME            0.01fGLfloat windowHeight, windowWidth;
GLuint texture[MAX_FILTER];
GLint range_x = 2000;
GLint range_y = 1000;
GLint range_z = 1000;
GLfloat t_speed = 0.002f;
//GLfloat rtt = 0;
GLfloat rtt_speed = 0.001f;
GLfloat z_t = 0;
GLfloat radius = 3.5f;
GLfloat limited_distance = 1000.0f;
GLfloat scale;typedef struct
{GLfloat    life;GLfloat   init_life;GLfloat    speed_aging;GLfloat r;GLfloat   g;GLfloat   b;GLfloat   x;GLfloat   y;GLfloat   z;GLfloat distance;GLfloat rtt_angle;GLfloat alpha;GLint filter;typedef struct{ //子粒子系统GLfloat x;GLfloat y;GLfloat z;GLfloat    r;GLfloat   g;GLfloat   b;GLfloat radius;}particleSon;particleSon particleSons[MAX_PARTICLESONS];   //环形队列记录子粒子信息int psons_head = 0;       //队首int psons_last = 0;        //队尾}particle;
particle particles[MAX_PARTICLES];//FUNCTION
int loadTextures();//当窗口改变大小时由GLUT函数库调用
void ChangeSize(int w, int h)
{glViewport(0, 0, w, h);glMatrixMode(GL_PROJECTION);     // Select The Projection MatrixglLoadIdentity();                // Reset The Projection Matrix// Calculate The Aspect Ratio And Set The Clipping Volumeif (h == 0) h = 1;gluPerspective(80, (float)w / (float)h, 1.0, 5000.0);glMatrixMode(GL_MODELVIEW);      // Select The Modelview MatrixglLoadIdentity();                // Reset The Modelview Matrix
}int InitPaticleSystem(void)
{for (int i = 0; i < MAX_PARTICLES;i++){particles[i].init_life = LIFE + rand() % 10 / 10.0;particles[i].life = particles[i].init_life;particles[i].speed_aging = TIME;particles[i].r = (float)(rand() % 35 + 150) / 255.0f;particles[i].g = (float)(rand() % 55 + 200) / 255.0f;particles[i].b = 1.0f;particles[i].x = rand() % range_x - range_x / 2;particles[i].y = rand() % range_y - range_y / 2;particles[i].z = rand() % range_z - range_z / 2;particles[i].distance = sqrt(particles[i].x*particles[i].x + particles[i].y*particles[i].y);particles[i].rtt_angle = atan(particles[i].x / particles[i].y)*180.0f / PI;particles[i].filter = rand() % MAX_FILTER;//初始化子系统for (int j = 0;j < MAX_PARTICLESONS;j++){particles[i].particleSons[j].x = 0;particles[i].particleSons[j].y = 0;particles[i].particleSons[j].z = 0;particles[i].particleSons[j].r = particles[i].r;particles[i].particleSons[j].g = particles[i].g;particles[i].particleSons[j].b = particles[i].b;particles[i].particleSons[j].radius = radius;}}return true;
}void RenderScene(void)
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);          // Clear screen and depth bufferglLoadIdentity();glTranslated(0, 0, -1000);for (int i = 0;i < MAX_PARTICLES; i++)                            // All particles{GLfloat x = particles[i].x;                              // Position of particleGLfloat y = particles[i].y;GLfloat z = particles[i].z;//glColor3f(particles[i].r, particles[i].g, particles[i].b);int j = particles[i].psons_head%MAX_PARTICLESONS;while (true){if (j == particles[i].psons_last)break;if (particles[i].particleSons[j].radius < radius && particles[i].particleSons[j].radius!=0)  //只绘制半径比父粒子小且尺寸不为0的子粒子{glBindTexture(GL_TEXTURE_2D, texture[particles[i].filter]);glBegin(GL_TRIANGLE_STRIP);glTexCoord2f(0.0f, 0.0f);glVertex3f(particles[i].particleSons[j].x - particles[i].particleSons[j].radius, particles[i].particleSons[j].y - particles[i].particleSons[j].radius, particles[i].particleSons[j].z);glTexCoord2f(1.0f, 0.0f);glVertex3f(particles[i].particleSons[j].x + particles[i].particleSons[j].radius, particles[i].particleSons[j].y - particles[i].particleSons[j].radius, particles[i].particleSons[j].z);glTexCoord2f(1.0f, 1.0f);glVertex3f(particles[i].particleSons[j].x + particles[i].particleSons[j].radius, particles[i].particleSons[j].y + particles[i].particleSons[j].radius, particles[i].particleSons[j].z);glTexCoord2f(0.0f, 1.0f);glVertex3f(particles[i].particleSons[j].x - particles[i].particleSons[j].radius, particles[i].particleSons[j].y + particles[i].particleSons[j].radius, particles[i].particleSons[j].z);glEnd();}j = (++j) % MAX_PARTICLESONS;}}glutSwapBuffers();
}void Update() {for (int i = 0;i < MAX_PARTICLES; i++)                    // All The Particles{particles[i].x = particles[i].distance * cos(particles[i].rtt_angle);particles[i].y = particles[i].distance * sin(particles[i].rtt_angle);particles[i].life -= particles[i].speed_aging;        // reduce particles life//更新子系统particles[i].particleSons[particles[i].psons_last].x = particles[i].x;particles[i].particleSons[particles[i].psons_last].y = particles[i].y;particles[i].particleSons[particles[i].psons_last].z = particles[i].z;particles[i].psons_last = (++particles[i].psons_last) % MAX_PARTICLESONS;/*printf("draw last++ %d %d %d %d %d", particles[i].psons_head, particles[i].psons_last,particles[i].particleSons[particles[i].psons_last].x,particles[i].particleSons[particles[i].psons_last].y,particles[i].particleSons[particles[i].psons_last].z);*/if (particles[i].psons_last - particles[i].psons_head == -1)    //若队列满了{particles[i].psons_head = (++particles[i].psons_head) % MAX_PARTICLESONS;printf("drink");}else{    //队列没有满//这里计算的是粒子逐渐衰减的半径。0.98是衰减倍数particles[i].particleSons[particles[i].psons_last].radius -=particles[i].particleSons[particles[i].psons_last].radius*pow(0.98f, (particles[i].psons_last - particles[i].psons_head + MAX_PARTICLESONS) % MAX_PARTICLESONS);//作出优化。如果父粒子距离小于限定距离,则把一部分子粒子半径归零(我们把数组中双数粒子半径归0)GLfloat tempx = particles[i].particleSons[particles[i].psons_last].x;GLfloat tempy = particles[i].particleSons[particles[i].psons_last].y;if (tempx*tempx + tempy * tempy < limited_distance*limited_distance)if (particles[i].psons_last % 2 == 0){particles[i].particleSons[particles[i].psons_last].radius = 0;}printf("psons_last is %d   ", particles[i].psons_last);printf(" radius is %f", particles[i].particleSons[particles[i].psons_last].radius);  //调试用}printf("         %d %d %d\n", particles[i].psons_head, particles[i].psons_last, (particles[i].psons_last - particles[i].psons_head + MAX_PARTICLESONS) % MAX_PARTICLESONS);}
}void TimerFunction(int value) {Update();for (int i = 0;i<MAX_PARTICLES;i++)particles[i].rtt_angle += rtt_speed;glutPostRedisplay();glutTimerFunc(10, TimerFunction, 1);
}// Setup the rendering state
int InitGL(GLvoid)
{if (!loadTextures()){return false;}glShadeModel(GL_SMOOTH);glClearColor(0.0f, 0.0f, 0.0f, 0.0f);glClearDepth(1.0f);glDisable(GL_DEPTH_TEST);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);glEnable(GL_TEXTURE_2D);}int main(int argc, char **argv) {// init GLUT and create windowglutInit(&argc, argv);glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA|GLUT_MULTISAMPLE);glutInitWindowPosition(100, 100);glutInitWindowSize(1280, 720);glutCreateWindow("Particle-Stardust");// register callbacksglutDisplayFunc(RenderScene);glutReshapeFunc(ChangeSize);glutTimerFunc(10, TimerFunction, 1);// Setup the rendering stateInitGL();InitPaticleSystem();// enter GLUT event processing loopglutMainLoop();return 0;
}
int loadTextures()
{texture[0] = SOIL_load_OGL_texture("assets/images/star_0.png",SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_INVERT_Y);texture[1] = SOIL_load_OGL_texture("assets/images/star_1.png",SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_INVERT_Y);texture[2] = SOIL_load_OGL_texture("assets/images/star_2.png",SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_INVERT_Y);texture[3] = SOIL_load_OGL_texture("assets/images/star_3.png",SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_INVERT_Y);printf("tex OK\n");if (texture[0] == 0)return false;glBindTexture(GL_TEXTURE_2D, texture[0]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);return true;
}

opengl/c++ 用粒子系统实现星轨效果相关推荐

  1. 行星轨迹制作_好看的星球轨迹是怎么制作的 PS制作星轨效果详细教程

    大家知道怎么制作好看的星轨效果吗?其实制作的方法很简单,如果你会使用 首先你将原图照片导入到PS中,然后在图层中拷贝一个图层出来(快捷键CTRL+J).并将图层的类型调整为[变亮]. 接着你将该图层使 ...

  2. Photoshop给星空添加星轨效果

    方法如下: 效果:

  3. 22岁何同学引爆B站!硬核毕设树莓派星轨拍摄仪,上演理工男的终极浪漫

    今天的热搜属于毕业的何同学!因无数科技测评视频爆红的何同学,已经3个月没更了!而这次他带来了迟到作品--我毕业了.视频中,他的毕设,化为满天繁星散落在母校上空,上演了理工男的极致浪漫! >> ...

  4. python随机函数random、画、星轨_教你绘制梵高的星空

    我是极影AX摄影师Joyous周游 引言intro 有人觉得后期很麻烦,很复杂!请大家记住我传授的后期内功心法:"他山之石可以攻玉,能用工具用工具!"这一期教大家如何两个插件配合使 ...

  5. 多张照片合成星轨 matlab实现

    基本思想 一般合成星轨需要使用ps做图像堆叠,虽然简单,但是需要依托于ps软件本身,而且再简单也需要记下操作步骤.最最最重要的是,这不符合一个工科生的浪漫. 简单研究了一下原理,发现实际上星轨合成原理 ...

  6. 22 岁何同学引爆 B 站!硬核毕设树莓派星轨拍摄仪,上演理工男的终极浪漫

    这是「进击的Coder」的第 168 篇热点新闻 来源:新智元 " 阅读本文大概需要 6 分钟. " [导读]B 站这两天热搜属于毕业的何同学!因无数科技测评视频爆红的何同学,已经 ...

  7. 超美的天环星轨动态引导页html官网源码下载

    锦鲤已测|免费|超美的天环引导页html官网源码下载好看!亲测了一下,挺好看的,星轨转起来的感觉挺棒的,作为个人主页,谁点开了不得按个F12扒一下呢 下载地址:https://pan.baidu.co ...

  8. python随机函数random、画、星轨_如何使用 NVIDIA StyleGAN 生成自己的动漫(老婆)头像...

    大家应该前段时间都被StyleGan生成动漫(老婆)头像的新闻刷屏了,但是基本上玩的起来的都是比较核心的程序猿,本文的主旨意思是希望每一个热爱动漫,喜欢研究人工智能的朋友都可以享受调教stylegan ...

  9. 利用python实现星轨合成

    有摄影经验的朋友应该都对星轨摄影有所了解,一般可以用长曝光直接出片或者通过拍摄多张照片后利用后期软件实现星轨合成.这里我们主要就讨论多张照片合成的方法,目前比较流行的是PS的堆栈法和使用StarTra ...

最新文章

  1. linux 查看剪贴板历史,Clipboard History Manager插件,查看浏览器剪贴板历史记录
  2. “No operations defined in spec!”一文教你swagger如何扫描多个controller
  3. C语言、嵌入式位操作精华技巧大汇总
  4. 永不丢失照片:防弹照片备份的完整指南
  5. 里rust怎么找蓝图_Rust错误处理
  6. html伪元素before占用高度,CSS:伪元素:before和:after从原始元素继承宽度/高度...
  7. javascript String 对象
  8. 死锁的处理策略——检测和解除
  9. 压力测试中需要掌握的几个基本概念
  10. CMU 15-213 Introduction to Computer Systems学习笔记(23) Thread-Level Parallelism
  11. 大数据学习入门规划?
  12. SpringBoot自动解压Gzip请求
  13. sap se06和scc4
  14. Matlab-16QAM调制与解调 16-QAM星座点图 16-QAM在AWGN信道下的误码率和误比特率性能,仿真值与理论值曲线对比图
  15. 前端安装项目报错1.gyp info it worked if it ends with ok
  16. AE/PR插件AI智能背景抠像颜色键控GoodbyeGreenscreenzxb V1.6.0官方版
  17. 团队作业10——事后诸葛亮分析
  18. 【Python随笔】python进程池ProcessPoolExecutor的用法与实现分析
  19. 在ubuntu上编写第一个C程序Hello world!
  20. Windows Vista Sp1 x86/x64 中文正式零售版下载

热门文章

  1. 九龙证券|沪指震荡跌0.32%,金融、地产等板块走弱,信创概念逆势活跃
  2. 动态规划经典题目——0-1背包
  3. 2020年,急需提及的十大最受欢迎的编程语言
  4. 1 partitions have leader brokers without a matching listener, including [baidd-0] (org.apache.kafka.
  5. 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法
  6. GIS开发:WGS84、GCJ-02、BD-09的区别
  7. 深度学习中常见的图像处理任务
  8. 编译Skia:结合云端Github Actions与本地Visual Studio
  9. python常见笔试题整理
  10. python的celery的面试_Python面试经验总结,面试一时爽,一直面试一直爽!