your_first_spatial_shader.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. .. _doc_your_first_spatial_shader:
  2. Your first Spatial shader
  3. ============================
  4. You have decided to start writing your own custom Spatial shader. Maybe you saw a cool trick
  5. online that was done with shaders, or you have found that the
  6. :ref:`StandardMaterial3D <class_StandardMaterial3D>` isn't quite meeting your needs. Either way,
  7. you have decided to write your own and now you need figure out where to start.
  8. This tutorial will explain how to write a Spatial shader and will cover more topics than the
  9. :ref:`CanvasItem <doc_your_first_canvasitem_shader>` tutorial.
  10. Spatial shaders have more built-in functionality than CanvasItem shaders. The expectation with
  11. spatial shaders is that Godot has already provided the functionality for common use cases and all
  12. the user needs to do in the shader is set the proper parameters. This is especially true for a
  13. PBR (physically based rendering) workflow.
  14. This is a two-part tutorial. In this first part we are going to go through how to make a simple terrain
  15. using vertex displacement from a heightmap in the vertex function. In the :ref:`second part <doc_your_second_spatial_shader>`
  16. we are going to take the concepts from this tutorial and walk through how to set up custom materials
  17. in a fragment shader by writing an ocean water shader.
  18. .. note:: This tutorial assumes some basic shader knowledge such as types (``vec2``, ``float``,
  19. ``sampler2D``), and functions. If you are uncomfortable with these concepts it is
  20. best to get a gentle introduction from `The Book of Shaders
  21. <https://thebookofshaders.com>`_ before completing this tutorial.
  22. Where to assign my material
  23. ---------------------------
  24. In 3D, objects are drawn using :ref:`Meshes <class_Mesh>`. Meshes are a resource type that store geometry
  25. (the shape of your object) and materials (the color and how the object reacts to light) in units called
  26. "surfaces". A Mesh can have multiple surfaces, or just one. Typically, you would
  27. import a mesh from another program (e.g. Blender). But Godot also has a few :ref:`PrimitiveMeshes <class_primitivemesh>`
  28. that allow you to add basic geometry to a scene without importing Meshes.
  29. There are multiple node types that you can use to draw a mesh. The main one is :ref:`MeshInstance <class_meshinstance>`,
  30. but you can also use :ref:`Particles <class_particles>`, :ref:`MultiMeshes <class_MultiMesh>` (with a
  31. :ref:`MultiMeshInstance <class_multimeshinstance>`), or others.
  32. Typically, a material is associated with a given surface in a mesh, but some nodes, like MeshInstance, allow
  33. you to override the material for a specific surface, or for all surfaces.
  34. If you set a material on the surface or mesh itself, then all MeshInstances that share that mesh will share that material.
  35. However, if you want to reuse the same mesh across multiple mesh instances, but have different materials for each
  36. instance then you should set the material on the Meshinstance.
  37. For this tutorial we will set our material on the mesh itself rather than taking advantage of the MeshInstance's
  38. ability to override materials.
  39. Setting up
  40. ----------
  41. Add a new :ref:`MeshInstance <class_meshinstance>` node to your scene.
  42. In the inspector tab beside "Mesh" click "[empty]" and select "New PlaneMesh".
  43. Then click on the image of a plane that appears.
  44. This adds a :ref:`PlaneMesh <class_planemesh>` to our scene.
  45. Then, in the viewport, click in the upper left corner on the button that says "Perspective".
  46. A menu will appear. In the middle of the menu are options for how to display the scene.
  47. Select 'Display Wireframe'.
  48. This will allow you to see the triangles making up the plane.
  49. .. image:: img/plane.png
  50. Now set ``Subdivide Width`` and ``Subdivide Depth`` to ``32``.
  51. .. image:: img/plane-sub-set.png
  52. You can see that there are now many more triangles in the :ref:`Mesh<class_MeshInstance>`. This will give
  53. us more vertices to work with and thus allow us to add more detail.
  54. .. image:: img/plane-sub.png
  55. :ref:`PrimitiveMeshes <class_primitivemesh>`, like PlaneMesh, only have one surface, so instead of
  56. an array of materials there is only one. Click beside "Material" where it says "[empty]" and
  57. select "New ShaderMaterial". Then click the sphere that appears.
  58. Now click beside "Shader" where it says "[empty]" and select "New Shader".
  59. The shader editor should now pop up and you are ready to begin writing your first Spatial shader!
  60. Shader magic
  61. ------------
  62. .. image:: img/shader-error.png
  63. Notice how there is already error? This is because the shader editor reloads shaders on
  64. the fly. The first thing Godot shaders need is a declaration of what type of shader they are.
  65. We set the variable ``shader_type`` to ``spatial`` because this is a spatial shader.
  66. .. code-block:: glsl
  67. shader_type spatial;
  68. Next we will define the ``vertex()`` function. The ``vertex()`` function determines where
  69. the vertices of your :ref:`Mesh<class_MeshInstance>` appear in the final scene. We will be
  70. using it to offset the height of each vertex and make our flat plane appear like a little terrain.
  71. We define the vertex shader like so:
  72. .. code-block:: glsl
  73. void vertex() {
  74. }
  75. With nothing in the ``vertex()`` function, Godot will use its default vertex shader. We can easily
  76. start to make changes by adding a single line:
  77. .. code-block:: glsl
  78. void vertex() {
  79. VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z);
  80. }
  81. Adding this line, you should get an image like the one below.
  82. .. image:: img/cos.png
  83. Okay, let's unpack this. The ``y`` value of the ``VERTEX`` is being increased. And we are passing
  84. the ``x`` and ``z`` components of the ``VERTEX`` as arguments to ``cos`` and ``sin``; that gives us
  85. a wave-like appearance across the ``x`` and ``z`` axes.
  86. What we want to achieve is the look of little hills; after all. ``cos`` and ``sin`` already look kind of like
  87. hills. We do so by scaling the inputs to the ``cos`` and ``sin`` functions.
  88. .. code-block:: glsl
  89. void vertex() {
  90. VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0);
  91. }
  92. .. image:: img/cos4.png
  93. This looks better, but it is still too spiky and repetitive, let's make it a little more interesting.
  94. Noise heightmap
  95. ---------------
  96. Noise is a very popular tool for faking the look of terrain. Think of it as similar to the cosine function
  97. where you have repeating hills except, with noise, each hill has a different height.
  98. Godot provides the :ref:`NoiseTexture <class_noisetexture>` resource for generating a noise texture
  99. that can be accessed from a shader.
  100. To access a texture in a shader add the following code near the top of your shader, outside the
  101. ``vertex()`` function.
  102. .. code-block:: glsl
  103. uniform sampler2D noise;
  104. This will allow you to send a noise texture to the shader. Now look in the inspecter under your material.
  105. You should see a section called "Shader Params". If you open it up, you'll see a section called "noise".
  106. Click beside it where it says "[empty]" and select "New NoiseTexture". Then in your NoiseTexture click beside
  107. where it says "Noise" and select "New OpenSimplexNoise".
  108. :ref:`OpenSimplexNoise <class_opensimplexnoise>` is used by the NoiseTexture to generate a heightmap.
  109. Once you set it up and should look like this.
  110. .. image:: img/noise-set.png
  111. Now, access the noise texture using the ``texture()`` function. ``texture()`` takes a texture as the first
  112. argument and a ``vec2`` for the position on the texture as the second argument. We use the ``x`` and ``z``
  113. channels of ``VERTEX`` to determine where on the texture to look up. Note that the PlaneMesh coordinates are
  114. within the [-1,1] range (for a size of 2), while the texture coordinates are within [0,1], so to normalize
  115. we divide by the size of the PlaneMesh 2.0 and add 0.5. ``texture()`` returns a ``vec4`` of the
  116. ``r, g, b, a`` channels at the position. Since the noise texture is grayscale, all of the values are the same,
  117. so we can use any one of the channels as the height. In this case we'll use the ``r``, or ``x`` channel.
  118. .. code-block:: glsl
  119. float height = texture(noise, VERTEX.xz / 2.0 + 0.5).x;
  120. VERTEX.y += height;
  121. Note: ``xyzw`` is the same as ``rgba`` in GLSL, so instead of ``texture().x`` above, we could use ``texture().r``.
  122. See the `OpenGL documentation <https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors>`_ for more details.
  123. Using this code you can see the texture creates random looking hills.
  124. .. image:: img/noise.png
  125. Right now it is too spiky, we want to soften the hills a bit. To do that, we will use a uniform.
  126. You already used a uniform above to pass in the noise texture, now let's learn how they work.
  127. Uniforms
  128. --------
  129. Uniform variables allow you to pass data from the game into the shader. They are
  130. very useful for controlling shader effects. Uniforms can be almost any
  131. datatype that can be used in the shader. To use a uniform, you declare it in
  132. your :ref:`Shader<class_Shader>` using the keyword ``uniform``.
  133. Let's make a uniform that changes the height of the terrain.
  134. .. code-block:: glsl
  135. uniform float height_scale = 0.5;
  136. Godot lets you initialize a uniform with a value; here, ``height_scale`` is set to
  137. ``0.5``. You can set uniforms from GDScript by calling the function ``set_shader_param()``
  138. on the material corresponding to the shader. The value passed from GDScript takes
  139. precedence over the value used to initialize it in the shader.
  140. ::
  141. # called from the MeshInstance
  142. mesh.material.set_shader_param("height_scale", 0.5)
  143. .. note:: Changing uniforms in Spatial-based nodes is different from CanvasItem-based nodes. Here,
  144. we set the material inside the PlaneMesh resource. In other mesh resources you may
  145. need to first access the material by calling ``surface_get_material()``. While in
  146. the MeshInstance you would access the material using ``get_surface_material()`` or
  147. ``material_override``.
  148. Remember that the string passed into ``set_shader_param()`` must match the name
  149. of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the uniform variable anywhere
  150. inside your :ref:`Shader<class_Shader>`. Here, we will use it to set the height value instead
  151. of arbitrarily multiplying by ``0.5``.
  152. .. code-block:: glsl
  153. VERTEX.y += height * height_scale;
  154. Now it looks much better.
  155. .. image:: img/noise-low.png
  156. Using uniforms, we can even change the value every frame to animate the height of the terrain.
  157. Combined with :ref:`Tweens <class_Tween>`, this can be especially useful for simple animations.
  158. Interacting with light
  159. ----------------------
  160. First, turn wireframe off. To do so, click in the upper-left of the Viewport again, where it says
  161. "Perspective", and select "Display Normal".
  162. .. image:: img/normal.png
  163. Note how the mesh color goes flat. This is because the lighting on it is flat. Let's add a light!
  164. First, we will add an :ref:`OmniLight<class_OmniLight>` to the scene.
  165. .. image:: img/light.png
  166. You can see the light affecting the terrain, but it looks odd. The problem is the light
  167. is affecting the terrain as if it were a flat plane. This is because the light shader uses
  168. the normals from the :ref:`Mesh <class_mesh>` to calculate light.
  169. The normals are stored in the Mesh, but we are changing the shape of the Mesh in the
  170. shader, so the normals are no longer correct. To fix this, we can recalculate the normals
  171. in the shader or use a normal texture that corresponds to our noise. Godot makes both easy for us.
  172. You can calculate the new normal manually in the vertex function and then just set ``NORMAL``.
  173. With ``NORMAL`` set, Godot will do all the difficult lighting calculations for us. We will cover
  174. this method in the next part of this tutorial, for now we will read normals from a texture.
  175. Instead we will rely on the NoiseTexture again to calculate normals for us. We do that by passing in
  176. a second noise texture.
  177. .. code-block:: glsl
  178. uniform sampler2D normalmap;
  179. Set this second uniform texture to another NoiseTexture with another OpenSimplexNoise. But this time, check
  180. off "As Normalmap".
  181. .. image:: img/normal-set.png
  182. Now, because this is a normalmap and not a per-vertex normal, we are going to assign it in the ``fragment()``
  183. function. The ``fragment()`` function will be explained in more detail in the next part of this tutorial.
  184. .. code-block:: glsl
  185. void fragment() {
  186. }
  187. When we have normals that correspond to a specific vertex we set ``NORMAL``, but if you have a normalmap
  188. that comes from a texture, set the normal using ``NORMALMAP``. This way Godot will handle the wrapping the
  189. texture around the mesh automatically.
  190. Lastly, in order to ensure that we are reading from the same places on the noise texture and the normalmap
  191. texture, we are going to pass the ``VERTEX.xz`` position from the ``vertex()`` function to the ``fragment()``
  192. function. We do that with varyings.
  193. Above the ``vertex()`` define a ``vec2`` called ``tex_position``. And inside the ``vertex()`` function
  194. assign ``VERTEX.xz`` to ``tex_position``.
  195. .. code-block:: glsl
  196. varying vec2 tex_position;
  197. void vertex() {
  198. ...
  199. tex_position = VERTEX.xz / 2.0 + 0.5;
  200. float height = texture(noise, tex_position).x;
  201. ...
  202. }
  203. And now we can access ``vertex_position`` from the ``fragment()`` function.
  204. .. code-block:: glsl
  205. void fragment() {
  206. NORMALMAP = texture(normalmap, vertex_position).xyz;
  207. }
  208. With the normals in place the light now reacts to the height of the mesh dynamically.
  209. .. image:: img/normalmap.png
  210. We can even drag the light around and the lighting will update automatically.
  211. .. image:: img/normalmap2.png
  212. Here is the full code for this tutorial. You can see it is not very long as Godot handles
  213. most of the difficult stuff for you.
  214. .. code-block:: glsl
  215. shader_type spatial;
  216. uniform float height_scale = 0.5;
  217. uniform sampler2D noise;
  218. uniform sampler2D normalmap;
  219. varying vec2 tex_position;
  220. void vertex() {
  221. tex_position = VERTEX.xz / 2.0 + 0.5;
  222. float height = texture(noise, tex_position).x;
  223. VERTEX.y += height * height_scale;
  224. }
  225. void fragment() {
  226. NORMALMAP = texture(normalmap, tex_position).xyz;
  227. }
  228. That is everything for this part. Hopefully, you now understand the basics of vertex
  229. shaders in Godot. In the next part of this tutorial we will write a fragment function
  230. to accompany this vertex function and we will cover a more advanced technique to turn
  231. this terrain into an ocean of moving waves.