DX C++实现超炫酷粒子特效之烟花特效

首先,给各位说明的是,本人网站已恢复。www.jackchen.world和www.jackchen.work均可访问,已经过工信部和公安双层备案,所以请各位放心访问。

我记得前段时间写过一篇关于一个宇宙粒子效果的文章,使用JS编写,不过考虑那次效果比较杂,所以各位可以参考下。从这篇文章开始,针对特效我打算做一系列文章,目前想到的有烟花特效(本篇所讲),火焰特效,自然天气特效,水波特效,影子特效、运动模糊拖尾效果。。。。,本人暂时能想到这些,如果有其他需要实现的,可以前往本人的知乎或CSDN评论区评论(也不一定是特效相关,其他技术也可以)。微信公众号的留言板本人还没有迁移至本人的服务器上,而且本人最近这段时间一堆事,所以近期微信公众号留言功能不会加上。

本程序由以下几个部分组成:片头询问拦截,弹幕花雨模块,形状烟花,普通烟花。本程序不提供源码,只提供可执行文件和配置文件(见最后),方便各位预览和配置效果。该程序是本人很久以前所编写,至于背后的故事嘛。。。(略过,哈哈),之所以将这个拿出来说明,是因为这个程序中的烟花粒子特效技术还是值得和各位分享的。本篇文章只对该程序的烟花进行讲解。先来一张效果图,不然都是空谈,哈哈。
在这里插入图片描述

粒子,不知道各位同僚是如何理解的,可能搞过游戏开发的人,对这块更懂一些。先说一下我对这块的感受,最开始接触粒子的时候,一头雾水。不知这玩意如何和程序结合在一起,总觉得玄乎的很。先给各位给出部分电影或游戏片段场景图。见下:
在这里插入图片描述

分别源自:电影《惊奇队长》身体周围火焰发光效果,游戏《绝地求生》河流水波起伏效果,游戏《英雄联盟》视野区域效果,游戏《奥日越黑暗森林》跳跃拖尾效果。

我想大多数从未接触过粒子特效开发的人来说,总会觉得游戏中的炫酷特效交给游戏引擎来做,视频中的粒子特效交给各种视频软件来做。但是我们忽略了一点的就是,无论游戏引擎或者视频软件都是程序,都是由我们程序员开发的。所以粒子特效本身也是由程序员编码实现,就是麻烦一点,没有那么玄乎。这次带领各位深度剖析一下粒子特效,哈哈。

说到特效,更多开发者可能会想到和Shader关联。的确,游戏引擎和3D图像库一般都会提供自己的一种Shader脚本语言,大同小异。本人觉得Sharder的主要作用就是如何能让开发者能快速的写出高效的粒子特效。前期本人的写的关于粒子特效的文章和Shader无关,纯数据结构实现,也就是说可以基于任何图像库实现。粒子特效是属于图形编程的,因为是涉及的操作像素和纹理的。不管你使用什么语言最终是需要将效果通过渲染到屏幕上进行显示的。这里提供C/C++语言的图像操作库(EasyX,GDI,GDI+,Direct3D,DirectDraw,OpenGL)。需要至少会一种图像操作库的简单使用,仅仅是用于渲染。

烟花粒子,我这里基于Direct3D图形库实现了两种2D烟花。一种形状烟花,一种普通散开的烟花。不知道各位还有印象没有,在win7/xp的蜘蛛纸牌胜利后会有一个烟花界面(2D),还有前段时间情人节前夕吃鸡游戏里面会有一个撒狗粮的的烟花场景(3D),哈哈。其实,不管2D还是3D烟花,基本形式是一样的,就是在运算的时候多了一维,增加了运算复杂度。

烟花特效,我这里是分为发射簇,和爆炸簇。然后发射簇和爆炸簇分别由很多单个烟花粒子组成。烟花特效单元组成就是单个粒子而已。分别给出发射簇和爆炸簇粒子截图:
在这里插入图片描述

形状烟花主要流程,发射簇->形状->爆炸簇;普通烟花主要流程,发射簇->爆炸簇。在说明各流程之前,我先给出该程序中的单个粒子数据结构和簇数据结构:

///单个粒子结构
struct FireworksPartical
{
    bool bExist;                //是否存在
    int nLifeTime;              //生命周期
    int nRunTime;               //运行时间
    DWORD dwCurTime;            //当前时间
    DWORD dwColor;              //颜色
    int nTextureId;             //纹理id

    D3DXVECTOR3 vPos;           //位置
    D3DXVECTOR3 vBeg;           //起点
    D3DXVECTOR3 vEnd;           //终点
    D3DXVECTOR3 vAccelSpeed;    //加速度

    int nMode;                  //方式(0速度计算,1终点插值计算, 2速度和插值混合计算)
    bool bTag;                  //标记

    //...
};

///簇结构(包含发射簇、爆炸簇)
struct FireworksClusterPartical
{
    int nExplosionNum;                         //爆炸粒子数量
    FireworksPartical* ptExplosionParticle;    //爆炸粒子结构
    int nExplosionCount;                       //粒子计数器
    int nExplosionRunTimeHalfCount;            //运行时间过半粒子计数器

    int nEmissionNum;                          //发射粒子数量
    FireworksPartical* ptEmissionParticle;     //发射粒子结构

    D3DXVECTOR3 vCurPos;                       //当前位置
    int nRiseHeight;                           //上升高度
    D3DXVECTOR3 vRiseSpeed;                    //上升速度
    DWORD dwCurTime;                           //当前时间
    DWORD dwFlickerTime;                       //闪烁时间
    DWORD dwFlickerColor;                      //闪烁颜色

    int nType;                                 //类型(烟花类型)
    int nStatus;                               //状态(-1初始状态|0上升状态|1爆炸状态|2结束)
    bool bTag;                                 //标记
    bool bFlicker;                             //闪烁
    int nTextureType;                          //纹理类型
    
    //...
};

在一般的粒子结构中,都会有这么几个共有的属性(生命周期,生成时间,生成位置,位置,运动速度【矢量,含方向】等),上面结构由于是几年前编写的,所以抽象的不是很好,仅供大家参考。以下是该程序中用到粒子属性详细说明:

  • 生命周期:多数情况下粒子都具备该属性,从粒子创建的那一刻起,就需要开始计时,当累计时间超过生命周期的时候,销毁即可。但形状(粒子逐渐变成爱心或字母形状)过程例外,是不需要生命周期的,这时会指定结束位置,是通过插值进行运算的;
  • 生成时间:在粒子创建的那一刻起,记录当时的时间戳;
  • 生成位置:在粒子创建的那一刻起,记录当时的粒子位置(依赖发射簇和爆炸簇中心位置)
  • 位置:这个是实时位置,需要每帧更新,根据速度方向/轨迹插值方程计算获得;
  • 速度:x、y、z三个方向可控随机生成,正负表示运动方向;
  • 终点位置:粒子不一定有该结构,仅在形状(粒子逐渐变成爱心或字母形状)过程使用;
  • 颜色:可以在粒子创建的时候随机指定,也可以在粒子运动过程中随机指定。粒子的颜色,为了让烟花更加绚丽,还有是为了让粒子实现淡出效果;
  • 纹理:粒子创建的那一刻起,随机指定。烟花中的粒子纹理,本人这里提供了11种纹理(百合、彼岸、蝴蝶兰、蓝色妖姬、满天星、玫瑰、蔷薇、勿忘我、郁金香、栀子、紫罗兰);
  • 位置计算方式:该程序中有三种情况,根据速度计算位置,插值计算位置以及速度插值混合计算位置;

下面在对簇(发射簇/爆炸簇)属性详细说明:

  • 发射簇粒子数量:初始化烟花效果的时候就需要指定,并创建烟花粒子;
  • 爆炸簇粒子数量:初始化烟花效果的时候就需要指定,并创建烟花粒子;
  • 位置:发射簇和爆炸簇的中心位置,起始是基于屏幕最下方随机生成x坐标,粒子的生成位置就是该位置,换句话说簇的中心位置就是粒子发射源;
  • 发射簇位移:初始化烟花效果时指定,基于屏幕最下方位置进行计算
  • 发射簇速度:初始化烟花效果时可控随机
  • 烟花状态:发射状态,爆炸状态(上述说的形状也是在这个环节)
  • 烟花类型:普通烟花,具体形状烟花(不同的形状,轨迹方程是不一样的)

发射簇,由几百个烟花粒子组成,这些粒子一直在循环使用。当有粒子消亡的时候,这时重置该粒子属性(生命周期,速度,生成位置等)。只有当整个发射簇需要消亡的时候(具体时时机是当发射簇运动完发射簇位移时),这些粒子才会被真正销毁。

1、形状烟花,发射簇销毁的那一刻。这时会将发射簇的中心位置,作为即将要执行形状过程的粒子的生成位置,并初始化粒子的其他属性(速度,终点位置等)。待形状过程完成后在进入爆炸状态,进入爆炸状态的粒子的起始位置就是形状过程最后的粒子位置,然后在初始化粒子的(生命周期,速度等);

2、普通烟花,发射簇销毁的那一刻。这时会将发射簇的中心位置,作为即将要执行爆炸状态的粒子的生成位置,并初始化粒子的其他属性(生命周期,速度等)

在整个烟花销毁前夕,需要计算透明度,实现淡出过程。淡出实现在本人之前一篇文章说明过,这里不再说明了。为了更好帮助理解,下面给出简单示意图(凑合看吧):
在这里插入图片描述

到这里,烟花整个过程基本讲完了(本人语言组织能力有限,只能到这里了,哎)。下面讲一下轨迹方程,本人在最开始实现部分烟花字母之后,打算搞一个烟花字体出来,无奈太庞大了,麻烦的要命,所以只实现了26个字母,0-9的数字以及三种爱心符号。各取每种类型的其中一种的轨迹方程进行说明,这里为了让字体大小统一,规定的x,y的范围均为 [-1, 1],心形除外,但是之后会乘以一个系数统一大小。

爱心轨迹方程(注意定义域):
在这里插入图片描述

字母轨迹方程(A):
在这里插入图片描述

数字轨迹方程(0):
在这里插入图片描述

需要强调的是,任何字母和数字,以及后面要扩展的汉字都是可以由各种直线,曲线组成,只要控制好定义域就可以实现了。如果各位同僚有魄力想实现一套字体,这里给各位一种稍微高效点的方式,实现五笔的偏旁部首,然后组合即可。比单个实现文字的工作量要小太多。

需要提醒各位的是,在计算粒子位置的时候要注意坐标系变换。像Direct3D的话是左手坐标系,OpenGL是右手坐标系,至于其他2D坐标是以屏幕左上角为原点,向下是y轴正方向,向右是x轴正方向。

最后,谈一下本人的感受吧。本片文章中用的游戏或影视素材基本都是国外研发的,但这些大作在国内受到广大玩家和影迷的追捧。每每和身边的人聊到影视和游戏时,谈及国内时评价不高(尽管这是实时)。有时会想,难道国内做不出来吗?我觉得国内如果想做还是做得出来的,如果说是因为缺乏相关的技术人才,我只能说我们国内有掌握那些高级技术的人,只不过这种人少,但并不是说没有。我个人觉得国内缺的是一个有资金并且有魄力的组织。只有这些组织才有能力把拥有相关技术的人才笼络到一块,并且有魄力投入研发。所以在这个有资金并且有魄力的组织出现之前,身为开发者的我们,我们先掌握好相关的技术,我相信不久的将来国内拍摄研发出我们常说的影视和游戏大作必定有我们的参与。愿与各位同僚共勉之!加油!

链接:https://pan.baidu.com/s/1WgvLPrTYPK_wZdm4vh10BA
提取码:jc8i

  • 5
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
目录回到顶部↑丛书前言 前言 读者须知 第1章 visual c++起点 实例1 mfc框架、文档和视 实例2 创建多文档窗口 实例3 创建对话框窗口 实例4 创建启动窗口 第2章 多窗口界面 实例5 多窗口的单文档界面 实例6 通过菜单改变视图 实例7 通过工具栏改变视图 实例8 多视图窗口数据显示 第3章 实现microsoft风格 实例9 visual studio风格的窗口 实例10 internetexplorer4.0风格的窗口 实例11 outlook风格的窗口 实例12 状态栏中的任务栏 第4章 超文本界面 实例13 创建超级链接界面 . 实例14 显示模式html对话框窗口 实例15 超文本浏览器 实例16 数字化时钟 第5章 各种特效窗口 实例17 创建不规则窗口 实例18 操作dib位图窗口 实例19 位图转换窗口 实例20 渐变的窗口 第6章 对话框界面 实例21 可扩展对话柜 实例22 位图预览对话框 实例23 显示目录树对话框 实例24 透明对话框 第7章 菜单状态操作 实例25 漂亮的图文菜单 实例26 随文档动态改变菜单 实例27 启用和禁止菜单命令 实例28 复选菜单命令 第8章 自绘制菜单 实例29 自绘制图标菜单 实例30 visualstudio外观的工具栏 实例31 对话框窗口中的菜单 实例32 弹出位图菜单 第9章 动画按钮 实例33 avi动画按钮 实例34 实现图标按钮 实例35 实现一组图标按钮 实例36 绘制dib动画按钮 第10章 多彩按钮 实例37 位图按钮 实例38 带颜色的按钮 实例39 对鼠标敏感的按钮 实例40 可弹出菜单的按钮 第11章 图文按钮风格大全 实例41 各种图文按钮风格 实例42 图标浮动按钮 实例43 图钉按钮 实例44 计数器按钮 第12章 不规则按钮大全 实例45 各种不规则按钮 实例46 环形按钮 实例47 三角形按钮 实例48 椭圆形按钮 第13章 掩膜编辑框 实例49 ip地址输入编辑框 实例50 下拉列表控件中的编辑柜 实例51 掩膜输入编辑框 实例52 数字输入控制编辑框 第14章 编辑框和树视控件 实例53 破解密码框内容 实例54 16进制编辑输入框 实例55 树型注释 实例56 目录树结构 第15章 组合框集锦 实例57 多列显示的组合框 实例58 自动完成功能的组合框 实例59 浮动的组合框 实例60 浮动的彩色组合框 第16章 操作系统的组合框 实例61 “快照”组合框 实例62 多列属性组合框 实例仍 使用组合框选择颜色 实例64 使用组合框选择字体 第17章 组合框和列表框 实例65 具有历史记录的组合框 实例66 系统图像列表 实例67 excel表格视图 实例68 浏览数据库的列表框 第18章 属性页初级篇 实例69 改变属性页的按钮区 实例70 完整的属性页应用程序 实例71 嵌入对话框中的属性页 实例72 实现wizard 第19章 属性页及标签页高级篇 实例73 在对话框中的属性页 实例74 实现标签下标式的视图切换 实例75 属性列表控件 实例76 标签列表控件 第20章 工具栏集锦 实例77 具有下拉按钮的工具栏 实例78 工具栏中的列表框 实例79 各种颜色和大小的工具栏 实例80 气球式提示窗口 第21章 状态栏初级篇 实例81 改变状态栏的状态格 实例82 进度条中的文字 实例83 带声音的提示条 实例84 3d向量控件 第22章 状态栏高级篇 实例85 丰富的状态栏风格 实例86 使用动画控件 实例87 使用日历控件 实例88 使用柱状图控件 第23章 系统操作之一 实例89 方便的日历托盘 实例90 列举系统字体 实例91 实现注册表功能 实例92 检测windows版本号 第24章 系统操作之二 实例93 动画显示的托盘 实例94 浮动窗口 实例95 获得系统硬件信息 第25章 系统操作之三 实例96 修改系统[开始]菜单 实例97 检测系统中的api包 第26章 系统操作之四 实例98 建立自己的任务栏 实例99 隐藏任务条中的应用程序 实例100 检测cpu的时钟 附录a 窗口类与窗口样式 a.1 窗口类结构 a.2 窗口类样式 a.3 窗口样式分组 附录b mfc中windows公共控件的通知消息 b.1 windows公共控件的通知 b.2 通知消息结构 b.3 通知过程概述 b.4 更好地处理通知的方案 b.5 用0n_n0tify_range指定通知域 附录c visual c++开发实用技巧
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值