您的位置:首页 > 其它

[GEiv]第六章:粒子特效 绚丽的火焰与爆炸

2014-09-24 15:18 225 查看
第六章:粒子特效
绚丽的火焰与爆炸
本章节主要介绍粒子特效设计的方法论,其中有相当的知识量是平台无关的;在本文中会以“爆炸”这个实际的例子为线索,进行详细的设计讲解,并最终使用GEiv实现它。

[为什么要使用”粒子”]

实现粒子特效的首要目的,是对一些环境效果进行模拟仿真,常见的环境效果,例如火焰、爆炸、雨、雪、雾等,都是无数微小的粒子以某些规律共同作用的结果。而对于计算机来讲,虽然没有足够的运算能力对每一个自然粒子进行抽象,但我们可以借鉴其原理,使用相对更少的粒子对这些自然现象进行模拟和仿真,以达到近似的效果。

[需要设计哪些内容]

[粒子属性]

首先需要设计的是单个粒子的属性,这里我们以粒子个体作为考虑的焦点,考虑的内容往往是粒子的共有属性,属性的内容可以是图形样式、大小、颜色等等。

[投射规律]

投射规律考虑粒子以何种方式投射到屏幕上,这里以粒子群为考虑的焦点,考虑的内容会涉及到实际的物理规律,例如粒子在空间中的角度分布、速度分布以及颗粒大小分布等情况。

[演变规律]

演变规律是抛射后的粒子随着时间变化的规律,它同样会涉及到物理规律的模拟,只不过这次是针对单个粒子的设计,例如速度、自旋角度、颜色等属性的变化规律。

[实例-模拟爆炸]

爆炸特效在游戏中的使用相当广泛,属于经典的粒子系统。现在我们从零开始,设计一个爆炸的粒子特效。
在想要模拟爆炸前先来观察一个实际的爆炸例子:



从图片中我们能够概况一些基本的物理规律:
首先,在一个爆炸中,粒子的大小显然是不同的,而且,简单的想,粒子的大小与其质量成正比,所以粒子速度应该与其大小负相关,你可以看到颗粒状的小型碎片已经飞到了火焰之外的区域,这是动量守恒定律所确定的。
其次,在爆炸的中心,能量较高,呈现出亮白色;而在爆炸的外围,与空气接触后热量明显下降,火焰呈现出暗红色,在这个过程中,颜色也呈现出了明显的变化规律:亮白-》黄色-》红色-》暗红。
还有,速度的变化规律:在爆炸发生后,粒子的速度并不会一直不变,它还要受到空气阻力的作用,根据流体力学的相关内容,空气阻力与速度的平方成正比,与物体在运动方向的正投影面积成正比,所以其速度变化应该表现为某种受到阻尼的运动状态。
最后,在能量耗尽的暗红色区域,粒子逐渐消失,也就是说其颜色通道系数应该以某种非线性(先慢后快)的方式衰减。

[属性设计]

粒子图元:首先需要确定的问题,我们如何选择粒子的图形呢,使用点?圆形?方块?还是使用某种贴图呢……其实设计粒子的基本形态很值得一说,我们暂且使用圆形来设计,在最后您可以看到更改粒子形态对整体特效的影响。
粒子的颜色:由白到红,初始值使用白色。
粒子的大小:为了较为明确的产生大小两种粒子,我将使用一定的概率分布策略随机产生大小(详见投射设计部分)。
自旋角度:在圆周上均匀分布,由于一开始我们使用圆形作为图元,所以这个自旋这个属性不会显露出来。
通道:Alph初始值设置为1.0。

[投射设计]

产生:在我们给定爆炸点之后,假定粒子围绕着给定点进行+/-5位置浮动的随机的产生。
大小分布:以50%的概率产生6~35大小的粒子,否则产生6~24大小的粒子,这里只是一个简单方案,你也可以考虑使用高斯分布等。
速度方向分布:以产生点进行360度均匀分布。
速度大小分布:为了简化选择了恒定值,但是,空气阻力模型在演变中起到作用,故仍可观察到非常近似地模拟结果。

[演变设计]

速度衰减:



对于每一帧:v -= a*w^2*v;其中,w是粒子大小,a是衰减系数,v是当前速度,也就是说,速度进行阻尼衰减,并且大碎片的速度衰减的更快。
颜色衰减:



↑衰减时间图



↑衰减过程均匀抽样
首先,RGB中的红色分量是不变的。
假如把时间t变量规格化到0~1之间。
那么,蓝色分量应该最快衰减,因为爆炸主色调至少应该是一个暖色调。所以蓝色线使用的是t^16。
绿色分量暂时设置为伴随t的线性衰减,其实,G分量衰减速度可以依据大小而定以获得更逼真的效果。
通道衰减:



↑衰减时间图



↑衰减过程均匀抽样
通道衰减过程先慢后快,这样,在特效开始的一段时间内,我们不会感到通道的变化,直到粒子快要消亡时才会有直观的视觉感受。

[编码实现]

接下来就是编码阶段了,我们也明确的看到,其实整个粒子特效的实现过程中,设计占了相当大的比例,在最后的阶段,只不过是要我们使用擅长的平台去实现罢了,其实很多软件开发都是这样的,编码只是个实现过程,不是什么高科技。
您可以到GitHub上找到本章中的例子。这里进入
在Geiv下,我们的粒子仅需要实现Individual接口,并使用个体的集群管理器进行管理即可(参阅第五章)。

//ExpIndividual.java:
package com.geiv.test;

import engineextend.crowdcontroller.Individual;
import geivcore.UESI;
import geivcore.enginedata.obj.Obj;
import java.awt.Color;
import com.thrblock.util.REPR;
import com.thrblock.util.RandomSet;

public class ExpIndividual implements Individual {
UESI UES;
Obj disp;

float sTallms = 500;//这里设置了粒子从产生到消亡的总经历时间
float allms = sTallms;
float Dms = 17;//这里设置了每一帧的时间,你也可以用1000/UES.getFPS这中方法在构造器里填充

float V = 4.5f;//运动的初始速度被固定为4.5像素每帧
float ax, ay;
float vx, vy;
float Theta;//自选角度,本例中暂时使用圆形,所以是看不出的

public ExpIndividual(UESI UES) {
this.UES = UES;
disp = UES.creatObj(UESI.XRIndex);//这里把图元产生在了XR层,前面的章节中介绍了该层次混合模式的特点。
disp.addGLOval("FFFFFF",0,0,12,12,12);//画一个圆形
disp.setGLFill(true);
disp.setColor("FFFFFF");
disp.setAlph(disp.getTopDivIndex(), 1.0f);

allms = sTallms;
}

@Override
public boolean isAvalible() {
return !disp.isPrintable();//关于Individual请参考第五章的介绍
}

@Override
public void getUse(Object[] ARGS, float... FARGS) {
int Rad;//我们使用一定的分布方法产生Rad大小,RandomSet是内置的随机数发生器,其静态方法名称都比较好理解,就不在这里细细讲解了。
if (RandomSet.getRate(50)) {//以50%的概率返回布尔值true
Rad = RandomSet.getRandomNum(6, 35);//返回6~35随机数,均匀分布。
} else {
Rad = RandomSet.getRandomNum(6, 24);
}
disp.setWidth(Rad);
disp.setHeight(Rad);
//初始位置具有+/-5的浮动区域
disp.setCentralX(FARGS[0] + RandomSet.getRandomNum(-5, 5));
disp.setCentralY(FARGS[1] + RandomSet.getRandomNum(-5, 5));
//初始自选角度,0~360均匀分布。
disp.setAngle(RandomSet.getRandomFloatIn_1() * 360);
//速度角,0~2PI均匀分布,使用弧度是为了方便调用Math下的三角函数。
Theta = (float) Math.PI * 2 * RandomSet.getRandomFloatIn_1();
vx = V * (float) Math.sin(Theta);//计算横纵向速度
vy = -V * (float) Math.cos(Theta);
ax = -0.0003f * (disp.getWidth() * disp.getWidth()) * vx;//计算加速度
ay = -0.0003f * (disp.getHeight() * disp.getHeight()) * vy;
disp.show();//显示到屏幕上(投射完成)
}

@Override
public void doStp(int clock) {
if (this.allms > Dms) {
allms -= Dms;//allms记录当前剩余存活期,使用这个变量是为了将存活期规格化到0~1之间。
//REPR是内置的变换工具,可以将一个规格化后的线性量转化为自定义的常用非线性量
//颜色变化
disp.setColor(new Color(1.0f, REPR.Rep_POW_1_F(allms / sTallms, disp.getWidth() / 24), REPR.Rep_POW_F(allms / sTallms, 16)));
//通道变化
disp.setAlph(REPR.Rep_POW_1_F(allms / sTallms, disp.getWidth() / 12));
//运算加速度
ax = -0.0003f * (disp.getWidth() * disp.getWidth()) * vx;
ay = -0.0003f * (disp.getHeight() * disp.getHeight()) * vy;
//运算速度
vx += ax;
vy += ay;
//运算位置
disp.setDx(disp.getDx() + vx);
disp.setDy(disp.getDy() + vy);
} else {
//生命周期结束后,将粒子资源回收
finish(Individual.SRC_INNER);
}
}

@Override
public void finish(int src) {
disp.hide();
//重置颜色与通道
disp.setColor("FFFFFF");
disp.setAlph(disp.getTopDivIndex(), 1.0f);
//重置大小
disp.setWidth(12);
disp.setHeight(12);
//重置存活时间。
allms = sTallms;
}

@Override
public void destroy() {
disp.destroy();
}
}


//Explosion.java:
package com.geiv.test;

import engineextend.crowdcontroller.CrowdController;
import geivcore.UESI;

public class Explosion{

UESI UES;
CrowdController cc;
public Explosion(UESI UES)
{
this.UES = UES;
cc = new CrowdController(UES, true);
for(int i = 0;i < 512;i++)//装入了512个粒子资源
{
cc.addIndividual(new ExpIndividual(UES));
}
}

public void doEffect(float dx,float dy) {
for(int i = 0;i < 128;i++)//当每次调用时,分配128个粒子资源,同时也意味着,您可以同时在屏幕上产生4个异步的爆炸特效。
{
cc.getAvailible().getUse(null,dx,dy);
}
}

public void forceClose() {
cc.finishAllInd();
}
}


//Main.java:
package com.geiv.test;

import geivcore.R;
import geivcore.UESI;

public class Main{
public static void main(String[] args) {
UESI UES = new R();
Explosion exp = new Explosion(UES);
for(;;){
exp.doEffect(400,300); //产生一个爆炸
UES.wait(3,1000); //延时1秒
}
}
}


执行效果:



[粒子特效的改进]

“一堆圆形一点儿也不像嘛”这是我同学看到程序后的第一句评价,的确,从粒子的行为模式上来讲,是有类似爆炸的性质了,不过一个爆炸也不能只让圆形组成不是吗?



↑给一个大点的图,可以更突出的发现这个问题

在属性设计时,我提到了关于粒子图元选择的问题,对于爆炸这个特效,显然均匀的圆形(或者其他图形)不是一种好的图元构成,我们需要一个形状并不均匀,甚至伴有随机性的图形来替换这个圆,于是笔者想到了“云”这个东西。



↑由于云是白色的,所以为了展示,把PS的衬底一起截下来了。
云本来是与爆炸毫不相干的东西,选择它是由它的图形性质决定的:边缘渐变、具有随机性、在颜色通道上也不均匀。而且,加上我们之前定义的自旋随机分布,加入自旋角的云看起来和彼此具有更大的差异。
为了使用云这个素材,先把它放在项目目录里:



之后找到ExpIndividual类,找到它的图元绘制部分:
我们把
disp.addGLOval("FFFFFF",0,0,12,12,12);
disp.setGLFill(true);
这两行改为:
disp.addGLImage(0, 0, 12, 12,".\\Effect\\PT_CLOUD1_POINT.png");
经过改进的特效:





↑可以跟圆形做一下对比,是不是好多了呢

[总结]

本章介绍了粒子特效设计的基本步骤,即属性、投射、演化三部分。
粒子特效是对自然的模拟,因此在设计时要充分地考虑到物理因素,这样会得到更好的仿真结果。
最后,恰当地选择粒子图元可以得到更好的结果,而粒子图元的选择与图形的性质有关。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: