之前用粒子系统基于原有萤火虫的粒子改了一波慢萤火效果就被惊艳到了,开始大家讨论,就都觉得这样大数量的粒子消耗挺大的,后面测试过才发现单纯的粒子系统在总粒子数量3000,每秒300的生成数量,屏幕呈现有1000多个粒子的时候, 在华为P30也能稳定维持FPS在60帧左右。可能unity后面做了优化。
思路
首先要确定的是在范围内绘制出流动和显隐, 这个功能是很多其他功能的基础,例如雪花,落叶,落花,战火等,扩展效果 一般是添加一些随机翻转,缩放,替换贴图等。 应用范围比较广。
尝试思路
c#能传入一个世界坐标,或者在shader里面找到模型内某个点的坐标,shader中深度图转换坐标后对应上这个位置的将其绘制出来。后来觉得这个思路不太正确。
-
因为深度转只能在给定的深度转出世界坐标,当某些部分的深度本身代表的区域不在cube内部的时候,转出的世界坐标也不在cube内部,传入的世界坐标都是在cube内部,那么这个区域就成了一个deadzone, 任何点都不会在这里出现。 点在运动图中因为从非deadzone走入deadzone而导致相应位置转不出相应坐标而出现的闪烁问题。这样只能在cube范围比较大的时候使用,deadzone与非deadzone夹杂,非deadzone足够多 ,表现才可能不太违和。
-
基于点去在shader中绘制一定范围的以该点为圆心的圆形区域,再以此采样贴图去表现,也是实现起来坑比较多的。主要是采样贴图这个部分,圆形区域内的所有uv不能确保全部计算正确,采样贴图注定做不很好,表现效果就不太正常。
-
就算上面的问题解决了,屏幕大范围的深度图采样和重建世界坐标消耗是比较大的,移动端会比较影响性能,严重一些就是移动端会因此砍掉这个。
所以这个方向做了一部分就停止了
最后思路
还是采用中规中矩的C#生成mesh,shader进行运动和细致表现的方法,能够轻松表现萤火与普通物体的互相遮挡的关系和空间感。 在c#中给出同一位置的四个顶点,分配完整的uv,在shader中连成由两个三角形组成的矩形,最后将所有的三角形组合成一个mesh。耗时的工作在GPU中做。
正方形面片的生成
在shader中根据顶点对应的UV,将顶点局部坐标位置以自身运动坐标为基础, 摄像机up轴和right轴为方向的扩展。
随机运动
使用柏林噪声函数,初始的模型坐标乘以时间变量和速度变化量,然后随机选取两个分量生成噪声,噪声用于位置偏移xyz,偏移可以乘以一个放大值表示路径的扩大程度。最后顶点原始位置加上这个偏移量,因为柏林噪声只要输入值是连续的,结果值就是连续的。输入是time的变种,总体来说是连续的,结果值就不会出现突变导致位置突变。
边界显隐
判断世界坐标xyz是否到了边界,取xyz分量离世界坐标边界的最小值,到了阈值范围就进行透明度变化
边界控制
运动后的粒子如果某个维度超过了边界值,就该维度对边界值取余,取余结果加上反向边界重新赋值给该维度。表现上就是在左边界飘出去的萤火,会在右边界飘进来。
问题
距离和速度的计算分离
每个面片的运动是独立的,那么思路在shader是每个面片根据自身初始的局部坐标位置来与时间做运算,得到不同的实时随机噪声,用这个噪声来扩大一定的倍数代表xyz方向上的运动路径。c#中不能得到每个面片的路径。统一传入到shader中的也是类似于time的一个变量。 干脆就用time变量了。
太久没写shader了,距离和速度的计算分离开始时候没有想清楚
perlin_noise
这个函数类似于用输入变量作为uv去采样柏林噪声图。
因此输入变量的变化快慢决定了结果值的变化快慢。
所以当显隐速度过快的时候,可以在输入哪里乘以小数减小变化量。
透明度
Blend One One 本身是当前颜色和缓冲区颜色直接叠加的方式进行变亮的。在不进行透明度调节的时候,可以表现出萤火的高光。但是在透明度调节的时候,其本身的含义就决定了透明度不会影响到表现效果。即不会有隐现效果。
位置的计算
M矩阵转换的时候,注意四维向量右乘四维矩阵,M的第四列前三行代表的是物体在世界坐标系的位移。如果用三维向量去乘,那么unity会自动补齐第四位为0,得到的结果将会是忽略了位移的模型空间到世界空间的变换,所以和M矩阵计算的时候,模型坐标系位置向量的第四位应该是1。
缩放的影响
边界值是世界单位的,顶点在模型坐标系中进行了位移计算后,转到裁剪空间的过程中,M矩阵包含了物体的缩放。
位移后的顶点会以相同的缩放进行放大,原来的边界就不足以囊括所有的顶点了。所以边界值要做对应维度的缩放。举个例子x方向最大维度是f,则x方向最大的边距是x+f,在x方向是1倍的时候,能包住所有粒子。 但是x方向扩大了三倍,为了保证x方向的边距能够包含所有的粒子,x方向最大的边距是x+3f。
工程链接
Unity 非后处理简单萤火效果
过程中有一些中间版本有点有趣,备份了一下。