Unity 中实现粒子系统的 LOD

时间:2023-03-09 04:10:06
Unity 中实现粒子系统的 LOD

模型的 LOD 比较简单,直接使用 Unity 提供的组件 LODGroup 挂到模型物体上,然后分别指定不同 LOD 级别的 Renderer 即可。

LODGroup 并不是用距离来控制 LOD,而是用物体在屏幕上的显示范围的高度与屏幕高度的比值来决定物体使用哪一级 LOD。这相当于用物体在屏幕上的面积大小来决定 LOD,避免了不同大小的物体在相同距离上使用相同的 LOD 这一不合理的情况。

如果要提供允许玩家指定物体精细度显示范围的设置,则需要动态修改这个比值。

Unity 没有对粒子系统提供 LOD 功能。如果要对粒子系统做 LOD,我认为可以通过控制粒子的最大数量来实现 LOD,并且参照距离来决定使用哪一级 LOD(因为要计算粒子系统的包围盒,并不是一件容易的事,需要算上所有存活粒子的位置,反而增加运算量)。

粒子特效质量,可以简单的分 3 个质量级别:低中高,以供玩家在设置中选择。每个质量级别在各自定义的一段距离内使用各自定义的一个缩放系数,超出此距离的则线性衰减此系数。使用此系数缩小粒子的最大数量。

 // 3 个质量级别下各自最高 LOD 级别的距离百分比(粒子能以此 LOD 级别显示的距离百分比)

 float[] HIGH_LEVEL_DISTANCE_PERCENTS = { xxx, xxx, xxx }
 // 3 个质量级别下各自最高 LOD 级别的粒子数量缩放系数

 float[] HIGH_LEVEL_SCALES = { 0.25, 0.5, }

提供一个函数,它可以返回在指定效果质量下指定距离处的缩放系数:

 float getParticleCountScale(int level, float distPercent)  // distPercent 是个距离百分比,并不是绝对值
{
float highLevelDist = HIGH_LEVEL_DISTANCE_PERCENTS[level];
float highLevelScale = HIGH_LEVEL_SCALES[level];
if (distPercent <= highLevelDist)
{
return highLevelScale;
}
else
{
// 超出最高 LOD 距离的,线性衰减
float factor = ( - distPercent) / ( - highLevelDist);
float scale = highLevelScale * factor;
return scale;
}
}

将这个缩放系数与粒子在预制件中的原始最大数量相乘,得出的新的最大数量再设回给粒子系统即可。

然而美术在预制件里设的粒子的最大数量并不准确,很多时候会设一个粒子在生命周期内不可能达到的特别大的数字,目的只是为了他做效果可以有足够的施展空间。这就需要我们程序自己估算一下粒子的最大数量了。

我估算粒子最大数量的方法是:最大数量 = 生命周期 x 发射速度

Unity 的粒子系统,很多参数(比如这里要用的生命周期和发射速度)都可能是个曲线,这时挑选几个采样点,采样后取平均值吧,毕竟只是估算而已。

有了最大数量(别忘了乘上缩放系数),设回给粒子系统时会发现,maxParticles 其实是个只读属性,没关系,使用反射机制可以修改此值。

 // ps 是个 UnityEngine.ParticleSystem 对象
System.Type mainType = ps.main.GetType();
System.Reflection.PropertyInfo pi = mainType.GetProperty("maxParticles");
// newMaxCount 是我们计算出来的新的最大数量
pi.SetValue(ps.main, newMaxCount, null);