パーティクルで数千の魚を制御する

MeshInstances の問題は、transform配列を更新するのにコストがかかることです。シーンの周囲に多くの静的オブジェクトを配置するのには最適です。ただし、オブジェクトをシーン内で移動することは依然として困難です。

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.

注釈

ParticlesはGLES2では使用できません。代わりに CPUParticles を使用します。これはParticlesと同じことを行いますが、GPUアクセラレーションの恩恵は受けません。

最初にParticlesノードを作成します。次に、Draw Passes の下で、パーティクルの Draw Pass 1Mesh に設定します。次に、[Process Material]の下に新しい ShaderMaterial を作成します。

shader_typeparticles に設定します。

shader_type particles

続いて、次の2つの関数を追加します:

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;
}

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.

次に、vertex 関数をセットアップします。パーティクルシェーダーには頂点関数のみが含まれ、その他は含まれません。

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.

大まかに言えば、次のようになります:

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

次に、4つの乱数を生成する必要があります。3つはランダムな位置を作成し、1つは遊泳サイクルのランダムなオフセットを作成します。

まず、上記の hash 関数を使用して RESTART ブロック内に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);

次に、これらのシードを使用して、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);

最後に、位置情報を保持するtansformの一部である TRANSFORM[3].xyzposition を割り当てます。

TRANSFORM[3].xyz = position * 20.0;

これまでのこのコードはすべて、RESTART ブロックの内部にあることに注意してください。

メッシュの頂点シェーダーは、前のチュートリアルとまったく同じままにすることができます。

これで、TRANSFORM に直接追加するか、VELOCITY に書き込むことで、各魚をフレームごとに個別に移動できます。

魚の VELOCITY を設定して魚を変身させましょう。

VELOCITY.z = 10.0;

これは、すべてのパーティクル(または魚)が同じ速度を持つように VELOCITY を設定する最も基本的な方法です。

VELOCITY を設定するだけで、好きなように魚を泳がせることができます。たとえば、次のコードを試してください。

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

これにより、各魚に 210 の間のユニークな速度が与えられます。

前回のチュートリアルで CUSTOM.y を使用した場合、VELOCITY に基づいて遊泳アニメーションの速度を設定することもできます。CUSTOM.y を使用してください。

CUSTOM.y = VELOCITY.z * 0.1;

このコードは、次の動作を提供します:

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

ParticlesMaterialを使用すると、魚の動作を好きなだけ単純または複雑にすることができます。このチュートリアルでは、Velocityのみを設定しますが、独自のシェーダーでは、COLOR、回転、スケール(TRANSFORM を使用)も設定できます。パーティクルシェーダーの詳細については、パーティクルシェーダーリファレンス を参照してください。