mesh_generation_with_heightmap_and_shaders.rst 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. .. _doc_mesh_generation_with_heightmap_and_shaders:
  2. Mesh generation with heightmap and shaders
  3. ==========================================
  4. Introduction
  5. ------------
  6. This tutorial will help you to use Godot shaders to deform a plane
  7. mesh so it appears like a basic terrain. Remember that this solution
  8. has pros and cons.
  9. Pros:
  10. - Pretty easy to do.
  11. - This approach allows computation of LOD terrains.
  12. - The heightmap can be used in Godot to create a normal map.
  13. Cons:
  14. - The Vertex Shader can't re-compute normals of the faces. Thus, if
  15. your mesh is not static, this method will **not** work with shaded
  16. materials.
  17. - This tutorial uses a plane mesh imported from Blender to Godot
  18. Engine. Godot is able to create meshes as well.
  19. See this tutorial as an introduction, not a method that you should
  20. employ in your games, except if you intend to do LOD. Otherwise, this is
  21. probably not the best way.
  22. However, let's first create a heightmap,or a 2D representation of the terrain.
  23. To do this, I'll use GIMP, but you can use any image editor you like.
  24. The heightmap
  25. -------------
  26. We will use a few functions of GIMP image editor to produce a simple
  27. heightmap. Start GIMP and create a square image of 512x512 pixels.
  28. .. image:: img/1_GIMP_createImage512.png
  29. You are now in front of a new, blank, square image.
  30. .. image:: img/2_GIMP.png
  31. Then, use a filter to render some clouds on this new image.
  32. .. image:: img/3_GIMP_FilterRenderClouds.png
  33. Parameter this filter to whatever you want. A white pixel corresponds
  34. to the highest point of the heightmap, a black pixel corresponds to
  35. the lowest one. So, darker regions are valleys and brighter are
  36. mountains. If you want, you can check "tileable" to render a heightmap
  37. that can be cloned and tiled close together with another one. X and Y
  38. size don't matter a lot as long as they are big enough to provide a
  39. decent ground. A value of 4.0 or 5.0 for both is nice. Click on the
  40. "New Seed" button to roll a dice and GIMP will create a new random
  41. heightmap. Once you are happy with the result, click "OK".
  42. .. image:: img/4_GIMP_Clouds.png
  43. You can continue to edit your image if you wish. For our example,
  44. let's keep the heightmap as is, and let's export it to a PNG file, say
  45. "heightmap.png". Save it in your Godot project folder.
  46. The plane mesh
  47. --------------
  48. Now, we will need a plane mesh to import in Godot. Let's run Blender.
  49. .. image:: img/5_Blender.png
  50. Remove the start cube mesh, then add a new plane to the scene.
  51. .. image:: img/6_Blender_CreatePlane.png
  52. Zoom a bit, then switch to Edit mode (Tab key) and in the Tools
  53. buttongroup at the left, hit "Subdivide" 5 or 6 times.
  54. .. image:: img/7_Blender_subdivided.png
  55. Your mesh is now subdivided, which means we added vertices to the
  56. plane mesh that we will later be able to move. Job's not finished yet:
  57. in order to texture this mesh a proper UV map is necessary. Currently,
  58. the default UV map contains only the 4 corner vertices we had at the
  59. beginning. However, we now have more, and we want to be able to
  60. texture over the whole mesh correctly.
  61. If all the vertices of your mesh are not selected, select them all
  62. (hit "A"). They must appear orange, not black. Then, in the
  63. Shading/UVs button group to the left, click the "Unwrap" button (or
  64. simply hit "U") and select "Smart UV Project". Keep the default
  65. options and hit "Ok".
  66. .. image:: img/8_Blender_UVSmart.png
  67. Now, we need to switch our view to "UV/Image editor".
  68. .. image:: img/9_Blender_UV_editor.png
  69. Select all the vertices again ("A") then in the UV menu, select
  70. "Export UV Layout".
  71. .. image:: img/10_Blender_exportUV.png
  72. Export the layout as a PNG file. Name it "plane.png" and save it in
  73. your Godot project folder. Now, let's export our mesh as an OBJ file.
  74. Top of the screen, click "File/Export/Wavefront (obj)". Save your
  75. object as "plane.obj" in your Godot project folder.
  76. Shader magic
  77. ------------
  78. Let's now open Godot Editor.
  79. Create a new project in the folder you previously created and name it
  80. what you want.
  81. .. image:: img/11_Godot.png
  82. In our default scene (3D), create a root node "Spatial".
  83. Create a MeshInstance node as a child of the node we just created.
  84. Then, load the Mesh selecting "Load" and then our "plane.obj" file.
  85. .. image:: img/14_Godot_LoadMesh.png
  86. Great! Our plane is now rendered in the 3D view.
  87. .. image:: img/15_Godot_MeshPlaneRendered.png
  88. It is time to add some shader stuff. In the Inspector, in the
  89. "Material" line, add a "New ShaderMaterial". Edit it by
  90. clicking it and then selecting the "Edit" option.
  91. .. image:: img/16_Godot_ShaderMaterial.png
  92. We need now to create the actual shader. On the Inspector,
  93. select the "Shader" line and click on "New Shader".
  94. .. image:: img/17_Godot_newMaterialShader.png
  95. Edit it by clicking the "Edit" option just like we did before. The Shader
  96. editor opens.
  97. .. image:: img/18_Godot_ShaderEditorOpened.png
  98. Let's start writing our shader. If you don't know how to use shaders in Godot
  99. you can check the :ref:`doc_shading_language` page.
  100. Let's start with the Fragment part.
  101. This one is used to texture the plane using an image.
  102. For this example, we will texture it with the heightmap image itself,
  103. so we'll actually see mountains as brighter regions and canyons as
  104. darker regions. Use this code:
  105. ::
  106. shader_type spatial;
  107. render_mode unshaded;
  108. uniform sampler2D source;
  109. void fragment() {
  110. ALBEDO = texture(source, UV).rgb;
  111. }
  112. First, we set the shader type as ``spatial`` (for 3D). The
  113. ``render_mode unshaded`` line makes our MeshInstance be unaffected
  114. by the lighting in our world. It doesn't matter since it is a
  115. greyscale image. We take a parameter (``uniform``) as a ``sampler2D``,
  116. which will be the texture of our heightmap.
  117. Then, we set the color of every pixel of the image given by
  118. ``texture(source, UV).rgb`` setting it to the ALBEDO variable.
  119. Remember that the ``UV`` variable is a shader variable that returns
  120. the 2D position of the pixel in the texture image, according to the
  121. vertex we are currently dealing with. That is the use of the UV Layout
  122. we made before.
  123. However, the plane is displayed white! This is because we didn't set
  124. the texture file and the color to use.
  125. .. image:: img/19_Godot_BlackPlane.png
  126. In the Inspector, click the back arrow to get back to the
  127. ShaderMaterial. This is where you want to set the texture and the
  128. color. Click the "Shader Param" line, in "Source", click "Load"
  129. and select the texture file "heightmap.png". Now you will see
  130. our heightmap.
  131. .. image:: img/20_Godot_TexturedPlane.png
  132. Good. Now, the Vertex part.
  133. The Vertex Shader is the first shader to be executed by the pipeline. It
  134. deals with vertices.
  135. Insert a new "uniform" variable right after the one that we introduced
  136. before:
  137. ::
  138. uniform float height_range;
  139. The ``height_range`` parameter is a
  140. parameter that we will use to increase the height effect.
  141. Then, insert the following function before the fragment function we wrote before.
  142. ::
  143. void vertex() {
  144. vec2 xz = VERTEX.xz;
  145. float h = texture(source, UV).g * height_range;
  146. VERTEX = vec3(xz.x, h, xz.y);
  147. }
  148. First, we save the x and z position of the VERTEX, because we
  149. do not want them to change: the plane must remain square. Remember
  150. that Y axis corresponds to the "altitude", which is the only one we
  151. want to change with the heightmap.
  152. Then, we compute an ``h`` variable by multiplying the pixel value
  153. at the UV position and the ``height_range``. As the heightmap is a
  154. greyscale image, all r, g and b channels contain the same value. We
  155. use ``g``, but any of r, g and b have the same effect.
  156. After that, we set the current vertex' position at (xz.x, h, xz.y)
  157. position. Concerning xz.y remember that its type is "vec2". Thus, its
  158. components are x and y.
  159. That's all good, but our plane remains flat. This is because the
  160. ``height_range`` value is 0. Increase this value to observe the mesh
  161. distort and take to form of the terrain we set before:
  162. .. image:: img/21_Godot_Fini.png