虚幻3粒子编辑器学习过程

前言

一开始接触虚幻3粒子相关模块是因为公司希望做一个面向用户的粒子编辑器,所以需要简化虚幻3粒子编辑器的一些功能以及加一些本土汉化,所以就开始着手粒子相关的研究。因为是在虚幻3基础上开发,所以一开始的学习成本其实比较低,不需要知道渲染相关的知识,只要会用它API就够了。当然后面会说粒子渲染相关的知识,我们先来说说粒子编辑器所需要具备的一些知识。

基础

虚幻3中如果想要粒子功能,就需要用到UParticleSystemComponent,它拥有一个粒子对象UParticleSystem。在虚幻3的资源管理器中看到的粒子文件其实就是UParticleSystem。而我们日常制作中,一个UParticleSystem会创建多个发射器,也就是多个UParticleEmitter,如下图每一列就是一个发射器。其实UParticleEmitter只不过是一堆配置,或者说是一堆数据。而实际在模拟粒子发射的东西是    FParticleEmitterInstance,它挂在UParticleSystemComponent下面,一个UParticleEmitter会对应一个FParticleEmitterInstance,FParticleEmitterInstance只不过是会根据UParticleEmitter的数据进行模拟。不过我们先不用关心它,回到UParticleEmitter中。发射器之所以有那么多功能,主要是因为UParticleEmitter下挂了多个组件,下图每一行就是一个组件。我们称它们为Module,在虚幻中它的类多以UParticleModule前缀来命名。

常用Module

然后我们来说说常用的Module有哪些,因为我们当时只是追求的是尽量覆盖常用的Module就够了,所以给的都是美术日常工作中常用的哪些。

UParticleModuleRequired:最基本而且必须的组件,可以设置材质,发射时间,循环,延迟,序列帧等功能

UParticleModuleSpawn:处理发射粒子数和喷射

UParticleModuleLifetime:管理粒子生命周期

UParticleModuleSize:设置粒子尺寸

UParticleModuleSizeMultiplyLife:控制粒子尺寸随生命周期变化

UParticleModuleVelocity:控制粒子初始各方向速度

UParticleModuleVelocityOverLifetime:控制粒子速度随生命变化,会乘以一个倍数到初始速度上

UParticleModuleColorOverLife:控制粒子颜色和透明度随生命变化

UParticleModuleColorScaleOverLife:控制粒子颜色和透明度随生命变化,会乘以一个倍数到当前颜色上

UParticleModuleLocation:控制粒子在一个立方体区域内生成

UParticleModuleLocationPrimitiveCylinder:控制粒子在圆柱体区域内生成

UParticleModuleLocationPrimitiveSphere:控制粒子在球体区域内生成

UParticleModuleSubUV:粒子序列帧功能

UParticleModuleRotation:控制粒子初始旋转

UParticleModuleRotationRate:控制粒子旋转速率

UParticleModuleOrbit:控制粒子扰动,不知道怎么形容没仔细研究,感觉就是粒子乱飞乱转

UParticleModuleAcceleration:控制粒子加速度

UParticleModuleOrientationAxisLock:控制粒子锁定轴向,常用于面向Z轴,也就是朝上,不面想摄像机

UParticleModuleTypeDataRibbon:用来制作飘带粒子

UParticleModuleTypeDataMesh:用来制作模型粒子

UParticleModuleMeshRotation:控制模型粒子初始旋转角度

UParticleModuleMeshRotationRate:控制模型粒子初始旋转速率

UParticleModuleMeshRotationRateOverLife:控制模型粒子旋转速率随生命周期变化,会叠加到初始旋转速率上

UParticleModuleTypeDataBeam:用来制作光束粒子

UParticleModuleBeamNoise:用来制作类似电流噪声类似的东西,不是特别了解

UParticleModuleBeamSource:用来设置光束起点

UParticleModuleBeamTarget:用来设置光束终点

粒子编辑器相关设计

上面这些其实已经将大半的Module都用上了,已经能满足大部分需求了,剩下的其实就是粒子的表现了。粒子的表现其实很大部分取决于材质以及贴图,但我们这是面向用户的粒子编辑器,不是材质编辑器,所以最终形态是用户只能选择我们做好的两个默认材质,基本上只有混合模式的不同,一个是Translucent另一个是Additive,然后支持传一张Diffuse和一张Mask,支持填UV的Tilling和Offset,后来支持了禁用深度比较。

当时因为公司有手游项目用了虚幻4,所以考虑做成一个我们自己的格式,方便移植,所以就将数据和贴图以及模型打包存成一个单独的文件。然而遗憾的是,直到最后这个产品都没有面向用户,因为公司调整我们团队被优化掉了。然后我跟着之前的领导出去创业,我继续给他们打工。因为之前的种种原因,觉得虚幻引擎过于庞大,而我们用到的功能甚至可能不足五分之一,所以开始着手研发自研引擎。一切就是这么巧合,自研引擎缺乏一套完善的粒子系统,加上之前做了粒子编辑器,我们完全有办法通过粒子编辑器产出自研引擎所需要的粒子资源,前提是自研引擎上装载了虚幻的这套粒子系统。所以开始踏上了粒子系统的研究路,用了一个月时间开发完成了,万幸的是基本没遇到什么大坑。主要就是在自研引擎上,把虚幻的粒子系统照搬过来。然后将之前自己的格式转成自研引擎的格式,需要将里面的图片和模型解出来去重放到本地。

而且后知后觉地发现了一些好处,因为单一的材质,节省了大量写shader的时间,因为没有材质编辑器也没办法生成shader,也不会像它那样编译shader浪费大量时间。可以程序处理掉完全相同的图片(遍历像素进行比较),虚幻3中一张图片可以导很多次,引擎不会自动识别,这样就造成了大量的硬盘空间浪费。

逻辑部分

我们先来讲逻辑部分,之前的Module其实大致可以分为4类,TypeDataModule,SpawnModule,UpdateModule,OrbitModule。TypeDataModule比较好理解,没有的话它是普通粒子,而跟它相关的Module有UParticleModuleTypeDataRibbon、UParticleModuleTypeDataMesh和UParticleModuleTypeDataBeam,也就是说它直接决定了粒子的功能,到底是普通,飘带,模型还是光束。SpawnModule则是在新发射一个新的粒子时,SpawnModule类型的Module可以用于初始化粒子,类似UParticleModuleLifetime、UParticleModuleSize、UParticleModuleVelocity等都是SpawnModule类型。而UpdateModule则是每帧都会进行更新的Module,大部分是随生命变化的那些Module。OrbitModule基本上就是扰动相关的Module了,这部分我用的少,所以不进行讨论。

发射器的模拟流程大致是

1、计算发射器的时间,看是否经过一个循环以及重置喷射状态以及处理延迟

2、销毁生命周期完结的粒子

3、生成粒子,这个阶段那些SpawnModule会初始化刚发射的粒子

4、重置所有粒子成初始刚Spawn出来的状态

5、遍历更新所有粒子,所有UpdateModule和TypeDataModule以及OrbitModule会参与更新

6、渲染粒子

渲染部分

常规的粒子发射器(不携带任何TypeDataModule),其实可以把粒子发射器理解成一个模型,它发射出来的粒子其实都是一张图片,也就是两个三角面,大部分时候它们都面向摄像机,也就是所谓的广告牌技术(Billboard)。我们只需要一次DrawCall,把这些三角面一次性提交进行渲染,就可以实现常规粒子发射器的功能

飘带发射器,一开始看着贼酷炫,但明白原理之后其实发现挺简单的。它是将按时间发射的粒子连起来,然后平均分配图片的UV坐标到这些粒子上,两个粒子间会根据距离补充一些面数。所以视觉效果上就是一张图片拉伸在一长条路径上,中间有一些扭动,其实就是有一两个粒子偏离了直线,所以连接起来就不是那么直。要做到这个视觉效果,飘带发射器必须运动起来,使得粒子间有距离,不然粒子都堆在一个点上时,其实是看不到任何效果的。

模型发射器,这部分没仔细看虚幻渲染是怎么实现的,我自己是直接用Instance的方式直接一次渲染多个模型的方式来做的。原本顶点数据是只传每个模型的矩阵,多传一个顶点色而已。

光束发射器,渲染的原理大致与飘带发射器是一样的,都是一长条的面片。不同的是逻辑部分,太过于复杂,当时只是手撸了一遍,没太看明白

性能相关

一个发射器就一个DrawCall,模型发射器比较特殊,如果硬件不支持Instance那么只能退化成传统的一个个画模型了,也就是一个模型一个DrawCall。

面数的话,常规发射器的面数只与你最多存活的粒子个数相关,飘带发射器的话,也与粒子个数相关,并且还与粒子间的面数相关,不过粒子间面数和最大粒子数这些都是可以控制的。

至于像素填充率,如果一个发射器发射的粒子有多而且面积又大,就会导致一个像素被渲染多次,像素填充率就高,手机上会吃不消。

其他

在自研引擎上,发现和虚幻3的视觉效果差距有些大,主要是色彩的鲜艳程度。相较之下我们的是有些暗的,后来发现是因为没有伽马矫正的问题,因为都在非线性空间做混合,所以才导致差距比较大。然后发现伽马矫正的后处理像素填充率太高,最终还是关了,毕竟是个全屏的shader。而且还发现R8G8B8A8存在精度问题,将图片转到线性空间后,也就是执行pow(2.2)之后,颜色就会变暗,这时因为R8G8B8A8的精度问题,值如果很小就会出现精度丢失变成黑色,就会出现一圈黑色光晕。类似于你用TeamViewer远程到其他电脑上,然后看锁屏界面一样效果,有很多光晕。

然后还发现虚幻3在粒子颜色过大时,会有曝光效果,一开始以为有什么黑科技还看了虚幻的shader。结果经人指导才知道那叫Bloom,而且还是HDRBloom,意味着需要用R16G16B16A16才存储渲染结果才行,如果用R8G8B8A8,颜色大于1.0的部分就丢失了,而HDRBloom就是将大于1.0的部分分散到周围的像素。然而R16G16B16A16这个功能需要手机支持ES3.0,网页支持webgl2.0才行。并且存在跟伽马矫正一样的问题,会导致手机像素填充率太高,因为也是全屏的

在美术指导下,知道他们基本只使用Translucent和Additive两种混合模式,两者的公式是。可以看出来Translucent就是传统的透明的融合因子,然后Additive这种叠加方式,决定了它如果颜色是黑色的话,它渲染完结果依旧是背景色,按他们的说法就是“去黑”只渲染非黑色的部分。

TranslucentColor = SrcColor * SrcAlpha + DestColor * (1 - SrcAlpha);
TranslucentAlpha = SrcAlpha + (1 - SrcAlpha) = 1;
AdditiveColor = SrcColor + DestColor;
AdditiveAlpha = SrcAlpha + DestAlpha;

 

结尾

后续其实想研究虚幻3的材质编辑器,看能否将虚幻3的材质以新引擎的shader格式导出,目的 是为了能支持之前积累的一些材质效果,做到真正地将虚幻3的粒子搬到自研引擎上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值