用粒子控制数千条鱼

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

让每个实例以有趣的方式移动,我们将使用:参考:`粒子<类_粒子>`节点。粒子利用GPU加速计算,以及在:参考:`着色器<类_着色器>`中每个实例的设置信息。

注解

在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.

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

shader_type particles

Then add the following two functions:

float rand_from_seed(in uint seed) {
  int k;
  int s = int(seed);
  if (s == 0)
    s = 305420679;
  k = s / 127773;
  s = 16807 * (s - k * 127773) - 2836 * k;
  if (s < 0)
    s += 2147483647;
  seed = uint(s);
  return float(seed % uint(65536)) / 65535.0;
}

uint hash(uint x) {
  x = ((x >> uint(16)) ^ x) * uint(73244475);
  x = ((x >> uint(16)) ^ x) * uint(73244475);
  x = (x >> uint(16)) ^ x;
  return x;
}

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

粒子着色器的一个独特之处在于,一些内置变量是跨帧保存的。“变换”、“颜色”和“自定义”都可以在网格的空间着色器中访问,也在粒子着色器的下一次运行。

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

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 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.

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

void vertex() {
  if (RESTART) {
    //Initialization code goes here
  } else {
    //per-frame code goes here
  }
}

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

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

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

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

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

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

TRANSFORM[3].xyz = position * 20.0;

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

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

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

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

VELOCITY.z = 10.0;

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

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

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

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

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

CUSTOM.y = VELOCITY.z * 0.1;

This code gives you the following behavior:

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

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