粒子效果
中心是一个人物模型,似乎也是由粒子贴片而成的。此外还有一些发光粒子,在无规则运动,不时会撞击到模型上,导致模型裂开成一些粒子。这些散落的粒子被撞击裂开后,会逐渐恢复原先在模型上的位置。
Niagara蓝图部分
蓝图部分给出了两种实现方案:基于GPU粒子的和基于CPU粒子的。本文将会着重解析基于GPU粒子的实现方案。
GPU粒子的实现方案里又有两个粒子发射器,一左一右分别对应无规则运动的发光粒子和组成人物骨骼模型网格的粒子。其中右边的粒子发射器中明显集中了比较多的自定义操作,这也会是我们分析的重点。
此外,另一个需要提前提到的点,就是System Spawn里设置了一个System级别的参数Influencers,顾名思义,就是会对其他粒子产生影响的粒子,这里需要填入该粒子所在的粒子发射器的名字(填了第一个也就是自发光粒子发射器的名字)
效果实现分析
自发光无规则粒子
这个粒子发射器是一个比较常规的粒子发射器。这里做了对每两个Tick间的时间间隔做了一个限制,即防止因为帧与帧的延迟导致的模拟上的问题。这里取了1/60,意味着模拟的是60帧以上的情况,当游戏降到60帧以下时,会采用60帧来进行模拟。
另外可以注意到,这个发射器在粒子里写入了一个属性(SecondSkeletalMeshInfluencerSpriteScale),而这个属性在这个发射器上并没有被用到,不过由它的名字可以知道,它是会作用在其他的发射器的粒子上。
骨架网格体粒子
来到骨架网格体粒子,全称应该是用来重建骨架网格体的sprite粒子。下图可以看出,一下就拿出了3w个粒子来“重建reproduction”这个skeletal mesh。
将粒子拼成骨架网格体的这个模块,就是Initialize Mesh Reproduction Sprite。用User命名空间的属性拿到外部SkeletalMesh,拿到模块里进行采样。勾选Overwrite Intrinsic Variables可以重写Particles里的一些属性。到这里,我们就拿到了一个完全由Sprite粒子组成的小白人。
下面进入让粒子产生交互的部分。
Iterate Over Influencers Inline
Iterate Over Influencers Inline主要是对三个参数和两个属性进行了读写:Transient.CompositedInfluencerVelocity,Transient.NumberofEffectiveInfluencers,Transient.TotalInfluence以及System.Influencers和Particles.Position。其中,那三个Transient变量是不会在帧与帧之间进行传递的,他们的主要作用是在同一帧不同模块间传递信息。
这一模块的脚本蓝图相当之长,其引入的一个新的节点是Map For。前面我们已经接触过Map Get和Map Set,分别是用来获取参数和设置参数的。Map For和Map Set的最大差别在于,Map For是一个循环结构,即它会进行自我循环迭代(相当于代码里的for循环,范围是到上一个Map Set,如果没有的话,则从入口开始循环)(此外,Iteration Count是迭代的总数,至于当前迭代的索引,应该是写在了底层代码里)。我这里做一个拆分,主要是依据Map For里迭代的这些变量,看他们是如何变化的。
Iteration Count代表了总的迭代数,它的取值是直接取到施加影响的发射器中的粒子个数。
Iteration是当前的迭代索引(局部变量),用来记录迭代的进程。
Number of Effective Influencers字面意思是有效影响者的数量,整形;Composited Influencer Velocity字面意思组合影响者的速度,向量;Total Influence意思是总的影响,浮点数。他们三个的值的赋予是通过一个条件判断。
这个条件判断(if语句)的条件如下图。其操作是通过Iteration作为索引取出Influencers中对应索引的粒子中的Position位置和Velocity速度属性,两者分别返回两个布尔值代表是否成功取到,然后对两个布尔值做逻辑与的操作,即只有两者都成功的情况下,返回true;否则返回false。
那么先看True的情况。
Total Influence即是图中Add节点的结果。而Subtract的A入口是粒子与影响者粒子之间的距离。该值减去设定的Influence Radius影响半径,再除以Influence Radius Falloff影响半径衰减(取Influence Radius Falloff和0.000001中的最大值),再去掉小于0大于1的部分,再用1减去这个值,再做一个幂运算(幂值取Distance Falloff Power距离衰减),再加上上一轮迭代的Total Influence的结果。描述太过冗长,这里用一个公式来表达:
T
o
t
a
l
I
n
f
l
u
e
n
c
e
+
=
(
1
−
c
l
a
m
p
(
(
L
e
n
g
t
h
−
I
n
f
l
u
e
n
c
e
R
a
d
i
u
s
)
/
M
A
X
(
I
n
f
l
u
e
n
c
e
R
a
d
i
u
s
F
a
l
l
o
f
f
,
0.000001
)
,
0
,
1
)
)
D
i
s
t
a
n
c
e
f
a
l
l
o
f
f
P
o
w
e
r
Total Influence +=(1- clamp((Length - Influence Radius)/MAX(Influence Radius Falloff,0.000001) ,0,1))^{Distance falloff Power}
TotalInfluence+=(1−clamp((Length−InfluenceRadius)/MAX(InfluenceRadiusFalloff,0.000001),0,1))DistancefalloffPower
可以看出,如果粒子与影响者粒子之间的距离Length的值过大,Total Influence不会增加(自增为0);粒子与影响者粒子之间的距离过小的话,Total Influence最大会自增1。其余的值会受到Influence Radius,Influence Radius Falloff,Distance falloff Power三个参数的调控。
从中也可以看出,Total Influence是一个总体上描述粒子受Influencers影响的程度的参数。
再来看Number of Effective Influencers。这个值会在每轮迭代中当Total Influence的自增量大于0时自增1,即记录迭代过程中有效自增(有效影响)的个数。
Composited Influencer Velocity不断叠加本轮次迭代中的Total Influence的自增量乘以Influencer的速度的量,在迭代完成后相当于是所有施加的速度影响的合速度。
总结来说,三个量其实都是受到Length影响的,即粒子到施加影响的粒子的距离(当然还有我们设定的各种距离衰减等值,这里暂时认为他们都是定值),即通过距离来判定施加影响的粒子到底对于当前粒子有没有施加到影响,以及应该施加多大的影响(这些“影响“都存储在了Transient变量Number of Effective Influencers,Composited Influencer Velocity和Total Influence里了)。
在完成迭代过程后,还需要对Total Influence进行一次标准化。这里用总的影响参数(Total Influence)除以了施加了有效影响的Influencers的个数,会得到一个0~1范围内的值,赋值给Total Influence。
到这里Iterate Over Influencers Inline的拆解算是完成了,我们知道了三个参数分别用来描述什么。但是到目前为止这些参数都还没有与粒子的属性发生联系。后面的模块就会着重于实现这几者之间的关联。
Reduce the Accumulated Influencer Over Time
这个模块是一个已有模块的重命名,原模块是Fade Over Time。本质上是一个对数值以DeltaTime为单位进行自减运算的。其中目标数值前面算出的Total Influence(范围0~1),每一帧的结果都会记录到Particles下的子命名空间Fade Over Time里的属性CurrentValue里。
Read Updated Skeletal Mesh Position
是Update Mesh Reproduction Sprite模块的重命名。作用是动态捕捉网格体上的属性。
Translate Accumulated Values Into Forces
是Set Physics Force模块的重命名。这里写了一个动态输入的方法来对PhysicsForce进行动态赋值。
里面是将速度也进行了标准化(用合速度除以有效影响数目得出一个平均的影响速度),然后乘以时间和强度,将这个算出来的值加到PhysicsForce上,相当于是在粒子受的物理力上将粒子影响通过速度的方式考虑了进去。
Set Color
颜色的调整,这里是将CurrentValue与颜色的Alpha通道相关联。
Lerp Particle Attributes
是用来将被力搞得乱起八糟的粒子插值恢复原状的。这里的Alpha是
(
1
−
C
u
r
r
e
n
t
V
a
l
u
e
)
3
(1-CurrentValue)^3
(1−CurrentValue)3
即CurrentValue为1的时候(此时影响者粒子对被影响者粒子的影响最大),Alpha为1;CurrentValue为0时(被影响者粒子脱离了影响者的影响范围),Alpha为0。通过前面的Fade Over Time模块,就可以实现CurrentValue从1到0(大概)的渐变。
最后还有一点,就是关于材质的部分。被击飞粒子会变成橘黄色,是在材质里写了一个混合,并由Particle Color的alpha值来作为混合参数。还记得吗,该alpha值也是和CurrentValue挂钩的。
以上就是对该粒子的全部分析。
总结
写的笔记越来越多,但是也发现了想要将一个技术点讲解清楚要花费的精力和需要的文采也是相当的多。这个例子外观非常的炫酷,其中使用的算法也非常具有启发性,并且对各个模块的灵活使用让人也大开眼界,当然其中茫茫多的参数也是调节起来也是很让人头痛的。
其实最近了解了一些混沌理论相关的东西,也在思考在编码过程中的混沌理论或者说混沌现象。我在想,具体某一个效果,是可以精确计算出来的吗,还是只能通过大致的感觉进行参数的拨弄,然后在某个意外的契机下调出了想要的效果。可是如此一来,计算在这其中的意义会有多大呢?如果意义有限的话,那么特效调整上的效率问题又该如何考量呢?