animating_thousands_of_fish.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. :article_outdated: True
  2. .. _doc_animating_thousands_of_fish:
  3. Animating thousands of fish with MultiMeshInstance3D
  4. ====================================================
  5. This tutorial explores a technique used in the game `ABZU <https://www.gdcvault.com/play/1024409/Creating-the-Art-of-ABZ>`_
  6. for rendering and animating thousands of fish using vertex animation and
  7. static mesh instancing.
  8. In Godot, this can be accomplished with a custom :ref:`Shader <class_Shader>` and
  9. a :ref:`MultiMeshInstance3D <class_MultiMeshInstance3D>`. Using the following technique you
  10. can render thousands of animated objects, even on low end hardware.
  11. We will start by animating one fish. Then, we will see how to extend that animation to
  12. thousands of fish.
  13. Animating one Fish
  14. ------------------
  15. We will start with a single fish. Load your fish model into a :ref:`MeshInstance3D <class_MeshInstance3D>`
  16. and add a new :ref:`ShaderMaterial <class_ShaderMaterial>`.
  17. Here is the fish we will be using for the example images, you can use any fish model you like.
  18. .. image:: img/fish.png
  19. .. note:: The fish model in this tutorial is made by `QuaterniusDev <https://quaternius.com>`_ and is
  20. shared with a creative commons license. CC0 1.0 Universal (CC0 1.0) Public Domain
  21. Dedication https://creativecommons.org/publicdomain/zero/1.0/
  22. Typically, you would use bones and a :ref:`Skeleton3D <class_Skeleton3D>` to animate objects. However,
  23. bones are animated on the CPU and so you end having to calculate thousands of operations every
  24. frame and it becomes impossible to have thousands of objects. Using vertex animation in a vertex
  25. shader, you avoid using bones and can instead calculate the full animation in a few lines of code
  26. and completely on the GPU.
  27. The animation will be made of four key motions:
  28. 1. A side to side motion
  29. 2. A pivot motion around the center of the fish
  30. 3. A panning wave motion
  31. 4. A panning twist motion
  32. All the code for the animation will be in the vertex shader with uniforms controlling the amount of motion.
  33. We use uniforms to control the strength of the motion so that you can tweak the animation in editor and see the
  34. results in real time, without the shader having to recompile.
  35. All the motions will be made using cosine waves applied to ``VERTEX`` in model space. We want the vertices to
  36. be in model space so that the motion is always relative to the orientation of the fish. For example, side-to-side
  37. will always move the fish back and forth in its left to right direction, instead of on the ``x`` axis in the
  38. world orientation.
  39. In order to control the speed of the animation, we will start by defining our own time variable using ``TIME``.
  40. .. code-block:: glsl
  41. //time_scale is a uniform float
  42. float time = TIME * time_scale;
  43. The first motion we will implement is the side to side motion. It can be made by offsetting ``VERTEX.x`` by
  44. ``cos`` of ``TIME``. Each time the mesh is rendered, all the vertices will move to the side by the amount
  45. of ``cos(time)``.
  46. .. code-block:: glsl
  47. //side_to_side is a uniform float
  48. VERTEX.x += cos(time) * side_to_side;
  49. The resulting animation should look something like this:
  50. .. image:: img/sidetoside.gif
  51. Next, we add the pivot. Because the fish is centered at (0, 0), all we have to do is multiply ``VERTEX`` by a
  52. rotation matrix for it to rotate around the center of the fish.
  53. We construct a rotation matrix like so:
  54. .. code-block:: glsl
  55. //angle is scaled by 0.1 so that the fish only pivots and doesn't rotate all the way around
  56. //pivot is a uniform float
  57. float pivot_angle = cos(time) * 0.1 * pivot;
  58. mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle)));
  59. And then we apply it in the ``x`` and ``z`` axes by multiplying it by ``VERTEX.xz``.
  60. .. code-block:: glsl
  61. VERTEX.xz = rotation_matrix * VERTEX.xz;
  62. With only the pivot applied you should see something like this:
  63. .. image:: img/pivot.gif
  64. The next two motions need to pan down the spine of the fish. For that, we need a new variable, ``body``.
  65. ``body`` is a float that is ``0`` at the tail of the fish and ``1`` at its head.
  66. .. code-block:: glsl
  67. float body = (VERTEX.z + 1.0) / 2.0; //for a fish centered at (0, 0) with a length of 2
  68. The next motion is a cosine wave that moves down the length of the fish. To make
  69. it move along the spine of the fish, we offset the input to ``cos`` by the position
  70. along the spine, which is the variable we defined above, ``body``.
  71. .. code-block:: glsl
  72. //wave is a uniform float
  73. VERTEX.x += cos(time + body) * wave;
  74. This looks very similar to the side to side motion we defined above, but in this one, by
  75. using ``body`` to offset ``cos`` each vertex along the spine has a different position in
  76. the wave making it look like a wave is moving along the fish.
  77. .. image:: img/wave.gif
  78. The last motion is the twist, which is a panning roll along the spine. Similarly to the pivot,
  79. we first construct a rotation matrix.
  80. .. code-block:: glsl
  81. //twist is a uniform float
  82. float twist_angle = cos(time + body) * 0.3 * twist;
  83. mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle)));
  84. We apply the rotation in the ``xy`` axes so that the fish appears to roll around its spine. For
  85. this to work, the fish's spine needs to be centered on the ``z`` axis.
  86. .. code-block:: glsl
  87. VERTEX.xy = twist_matrix * VERTEX.xy;
  88. Here is the fish with twist applied:
  89. .. image:: img/twist.gif
  90. If we apply all these motions one after another, we get a fluid jelly-like motion.
  91. .. image:: img/all_motions.gif
  92. Normal fish swim mostly with the back half of their body. Accordingly, we need to limit the
  93. panning motions to the back half of the fish. To do this, we create a new variable, ``mask``.
  94. ``mask`` is a float that goes from ``0`` at the front of the fish to ``1`` at the end using
  95. ``smoothstep`` to control the point at which the transition from ``0`` to ``1`` happens.
  96. .. code-block:: glsl
  97. //mask_black and mask_white are uniforms
  98. float mask = smoothstep(mask_black, mask_white, 1.0 - body);
  99. Below is an image of the fish with ``mask`` used as ``COLOR``:
  100. .. image:: img/mask.png
  101. For the wave, we multiply the motion by ``mask`` which will limit it to the back half.
  102. .. code-block:: glsl
  103. //wave motion with mask
  104. VERTEX.x += cos(time + body) * mask * wave;
  105. In order to apply the mask to the twist, we use ``mix``. ``mix`` allows us to mix the
  106. vertex position between a fully rotated vertex and one that is not rotated. We need to
  107. use ``mix`` instead of multiplying ``mask`` by the rotated ``VERTEX`` because we are not
  108. adding the motion to the ``VERTEX`` we are replacing the ``VERTEX`` with the rotated
  109. version. If we multiplied that by ``mask``, we would shrink the fish.
  110. .. code-block:: glsl
  111. //twist motion with mask
  112. VERTEX.xy = mix(VERTEX.xy, twist_matrix * VERTEX.xy, mask);
  113. Putting the four motions together gives us the final animation.
  114. .. image:: img/all_motions_mask.gif
  115. Go ahead and play with the uniforms in order to alter the swim cycle of the fish. You will
  116. find that you can create a wide variety of swim styles using these four motions.
  117. Making a school of fish
  118. -----------------------
  119. Godot makes it easy to render thousands of the same object using a MultiMeshInstance3D node.
  120. A MultiMeshInstance3D node is created and used the same way you would make a MeshInstance3D node.
  121. For this tutorial, we will name the MultiMeshInstance3D node ``School``, because it will contain
  122. a school of fish.
  123. Once you have a MultiMeshInstance3D add a :ref:`MultiMesh <class_MultiMesh>`, and to that
  124. MultiMesh add your :ref:`Mesh <class_Mesh>` with the shader from above.
  125. MultiMeshes draw your Mesh with three additional per-instance properties: Transform (rotation,
  126. translation, scale), Color, and Custom. Custom is used to pass in 4 multi-use variables using
  127. a :ref:`Color <class_Color>`.
  128. ``instance_count`` specifies how many instances of the mesh you want to draw. For now, leave
  129. ``instance_count`` at ``0`` because you cannot change any of the other parameters while
  130. ``instance_count`` is larger than ``0``. We will set ``instance count`` in GDScript later.
  131. ``transform_format`` specifies whether the transforms used are 3D or 2D. For this tutorial, select 3D.
  132. For both ``color_format`` and ``custom_data_format`` you can choose between ``None``, ``Byte``, and
  133. ``Float``. ``None`` means you won't be passing in that data (either a per-instance ``COLOR`` variable,
  134. or ``INSTANCE_CUSTOM``) to the shader. ``Byte`` means each number making up the color you pass in will
  135. be stored with 8 bits while ``Float`` means each number will be stored in a floating-point number
  136. (32 bits). ``Float`` is slower but more precise, ``Byte`` will take less memory and be faster, but you
  137. may see some visual artifacts.
  138. Now, set ``instance_count`` to the number of fish you want to have.
  139. Next we need to set the per-instance transforms.
  140. There are two ways to set per-instance transforms for MultiMeshes. The first is entirely in editor
  141. and is described in the :ref:`MultiMeshInstance3D tutorial <doc_using_multi_mesh_instance>`.
  142. The second is to loop over all the instances and set their transforms in code. Below, we use GDScript
  143. to loop over all the instances and set their transform to a random position.
  144. ::
  145. for i in range($School.multimesh.instance_count):
  146. var position = Transform3D()
  147. position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25))
  148. $School.multimesh.set_instance_transform(i, position)
  149. Running this script will place the fish in random positions in a box around the position of the
  150. MultiMeshInstance3D.
  151. .. note:: If performance is an issue for you, try running the scene with fewer fish.
  152. Notice how all the fish are all in the same position in their swim cycle? It makes them look very
  153. robotic. The next step is to give each fish a different position in the swim cycle so the entire
  154. school looks more organic.
  155. Animating a school of fish
  156. --------------------------
  157. One of the benefits of animating the fish using ``cos`` functions is that they are animated with
  158. one parameter, ``time``. In order to give each fish a unique position in the
  159. swim cycle, we only need to offset ``time``.
  160. We do that by adding the per-instance custom value ``INSTANCE_CUSTOM`` to ``time``.
  161. .. code-block:: glsl
  162. float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
  163. Next, we need to pass a value into ``INSTANCE_CUSTOM``. We do that by adding one line into
  164. the ``for`` loop from above. In the ``for`` loop we assign each instance a set of four
  165. random floats to use.
  166. ::
  167. $School.multimesh.set_instance_custom_data(i, Color(randf(), randf(), randf(), randf()))
  168. Now the fish all have unique positions in the swim cycle. You can give them a little more
  169. individuality by using ``INSTANCE_CUSTOM`` to make them swim faster or slower by multiplying
  170. by ``TIME``.
  171. .. code-block:: glsl
  172. //set speed from 50% - 150% of regular speed
  173. float time = (TIME * (0.5 + INSTANCE_CUSTOM.y) * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
  174. You can even experiment with changing the per-instance color the same way you changed the per-instance
  175. custom value.
  176. One problem that you will run into at this point is that the fish are animated, but they are not
  177. moving. You can move them by updating the per-instance transform for each fish every frame. Although
  178. doing so will be faster than moving thousands of MeshInstance3Ds per frame, it'll still likely be
  179. slow.
  180. In the next tutorial we will cover how to use :ref:`GPUParticles3D <class_GPUParticles3D>` to take advantage
  181. of the GPU and move each fish around individually while still receiving the benefits of instancing.