用粒子控制数千条鱼

:参考:`网格实例<类_网格实例>`的问题是,更新转换数组的代价很高。它非常适合在场景周围放置许多静态对象。但在场景中移动物体,仍然很困难。

To make each instance move in an interesting way, we will use a Particles node. Particles take advantage of GPU acceleration by computing and setting the per-instance information in a Shader.

注解

在GLES2中无法使用粒子,而是使用:参考:`CPU粒子<类_CPU粒子>`来替代,它和粒子一样的功能,但是不能使用GPU加速。

First create a Particles node. Then, under “Draw Passes” set the Particle’s “Draw Pass 1” to your Mesh. Then under “Process Material” create a new ShaderMaterial.

将“着色器_类型”设置为“粒子”。

  1. shader_type particles

Then add the following two functions:

  1. float rand_from_seed(in uint seed) {
  2. int k;
  3. int s = int(seed);
  4. if (s == 0)
  5. s = 305420679;
  6. k = s / 127773;
  7. s = 16807 * (s - k * 127773) - 2836 * k;
  8. if (s < 0)
  9. s += 2147483647;
  10. seed = uint(s);
  11. return float(seed % uint(65536)) / 65535.0;
  12. }
  13. uint hash(uint x) {
  14. x = ((x >> uint(16)) ^ x) * uint(73244475);
  15. x = ((x >> uint(16)) ^ x) * uint(73244475);
  16. x = (x >> uint(16)) ^ x;
  17. return x;
  18. }

这些函数来自默认值:参考:粒子材质<类_粒子材质>。它们用于从每个粒子的“随机_种子”生成随机数。

A unique thing about particle shaders is that some built-in variables are saved across frames. TRANSFORM, COLOR, and CUSTOM can all be accessed in the Spatial shader of the mesh, and also in the particle shader the next time it is run.

接下来,设置您的“顶点”函数。粒子着色器只包含一个顶点函数,不包含其他函数。

First we will distinguish between code that needs to be run only when the particle system starts and code that should always run. We want to give each fish a random position and a random animation offset when the system is first run. To do so, we wrap that code in an if statement that checks the built-in variable RESTART which becomes true for one frame when the particle system is restarted.

从高的级别来看,这看起来像:

  1. void vertex() {
  2. if (RESTART) {
  3. //Initialization code goes here
  4. } else {
  5. //per-frame code goes here
  6. }
  7. }

接下来,我们需要生成4个随机数:3个用于创建一个随机位置,1个用于游泳周期的随机偏移量。

首先,使用上面提供的“哈希”函数在“重新开始”块中生成4个种子:

  1. uint alt_seed1 = hash(NUMBER + uint(1) + RANDOM_SEED);
  2. uint alt_seed2 = hash(NUMBER + uint(27) + RANDOM_SEED);
  3. uint alt_seed3 = hash(NUMBER + uint(43) + RANDOM_SEED);
  4. uint alt_seed4 = hash(NUMBER + uint(111) + RANDOM_SEED);

然后,使用这些种子生成随机数,使用“随机_自_种子”:

  1. CUSTOM.x = rand_from_seed(alt_seed1);
  2. vec3 position = vec3(rand_from_seed(alt_seed2) * 2.0 - 1.0,
  3. rand_from_seed(alt_seed3) * 2.0 - 1.0,
  4. rand_from_seed(alt_seed4) * 2.0 - 1.0);

最后,将“位置”赋值给“TRANSFORM[3].xyz”,它是保存位置信息的转换的一部分。

  1. TRANSFORM[3].xyz = position * 20.0;

记住,到目前为止,所有这些代码都位于“重新开始”块中。

网格的顶点着色器,可以完全复用前一教程中的。

现在你可以单独移动每条鱼的每一帧,可以通过直接增加“变换”,或是通过编写“速度”。

让我们通过设置鱼的“速度”来改变它们。

  1. VELOCITY.z = 10.0;

这是设置“速度”的最基本方法,每个粒子(或鱼)都有相同的速度。

只要设置“速度”,你就可以让鱼自由游动。例如,尝试下面的代码。

  1. VELOCITY.z = cos(TIME + CUSTOM.x * 6.28) * 4.0 + 6.0;

这将给每条鱼一个在“2”和“10”之间的独特速度。

如果你在上节课中使用“自定义.y”,你也可以只使用“自定义.y”,根据“速度”设置游泳动画的速度。

  1. CUSTOM.y = VELOCITY.z * 0.1;

This code gives you the following behavior:

../../../_images/scene.gif

使用粒子材质,你可以让鱼的行为变得简单或复杂,随你喜欢。在本教程中,我们只设置了速度,但是在你自己的着色器中,你也可以设置“颜色”,旋转,缩放(通过“变换”)。有关粒子着色器的更多信息,请参考:参考:粒子着色器参考<文档_粒子_着色器>。