【Unity】关于Unity的Particle System

Posted by FlowingCrescent on 2021-11-14
Estimated Reading Time 3 Minutes
Words 795 In Total
Viewed Times

最近做了个有关Particle System的需求,发现Unity这Particle System实在奇葩,遂写篇简单的笔记记录一下。

根本不存在的“模型空间”

由于需求比较特殊,shader中设计一些模型空间的转换,但是我发现本来在普通物体上生效的shader,放在Particle System的Material中就失效了,查找了许久原因,才发现这Partcicle System根本不存在所谓的“模型空间”这一概念,因为它模型空间坐标值与世界空间坐标完全相等
比如我们做以下实验,在vertex shader中如此写:

1
2
3
4
VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS.xyz);
o.positionCS = positionInputs.positionCS;
o.positionOS.xyz = v.positionOS.xyz;
o.positionWS = positionInputs.positionWS;

然后在frag中直接输出positionOS与positionWS的差值的绝对值,并且乘以1000以增强效果。

1
2
3
4
half4 frag(v2f i): SV_Target
{
return float4(abs(i.positionOS.rgb - i.positionWS.xyz)*1000, 1);
}

然后这是输出结果:
image.png
显然positionOS与positionWS完全相等,或者说相差极小。
在renderdoc中也可发现POSITION与TEXCOORD5(即我设置的PositionWS)完全相等。
image.png
那么很显然了,Particle System根本不存在所谓“模型空间”的概念,甚至MVP变换中的Model变换都是装饰用……

CustomData

没有模型空间,那我们就需要自己构造模型空间,但我们还缺少一些数据,比如每个粒子的中心坐标以及粒子的旋转值。
Unity确实给我们提供了使用脚本给粒子传入数据到GPU端的方法,即Custom Data
image.png
在Particle System的面板上也可以看到,确实存在这个功能,只是最大也只能传两个矢量,也就是8个浮点数。
如果要直接传Model变换矩阵,那就需要4x4,即16个浮点数,显然是不够的。那就只能自己传一些关键数值,再导shader里边计算矩阵了。
传入数据的脚本可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private ParticleSystem particleSystem;
private List<Vector4> particlePositions= new List<Vector4>();
private List<Vector4> particleRotations= new List<Vector4>();

void Start()
{
particleSystem = GetComponent<ParticleSystem>();
particleSystem.customData.SetMode(ParticleSystemCustomData.Custom1, ParticleSystemCustomDataMode.Vector);
particleSystem.customData.SetMode(ParticleSystemCustomData.Custom2, ParticleSystemCustomDataMode.Vector);
}

void Update()
{
ParticleSystem.Particle[] particles = new ParticleSystem.Particle[particleSystem.particleCount];
particleSystem.GetParticles(particles);
particleSystem.GetCustomParticleData(particlePositions, ParticleSystemCustomData.Custom1);
particleSystem.GetCustomParticleData(particleRotations, ParticleSystemCustomData.Custom2);
for(int i = 0; i < particleSystem.particleCount; i++)
{
particlePositions[i] = particles[i].position;
particleRotations[i] = new Vector4(particles[i].rotation3D.x * Mathf.Deg2Rad, particles[i].rotation3D.y * Mathf.Deg2Rad, particles[i].rotation3D.z * Mathf.Deg2Rad, 1);
if(i == 0)
Debug.Log(particleRotations[i]);
}
particleSystem.SetCustomParticleData(particlePositions, ParticleSystemCustomData.Custom1);
particleSystem.SetCustomParticleData(particleRotations, ParticleSystemCustomData.Custom2);
}

遍历每个粒子,然后传入数据,总的来说逻辑还是很简单的。
注意这传入的position是粒子的中心位置(如果粒子的Simulation Space选择local,则这中心位置还是相对于粒子发射器的,选择world则输出世界空间的值),rotation3D则是欧拉角。

image.png
还需要设置一下传入的Custom Data在Vertex的哪个位置,如图我设置在了TEXCOORD0与TEXCOORD1,在顶点着色器中取用即可。

之后就是在shader中还原model(逆)矩阵了,这直接抄别人的文章就可以了,要注意每个轴的旋转先后顺序:
Unity中常用矩阵的推导

image.png
最终还原后直接输出模型空间坐标结果


感谢您阅读完本文,若您认为本文对您有所帮助,可以将其分享给其他人;若您发现文章对您的利益产生侵害,请联系作者进行删除。