your_first_3d_shader.rst 14 KB

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