近期需要实现水体的FlowMap可视化效果,体现湖泊河流的水流流向,这里记录一个UE5.5引擎下使用Niagara ribbon粒子的实现方式。
实现原理是我们将粒子流动方向的二维向量写入texture贴图纹理中,再在Niagara system中读取texture的R、G通道获取相应的粒子流向,给粒子施加相应的加速度,让ribbon粒子画出流场的流动轨迹和趋势。
这里我们以一个漩涡旋转的texture纹理为例,下图以R、G通道(0~255)分别存储了X、Y轴向上的二维向量(0~1),我们可以用它调试要创建的Flowmap粒子系统。
我们将该纹理导入UE,导入后texture的sRGB属性需要勾选false,以启用线性色值,这样R、G通道取值范围0-255才能线性地对应二维向量值的0-1取值范围。
接着我们新建一个粒子系统,并新建一个minimal的粒子发射器Emitter,命名为FlowMapPoint
因为Emmiter需要用到Sample Texture作为输入,这里需要将Sim Target改为GPU***pute Sim,同时Calculate Bounds需要选为Fixed,Local Space需要勾选为true,Interpolate Spawning设为false
在Emitter Update下新加一个Spawn Particles in Grid 模块,让粒子按网格点位生成,这里会遇到提示,提醒创建该模块需要同时添加Grid Location模块,这里点击fix可以自动创建Grid Location模块同时修复报错。
这里的flowmap是二维向量,不需要z轴方向,生成粒子数量的z值设置为0。因为输入的图片尺寸可能不固定,设为user参数方便动态设置。
接下来设置initial particle模块参数,首先initial particle模块需要排在grid location模块之前,sprite size mode设为uniform统一大小尺寸,lifetime mode设为random,取一个时间范围内的随机生命长度,这样粒子错开生成和消失时间比较自然。
之后设置grid location,grid的x y维度尺寸同样暴露为user参数,按不同的输入图片尺寸设置,z轴不需要,设置为0即可。生成位置的offset取一个随机值,这样粒子生成位置分布也显得比较自然一些。
下一步我们在particle update下添加sample texture的模块,输入粒子流动方向的数据。sample texture的纹理可以使用上面准备好的漩涡旋转texture纹理,贴图的UV为(Particle.Position.X/User.GridSize.X+0.5,Particle.Position.Y/User.GridSize.Y+0.5)
这样将纹理的中心点(0.5,0.5)映射到粒子系统原点,并将输入纹理的范围映射为粒子的分布范围GridSize
通过这一步粒子的颜色会初步映射为输入纹理的颜色
然后我们可以基于纹理给粒子添加流动加速度了,添加一个A***eleration Force模块,同时需要添加Solve Forces and Velocity模块,同样可以点击fix自动创建Solve Forces and Velocity模块同时修复报错。
这里加速度设置为每个粒子上RG通道的值减0.5,大于0.5为正向小于0.5为反方向,因为UE引擎是左手坐标系,这里Y方向的加速度要乘-1取反,最后再乘上一个系数调节加速度的大小,这里先乘了100。
设置好加速度后粒子应该会按螺旋状初步移动起来。
接下来我们添加kill particles模块过滤掉未移动和移动范围很小的粒子,这里我们将XY方向速度均小于0.05的粒子筛选掉。kill particles模块创建好后会有有个bool值控制,这里我们创建一个scratch dynamic input点开编辑scratch dynamic input,我们添加三个input节点X、Y、tolerance,筛选XY方向距离0.5均小于tolerance的值,然后回来在kill particles设X、Y为sample texture纹理的RG通道,设置tolerance为0.01,即筛选掉RG通道均约等于0.5的粒子。
添加drag模块让粒子移动效果更为匀速
这样我们把粒子系统放入场景可以初步看到粒子沿着纹理的螺旋轨迹不断移动,但我们的目标不是点状而是线性流动的效果,这里我们使用ribbon粒子来实现,所以下一步我们在Niagara system再创建一个新的emitter,render下设置为ribbon render。
新建的ribbon emitter同样将Sim Target改为GPU***pute Sim,同时Calculate Bounds需要选为Fixed,Local Space需要勾选为true,Interpolate Spawning设为false
我们需要根据之前生成的FlowMapPoint发射器的粒子位置来生成ribbon粒子,这里要在ribbon粒子发射器的emmiter update下添加Spawn Particles from Other Emitter模块,同时需要在Particle Spawn添加Sample Particles from Other Emitter模块和Particle Update下添加Solve Forces and Velocity模块,缺失的话同样点击报错的fix it可以自动添加。
添加了几个模块后,先在Spawn Particles from Other Emitter中设置emitter binding的emitter名称为我们之前创建好的FlowMapPoint
然后我们需要根据FlowMapPoint的粒子ID分别生成ribbon粒子,否则只会生成一条贯穿所有粒子位置的ribbon。这个我们一边需要在ribbon粒子的Sample Particles from Other Emitter模块中设置Particle ID Sampling的方式为Apply Sampled ID as Ribbon ID,另一边要在之前的FlowMapPoint粒子发射器中开启Requires Persistent IDS
我们在场景中只需要显示Ribbon粒子,因此可以将FlowMapPoint下的Sprite Render勾选为false隐藏掉,这样应该可以看到一个单纯的Ribbon粒子FlowMap效果,但是没有颜色。
最后我们给Ribbon发射器加一个Sample Texture模块,将Flowmap对应的热力图或伪彩色图,写在一张纹理上,并传输给Ribbon粒子。这里和FlowMapPoint的Sample Texture模块类似,将每个粒子位置换算成整体flowmap的UV然后获取对应位置的颜色值,这里的Texture UV可以将FlowMapPoint的UV参数复制过来,但要注意Generate UV using Importance Sampling要勾选为false,否则图像是按每个Ribbon粒子的UV渲染。
最后用测试纹理的效果如下