Controlling thousands of fish with Particles

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

    Note

    Particles are not available in GLES2, instead use , which do the same thing as Particles, but do not benefit from GPU acceleration.

    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 .

    Set the to 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. }
    12. uint hash(uint x) {
    13. x = ((x >> uint(16)) ^ x) * uint(73244475);
    14. x = ((x >> uint(16)) ^ x) * uint(73244475);
    15. x = (x >> uint(16)) ^ x;
    16. return x;

    These functions come from the default ParticlesMaterial. They are used to generate a random number from each particle’s RANDOM_SEED.

    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.

    From a high level, this looks like:

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

    Next, we need to generate 4 random numbers: 3 to create a random position and one for the random offset of the swim cycle.

    First, generate 4 seeds inside the RESTART block using the hash function provided above:

    Then, use those seeds to generate random numbers using rand_from_seed:

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

    Finally, assign position to TRANSFORM[3].xyz, which is the part of the transform that holds the position information.

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

    Remember, all this code so far goes inside the RESTART block.

    The vertex shader for your mesh can stay the exact same as it was in the previous tutorial.

    Let’s transform the fish by setting their VELOCITY.

    This is the most basic way to set VELOCITY every particle (or fish) will have the same velocity.

    Just by setting VELOCITY you can make the fish swim however you want. For example, try the code below.

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

    This will give each fish a unique speed between 2 and 10.

    If you used CUSTOM.y in the last tutorial, you can also set the speed of the swim animation based on the VELOCITY. Just use CUSTOM.y.

      This code gives you the following behavior:

      Using a ParticlesMaterial you can make the fish behavior as simple or complex as you like. In this tutorial we only set Velocity, but in your own Shaders you can also set COLOR, rotation, scale (through ). Please refer to the for more information on particle shaders.