Controlling thousands of fish with Particles
MeshInstances is that it is expensive to update their transform array. It is great for placing many static objects around the scene. But it is still difficult to move the objects around the scene.
Particles node. Particles take advantage of GPU acceleration by computing and setting the per-instance information in a Shader.
Note
CPUParticles, which do the same thing as Particles, but do not benefit from GPU acceleration.
Mesh. Then under “Process Material” create a new ShaderMaterial.
shader_type to particles.
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;}
ParticlesMaterial. They are used to generate a random number from each particle’s RANDOM_SEED.
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.
vertex function. Particles shaders only contain a vertex function and no others.
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:
void vertex() { if (RESTART) { //Initialization code goes here } else { //per-frame code goes here }}
Next, we need to generate 4 random numbers: 3 to create a random position and one for the random offset of the swim cycle.
RESTART block using the hash function provided above:
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);
rand_from_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);
position to TRANSFORM[3].xyz, which is the part of the transform that holds the position information.
TRANSFORM[3].xyz = position * 20.0;
RESTART block.
The vertex shader for your mesh can stay the exact same as it was in the previous tutorial.
TRANSFORM directly or by writing to VELOCITY.
VELOCITY.
VELOCITY.z = 10.0;
VELOCITY every particle (or fish) will have the same velocity.
VELOCITY you can make the fish swim however you want. For example, try the code below.
VELOCITY.z = cos(TIME + CUSTOM.x * 6.28) * 4.0 + 6.0;
2 and 10.
CUSTOM.y in the last tutorial, you can also set the speed of the swim animation based on the VELOCITY. Just use CUSTOM.y.
CUSTOM.y = VELOCITY.z * 0.1;
This code gives you the following behavior:
COLOR, rotation, scale (through TRANSFORM). Please refer to the Particles Shader Reference for more information on particle shaders.
