最近做了个有关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); }
|
然后这是输出结果:
显然positionOS与positionWS完全相等,或者说相差极小。
在renderdoc中也可发现POSITION与TEXCOORD5(即我设置的PositionWS)完全相等。
那么很显然了,Particle System根本不存在所谓“模型空间”的概念,甚至MVP变换中的Model变换都是装饰用……
CustomData
没有模型空间,那我们就需要自己构造模型空间,但我们还缺少一些数据,比如每个粒子的中心坐标以及粒子的旋转值。
Unity确实给我们提供了使用脚本给粒子传入数据到GPU端的方法,即Custom Data
在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则是欧拉角。
还需要设置一下传入的Custom Data在Vertex的哪个位置,如图我设置在了TEXCOORD0与TEXCOORD1,在顶点着色器中取用即可。
之后就是在shader中还原model(逆)矩阵了,这直接抄别人的文章就可以了,要注意每个轴的旋转先后顺序:
Unity中常用矩阵的推导
最终还原后直接输出模型空间坐标结果
感谢您阅读完本文,若您认为本文对您有所帮助,可以将其分享给其他人;若您发现文章对您的利益产生侵害,请联系作者进行删除。