Browse Source

Merge pull request #1540 from clayjohn/heightmap_tutorial

Rewrote Mesh Generation Tutorial
Max Hilbrunner 7 years ago
parent
commit
6252edce6b
46 changed files with 369 additions and 239 deletions
  1. BIN
      tutorials/3d/img/10_Blender_exportUV.png
  2. BIN
      tutorials/3d/img/11_Godot.png
  3. BIN
      tutorials/3d/img/14_Godot_LoadMesh.png
  4. BIN
      tutorials/3d/img/15_Godot_MeshPlaneRendered.png
  5. BIN
      tutorials/3d/img/16_Godot_ShaderMaterial.png
  6. BIN
      tutorials/3d/img/17_Godot_newMaterialShader.png
  7. BIN
      tutorials/3d/img/18_Godot_ShaderEditorOpened.png
  8. BIN
      tutorials/3d/img/19_Godot_BlackPlane.png
  9. BIN
      tutorials/3d/img/1_GIMP_createImage512.png
  10. BIN
      tutorials/3d/img/20_Godot_TexturedPlane.png
  11. BIN
      tutorials/3d/img/21_Godot_Fini.png
  12. BIN
      tutorials/3d/img/2_GIMP.png
  13. BIN
      tutorials/3d/img/3_GIMP_FilterRenderClouds.png
  14. BIN
      tutorials/3d/img/4_GIMP_Clouds.png
  15. BIN
      tutorials/3d/img/5_Blender.png
  16. BIN
      tutorials/3d/img/6_Blender_CreatePlane.png
  17. BIN
      tutorials/3d/img/7_Blender_subdivided.png
  18. BIN
      tutorials/3d/img/8_Blender_UVSmart.png
  19. BIN
      tutorials/3d/img/9_Blender_UV_editor.png
  20. BIN
      tutorials/3d/img/vertex_displacement_cos.png
  21. BIN
      tutorials/3d/img/vertex_displacement_cos_amplitude.png
  22. BIN
      tutorials/3d/img/vertex_displacement_cos_scaled.png
  23. BIN
      tutorials/3d/img/vertex_displacement_light1.png
  24. BIN
      tutorials/3d/img/vertex_displacement_light2.png
  25. BIN
      tutorials/3d/img/vertex_displacement_new_mesh.png
  26. BIN
      tutorials/3d/img/vertex_displacement_new_shader.png
  27. BIN
      tutorials/3d/img/vertex_displacement_new_shader_material.png
  28. BIN
      tutorials/3d/img/vertex_displacement_noise1.png
  29. BIN
      tutorials/3d/img/vertex_displacement_noise2.png
  30. BIN
      tutorials/3d/img/vertex_displacement_noise3.png
  31. BIN
      tutorials/3d/img/vertex_displacement_normal.png
  32. BIN
      tutorials/3d/img/vertex_displacement_normal_detailed1.png
  33. BIN
      tutorials/3d/img/vertex_displacement_normal_detailed2.png
  34. BIN
      tutorials/3d/img/vertex_displacement_normal_detailed3.png
  35. BIN
      tutorials/3d/img/vertex_displacement_planemesh.png
  36. BIN
      tutorials/3d/img/vertex_displacement_shader_editor.png
  37. BIN
      tutorials/3d/img/vertex_displacement_subdivided_mesh.png
  38. BIN
      tutorials/3d/img/vertex_displacement_terrain.png
  39. BIN
      tutorials/3d/img/vertex_displacement_uniform1.png
  40. BIN
      tutorials/3d/img/vertex_displacement_uniform2.png
  41. BIN
      tutorials/3d/img/vertex_displacement_viewport_settings.png
  42. BIN
      tutorials/3d/img/vertex_displacement_wireframe1.png
  43. BIN
      tutorials/3d/img/vertex_displacement_wireframe2.png
  44. 1 1
      tutorials/3d/index.rst
  45. 0 238
      tutorials/3d/mesh_generation_with_heightmap_and_shaders.rst
  46. 368 0
      tutorials/3d/vertex_displacement_with_shaders.rst

BIN
tutorials/3d/img/10_Blender_exportUV.png


BIN
tutorials/3d/img/11_Godot.png


BIN
tutorials/3d/img/14_Godot_LoadMesh.png


BIN
tutorials/3d/img/15_Godot_MeshPlaneRendered.png


BIN
tutorials/3d/img/16_Godot_ShaderMaterial.png


BIN
tutorials/3d/img/17_Godot_newMaterialShader.png


BIN
tutorials/3d/img/18_Godot_ShaderEditorOpened.png


BIN
tutorials/3d/img/19_Godot_BlackPlane.png


BIN
tutorials/3d/img/1_GIMP_createImage512.png


BIN
tutorials/3d/img/20_Godot_TexturedPlane.png


BIN
tutorials/3d/img/21_Godot_Fini.png


BIN
tutorials/3d/img/2_GIMP.png


BIN
tutorials/3d/img/3_GIMP_FilterRenderClouds.png


BIN
tutorials/3d/img/4_GIMP_Clouds.png


BIN
tutorials/3d/img/5_Blender.png


BIN
tutorials/3d/img/6_Blender_CreatePlane.png


BIN
tutorials/3d/img/7_Blender_subdivided.png


BIN
tutorials/3d/img/8_Blender_UVSmart.png


BIN
tutorials/3d/img/9_Blender_UV_editor.png


BIN
tutorials/3d/img/vertex_displacement_cos.png


BIN
tutorials/3d/img/vertex_displacement_cos_amplitude.png


BIN
tutorials/3d/img/vertex_displacement_cos_scaled.png


BIN
tutorials/3d/img/vertex_displacement_light1.png


BIN
tutorials/3d/img/vertex_displacement_light2.png


BIN
tutorials/3d/img/vertex_displacement_new_mesh.png


BIN
tutorials/3d/img/vertex_displacement_new_shader.png


BIN
tutorials/3d/img/vertex_displacement_new_shader_material.png


BIN
tutorials/3d/img/vertex_displacement_noise1.png


BIN
tutorials/3d/img/vertex_displacement_noise2.png


BIN
tutorials/3d/img/vertex_displacement_noise3.png


BIN
tutorials/3d/img/vertex_displacement_normal.png


BIN
tutorials/3d/img/vertex_displacement_normal_detailed1.png


BIN
tutorials/3d/img/vertex_displacement_normal_detailed2.png


BIN
tutorials/3d/img/vertex_displacement_normal_detailed3.png


BIN
tutorials/3d/img/vertex_displacement_planemesh.png


BIN
tutorials/3d/img/vertex_displacement_shader_editor.png


BIN
tutorials/3d/img/vertex_displacement_subdivided_mesh.png


BIN
tutorials/3d/img/vertex_displacement_terrain.png


BIN
tutorials/3d/img/vertex_displacement_uniform1.png


BIN
tutorials/3d/img/vertex_displacement_uniform2.png


BIN
tutorials/3d/img/vertex_displacement_viewport_settings.png


BIN
tutorials/3d/img/vertex_displacement_wireframe1.png


BIN
tutorials/3d/img/vertex_displacement_wireframe2.png


+ 1 - 1
tutorials/3d/index.rst

@@ -19,5 +19,5 @@
    using_multi_mesh_instance
    working_with_3d_skeletons
    inverse_kinematics
-   mesh_generation_with_heightmap_and_shaders
+   vertex_displacement_with_shaders
    fps_tutorial/index

+ 0 - 238
tutorials/3d/mesh_generation_with_heightmap_and_shaders.rst

@@ -1,238 +0,0 @@
-.. _doc_mesh_generation_with_heightmap_and_shaders:
-
-Mesh generation with heightmap and shaders
-==========================================
-
-Introduction
-------------
-
-This tutorial will help you to use Godot shaders to deform a plane
-mesh so that it appears like a basic terrain. Remember that this solution
-has pros and cons.
-
-Pros:
-
--  Pretty easy to do.
--  This approach allows computation of LOD terrains.
--  The heightmap can be used in Godot to create a normal map.
-
-Cons:
-
--  The Vertex Shader can't re-compute normals of the faces. Thus, if
-   your mesh is not static, this method will **not** work with shaded
-   materials.
--  This tutorial uses a plane mesh imported from Blender to Godot
-   Engine. Godot is able to create meshes as well.
-
-See this tutorial as an introduction, not a method that you should
-employ in your games, except if you intend to do LOD. Otherwise, this is
-probably not the best way.
-
-However, let's first create a heightmap, or a 2D representation of the terrain.
-To do this, I'll use GIMP, but you can use any image editor you like.
-
-The heightmap
--------------
-
-We will use a few functions of GIMP image editor to produce a simple
-heightmap. Start GIMP and create a square image of 512x512 pixels.
-
-.. image:: img/1_GIMP_createImage512.png
-
-You are now in front of a new, blank, square image.
-
-.. image:: img/2_GIMP.png
-
-Then, use a filter to render some clouds on this new image.
-
-.. image:: img/3_GIMP_FilterRenderClouds.png
-
-Parameter this filter to whatever you want. A white pixel corresponds
-to the highest point of the heightmap, a black pixel corresponds to
-the lowest one. So, darker regions are valleys and brighter are
-mountains. If you want, you can check "tileable" to render a heightmap
-that can be cloned and tiled close together with another one. X and Y
-size don't matter a lot as long as they are big enough to provide a
-decent ground. A value of 4.0 or 5.0 for both is nice. Click on the
-"New Seed" button to roll a dice and GIMP will create a new random
-heightmap. Once you are happy with the result, click "OK".
-
-.. image:: img/4_GIMP_Clouds.png
-
-You can continue to edit your image if you wish. For our example,
-let's keep the heightmap as is, and let's export it to a PNG file, say
-"heightmap.png". Save it in your Godot project folder.
-
-The plane mesh
---------------
-
-Now, we will need a plane mesh to import in Godot. Let's run Blender.
-
-.. image:: img/5_Blender.png
-
-Remove the start cube mesh, then add a new plane to the scene.
-
-.. image:: img/6_Blender_CreatePlane.png
-
-Zoom a bit, then switch to Edit mode (Tab key) and in the Tools
-buttongroup at the left, hit "Subdivide" 5 or 6 times.
-
-.. image:: img/7_Blender_subdivided.png
-
-Your mesh is now subdivided, which means we added vertices to the
-plane mesh that we will later be able to move. Job's not finished yet:
-in order to texture this mesh a proper UV map is necessary. Currently,
-the default UV map contains only the 4 corner vertices we had at the
-beginning. However, we now have more, and we want to be able to
-texture over the whole mesh correctly.
-
-If all the vertices of your mesh are not selected, select them all
-(hit "A"). They must appear orange, not black. Then, in the
-Shading/UVs button group to the left, click the "Unwrap" button (or
-simply hit "U") and select "Smart UV Project". Keep the default
-options and hit "Ok".
-
-.. image:: img/8_Blender_UVSmart.png
-
-Now, we need to switch our view to "UV/Image editor".
-
-.. image:: img/9_Blender_UV_editor.png
-
-Select all the vertices again ("A") then in the UV menu, select
-"Export UV Layout".
-
-.. image:: img/10_Blender_exportUV.png
-
-Export the layout as a PNG file. Name it "plane.png" and save it in
-your Godot project folder. Now, let's export our mesh as an OBJ file.
-Top of the screen, click "File/Export/Wavefront (obj)". Save your
-object as "plane.obj" in your Godot project folder.
-
-Shader magic
-------------
-
-Let's now open Godot Editor.
-
-Create a new project in the folder you previously created and name it
-what you want.
-
-.. image:: img/11_Godot.png
-
-In our default scene (3D), create a root node "Spatial".
-
-Create a MeshInstance node as a child of the node we just created.
-Then, load the Mesh selecting "Load" and then our "plane.obj" file.
-
-.. image:: img/14_Godot_LoadMesh.png
-
-Great! Our plane is now rendered in the 3D view.
-
-.. image:: img/15_Godot_MeshPlaneRendered.png
-
-It is time to add some shader stuff. In the Inspector, in the
-"Material" line, add a "New ShaderMaterial". Edit it by
-clicking it and then selecting the "Edit" option.
-
-.. image:: img/16_Godot_ShaderMaterial.png
-
-We need now to create the actual shader. On the Inspector,
-select the "Shader" line and click on "New Shader".
-
-.. image:: img/17_Godot_newMaterialShader.png
-
-Edit it by clicking the "Edit" option just like we did before. The Shader
-editor opens.
-
-.. image:: img/18_Godot_ShaderEditorOpened.png
-
-Let's start writing our shader. If you don't know how to use shaders in Godot
-you can check the :ref:`doc_shading_language` page.
-
-Let's start with the Fragment part.
-This one is used to texture the plane using an image.
-For this example, we will texture it with the heightmap image itself,
-so we'll actually see mountains as brighter regions and canyons as
-darker regions. Use this code:
-
-::
-
-    shader_type spatial;
-    render_mode unshaded;
-
-    uniform sampler2D source;
-
-    void fragment() {
-        ALBEDO = texture(source, UV).rgb;
-    }
-
-First, we set the shader type as ``spatial`` (for 3D). The
-``render_mode unshaded`` line makes our MeshInstance be unaffected
-by the lighting in our world. It doesn't matter since it is a
-greyscale image. We take a parameter (``uniform``) as a ``sampler2D``,
-which will be the texture of our heightmap.
-
-Then, we set the color of every pixel of the image given by
-``texture(source, UV).rgb`` setting it to the ALBEDO variable.
-Remember that the ``UV`` variable is a shader variable that returns
-the 2D position of the pixel in the texture image, according to the
-vertex we are currently dealing with. That is the use of the UV Layout
-we made before.
-
-However, the plane is displayed white! This is because we didn't set
-the texture file and the color to use.
-
-.. image:: img/19_Godot_BlackPlane.png
-
-In the Inspector, click the back arrow to get back to the
-ShaderMaterial. This is where you want to set the texture and the
-color. Click the "Shader Param" line, in "Source", click "Load"
-and select the texture file "heightmap.png". Now you will see
-our heightmap.
-
-.. image:: img/20_Godot_TexturedPlane.png
-
-Good. Now, the Vertex part.
-
-The Vertex Shader is the first shader to be executed by the pipeline. It
-deals with vertices.
-
-Insert a new "uniform" variable right after the one that we introduced
-before:
-
-::
-
-    uniform float height_range;
-
-The ``height_range`` parameter is a
-parameter that we will use to increase the height effect.
-
-Then, insert the following function before the fragment function we wrote before.
-
-::
-
-    void vertex() {
-        vec2 xz = VERTEX.xz;
-        float h = texture(source, UV).g * height_range;
-        VERTEX = vec3(xz.x, h, xz.y);
-    }
-
-
-First, we save the x and z position of the VERTEX, because we
-do not want them to change: the plane must remain square. Remember
-that Y axis corresponds to the "altitude", which is the only one we
-want to change with the heightmap.
-
-Then, we compute an ``h`` variable by multiplying the pixel value
-at the UV position and the ``height_range``. As the heightmap is a
-greyscale image, all r, g and b channels contain the same value. We
-use ``g``, but any of r, g and b have the same effect.
-
-After that, we set the current vertex' position at (xz.x, h, xz.y)
-position. Concerning xz.y remember that its type is "vec2". Thus, its
-components are x and y.
-
-That's all good, but our plane remains flat. This is because the
-``height_range`` value is 0. Increase this value to observe the mesh
-distort and take to form of the terrain we set before:
-
-.. image:: img/21_Godot_Fini.png

+ 368 - 0
tutorials/3d/vertex_displacement_with_shaders.rst

@@ -0,0 +1,368 @@
+.. _doc_vertex_displacement_with_shaders:
+
+Vertex displacement with shaders
+================================
+
+Introduction
+------------
+
+This tutorial will teach you how to displace the vertices of 
+a :ref:`Plane Mesh<class_PlaneMesh>` inside a shader. Vertex displacement can be used 
+for a wide variety of effects, but most commonly it is used
+as a quick way to turn a flat plane into a simple terrain. Typically
+this is done using a heightmap, but in order to keep everything self
+contained, in this tutorial we will use noise in a shader. At the end
+of this tutorial we will have a deformed plane that looks like a 
+miniature terrain complete with dynamic lighting.
+
+By reading this tutorial you should gain a basic understanding of:
+
+* How to create and subdivide a :ref:`Plane Mesh<class_PlaneMesh>`
+* How to create and assign a material to a :ref:`Mesh<class_MeshInstance>`
+* How to write a :ref:`Shader<class_Shader>` that displaces the vertices of a :ref:`Mesh<class_MeshInstance>`
+* How to pass values (Uniforms) into a :ref:`Shader<class_Shader>` to update the :ref:`Mesh<class_MeshInstance>` in realtime
+* How to approximate normals from a height function
+* How to use a light with a custom material
+
+The plane mesh
+--------------
+
+First, add a :ref:`Spatial<class_Spatial>` node to the scene to act as the root. Next, add a :ref:`MeshInstance<class_MeshInstance>`
+as a child. 
+
+.. image:: img/vertex_displacement_new_mesh.png
+
+Select the newly created :ref:`MeshInstance<class_MeshInstance>`. Then click on the button that says "null" 
+next to the :ref:`Mesh<class_MeshInstance>` in the Inspector. This will bring up a list of :ref:`PrimitiveMeshes<class_PrimitiveMesh>`.
+Select "New PlaneMesh".
+
+.. image:: img/vertex_displacement_planemesh.png
+
+The button will change into a small image of a plane. Click on it to enter into
+the Inspector for the :ref:`Plane Mesh<class_MeshInstance>`. 
+
+Then, in the viewport, click in the upper left corner where it says [Perspective].
+A menu will appear. In the middle of the menu are options for how to display the scene.
+Select 'Display Wireframe'.
+
+.. image:: img/vertex_displacement_viewport_settings.png
+
+This will allow you to see the triangles making up the plane.
+
+.. image:: img/vertex_displacement_wireframe1.png
+
+Now set the ``Subdivide Width`` and ``Subdivide Height`` to ``32``.
+
+.. image:: img/vertex_displacement_subdivided_mesh.png
+
+You can see that there are now way more triangles in the :ref:`Mesh<class_MeshInstance>`. This will give
+us more vertices to work with and thus allow us to add more detail.
+
+.. image:: img/vertex_displacement_wireframe2.png
+
+
+Shader magic
+------------
+
+Now that we have a :ref:`Plane Mesh<class_MeshInstance>` to draw lets setup the material that will deform the :ref:`Mesh<class_MeshInstance>`.
+
+Click beside material in the :ref:`Plane Mesh<class_MeshInstance>` Menu and create a new :ref:`ShaderMaterial<class_ShaderMaterial>`.
+
+.. image:: img/vertex_displacement_new_shader_material.png
+
+Then click on the created :ref:`ShaderMaterial<class_ShaderMaterial>`.
+
+Then click beside 'shader' and create a new :ref:`Shader<class_Shader>`.
+
+.. image:: img/vertex_displacement_new_shader.png
+
+Click into the newly created :ref:`Shader<class_Shader>`. You should now see Godot's Shader editor.
+
+.. image:: img/vertex_displacement_shader_editor.png
+
+Notice how it is throwing an error? This is because the shader editor reloads shaders on
+the fly automatically. The first thing Godot shaders need is a declaration of what type of
+shader they are. Accordingly, we set the variable ``shader_type`` to ``spatial``. One more
+thing we will add is the ``render_mode``, we will set it to ``unshaded``. This means that
+Godot won't run the light shader on this object.
+
+::
+
+  shader_type spatial;
+  render_mode unshaded;
+
+This should remove the errors and your :ref:`Mesh<class_MeshInstance>` should turn white. If you were to comment out
+the ``render_mode`` the plane would appear blue because it would pick up the sky colors.
+
+Next we will define a vertex shader. The vertex shader determines where the vertices of your
+:ref:`Mesh<class_MeshInstance>` appear in the final scene. We will be using it to offset the height of each vertex and
+make our flat plane appear like a little terrain. 
+
+We define the vertex shader like so:
+
+::
+
+  void vertex() {
+
+  }
+
+With nothing in the ``vertex`` function Godot will use its default vertex shader. We can easily 
+start to make changes by adding a single line:
+
+::
+
+  void vertex() {
+    VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z);
+  }
+  
+Adding this line you should get an image like the one below.
+
+.. image:: img/vertex_displacement_cos.png
+
+Okay, lets unpack this. The ``y`` value of the ``VERTEX`` is being increased. And we are passing
+the ``x`` and ``z`` components of the ``VERTEX`` as arguments to ``cos`` and ``sin`` this gives us
+a wave like appearance across the ``x`` and ``z`` axis. 
+
+What we want to achieve is the look of little hills, after all ``cos`` and ``sin`` already look kind of like
+hills. We do so by scaling the inputs to the ``cos`` and ``sin`` functions.
+
+::
+
+  void vertex() {
+    VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0);
+  }
+
+.. image:: img/vertex_displacement_cos_scaled.png
+
+This looks better, but it is still too spiky. This is because ``cos`` and ``sin`` output values between ``-1`` and ``1``, 
+so the range of the output is much too high. We correct this by multiplying the result by ``0.5`` to reduce the size.
+
+::
+
+  void vertex() {
+    VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0) * 0.5;
+  }
+
+.. image:: img/vertex_displacement_cos_amplitude.png
+
+Looks much more hilly now. But ``cos`` and ``sin`` are boring. Lets move onto something more interesting.
+
+Noise
+-----
+
+Noise is a very popular tool for procedural generation. Think of it as similar to the cosine function
+where you have repeating hills except with noise each hill has a different height. Understanding 
+noise is not necessary for this tutorial. There is nothing wrong with simply copying and pasting
+the code below.
+
+The first function we use to generate the noise is the ``hash`` function. It gives the random height
+for each of the hill tops. 
+
+::
+
+  float hash(vec2 p) {
+    return fract(sin(dot(p * 17.17, vec2(14.91, 67.31))) * 4791.9511);
+  }
+
+You will find similar functions to this all over the internet. It is lovingly referred to as the
+'one-liner hash function'. It works well for simple noise, but there are many better alternatives 
+floating around as well. For this tutorial it will work fine. 
+
+Next we define the ``noise`` function. It smoothly interpolates between the random heights.
+Again, if this code seems daunting, do not worry, just copy paste and move on with the tutorial.
+
+::
+
+  float noise(vec2 x) {
+    vec2 p = floor(x);
+    vec2 f = fract(x);
+    f = f * f * (3.0 - 2.0 * f);
+    vec2 a = vec2(1.0, 0.0);
+    return mix(mix(hash(p + a.yy), hash(p + a.xy), f.x), 
+           mix(hash(p + a.yx), hash(p + a.xx), f.x), f.y);
+  }
+
+Lastly, to add detail we combine successive layers of noise using something called fractal
+brownian motion or FBM. Scary name aside FBM noise just adds together layers of noise with
+increase frequency and decreasing amplitude. To implement it we run over a for loop where
+we increase the frequency each level, decrease the amplitude, and calculate a new layer of noise.
+
+::
+
+  float fbm(vec2 x) {
+    float height = 0.0;
+    float amplitude = 0.5;
+    float frequency = 3.0;
+    for (int i = 0; i < 6; i++){
+      h += noise(x * frequency) * amplitude;
+      a *= 0.5;
+      p *= 2.0;
+    }
+    return h;
+  }
+
+We can now use this noise function in place of ``cos`` and ``sin`` in the previous section. 
+
+::
+
+  float height = noise(VERTEX.xz * 4.0);
+  VERTEX.y += height * 0.5;
+
+.. image:: img/vertex_displacement_noise1.png
+
+With the noise function in place we already have something that looks kind of cool.
+There is a lot of detail, it kind of looks hilly or mountainous. 
+
+Fragment Shader
+---------------
+
+The difference between a vertex shader and a fragment shader is that the vertex shader
+runs per vertex and sets properties such as ``VERTEX`` (position) and ``NORMAL``, while
+the fragment shader runs per pixel and, most importantly, sets the ``ALBEDO`` color of the :ref:`Mesh<class_MeshInstance>`.
+
+Now lets look at the :ref:`Mesh<class_MeshInstance>` with a regular shader instead of the wireframe. Set the 
+viewport back to 'Display Normal'.
+
+.. image:: img/vertex_displacement_noise2.png
+
+The :ref:`Mesh<class_MeshInstance>` appears completely white because the fragment shader is coloring each pixel white,
+but if every pixel is white we lose detail on the :ref:`Mesh<class_MeshInstance>`. So lets color each pixel based
+on the height calculated in the vertex shader. We do so by setting the ``COLOR`` variable 
+in the vertex shader. And by setting the ``ALBEDO`` in the fragment shader to the calculated
+``COLOR`` variable.
+
+::
+
+  void vertex() {
+    ...
+    COLOR.xyz = vec3(height);
+  }
+
+  void fragment(){
+    ALBEDO = COLOR.xyz;
+  }
+
+With this change we can see the detail of the :ref:`Mesh<class_MeshInstance>`, even without displaying the :ref:`Mesh<class_MeshInstance>`'s wireframe.
+
+.. image:: img/vertex_displacement_noise3.png
+
+Uniforms
+--------
+
+Uniform variables allow you to pass data from the game into the shader. They can
+be very useful for controlling shader effects. Uniforms can be almost any 
+datatype that can be used in the shader. To use a uniform you declare it in
+your :ref:`Shader<class_Shader>` using the keyword ``uniform``.
+
+Lets make a uniform that changes the height of the terrain.
+
+::
+
+  uniform float height_scale = 0.5;
+
+
+Godot lets you initialize a uniform with a value, here ``height_scale`` is set to
+``0.5``. You can set uniforms from gdscript by calling the function ``set_shader_param`` 
+on the material corresponding to the shader. The value passed from gdscript takes
+precedence over the value used to initialize it in the shader.
+
+:: 
+
+  material.set_shader_param("height_scale", 0.5)
+
+Remember that the string passed into ``set_shader_param`` must match the name
+of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the uniform variable anywhere 
+inside your :ref:`Shader<class_Shader>`. Here, we will use it to set the height value instead
+of arbitrarily multiplying by ``0.5``.
+
+::
+
+  VERTEX.y += height * height_scale;
+
+The terrain should look exactly the same, but now we have control over the height easily.
+Here is the same terrain with ``height_scale`` set to ``1``:
+
+.. image:: img/vertex_displacement_uniform1.png
+
+And here it is with ``height_scale`` set to ``0.2``:
+
+.. image:: img/vertex_displacement_uniform2.png
+
+Using uniforms we can even change the value every frame to animate the height of the terrain.
+Combined with :ref:`Tweens<class_Tween>` this can be especially useful for simple animations.
+
+Interacting with light
+----------------------
+
+As a final part of this tutorial lets try to set up the terrain to interact with light.
+First, we will add an :ref:`OmniLight<class_OmniLight>` to the scene.
+
+
+.. image:: img/vertex_displacement_light1.png
+
+You should notice that nothing changes, this is because we set the ``render_mode`` to ``unshaded``
+at the beginning of this tutorial, lets remove that.
+
+::
+
+  shader_type spatial;
+  //render_mode unshaded;
+
+.. image:: img/vertex_displacement_light2.png
+
+It looks slightly better now, you can see the light affecting the terrain, and it has
+turned blue as a result of the sky. The problem is the light is affecting the terrain
+as if it were a flat plane. This is because the light shader uses the normals of the
+:ref:`Mesh<class_MeshInstance>` to calculate light. The normals are stored in the :ref:`Mesh<class_MeshInstance>`, but we are changing 
+the shape of the :ref:`Mesh<class_MeshInstance>` in the shader so the normals are no longer correct. To fix this
+we need to recalculate the normals in the shader. Godot makes this easy for us, all we
+have to do is calculate the new normal and set ``NORMAL`` to that value in the vertex shader.
+With ``NORMAL`` set Godot will do all the difficult lighting calculations for us.
+
+To calculate the normal from noise we are going to use a technique called 'central differences'.
+This is used a lot, especially in places like shadertoy, to calculate normals in shaders. 
+What we will do is calculate the noise at four points surrounding the vertex in the ``x`` and ``z`` directions and then calculate 
+the slope at the vertex from that. After all a normal is just an indicator of the slope of the
+noise.
+
+We calculate the normal with one line in the vertex shader.
+
+::
+
+  vec2 e = vec2(0.01, 0.0);
+  vec3 normal = normalize(vec3(fbm(VERTEX.xz - e) - fbm(VERTEX.xz + e), 2.0 * e.x, fbm(VERTEX.xz - e.yx) - fbm(VERTEX.xz + e.yx)));
+  NORMAL = normal;
+
+The variable ``e`` just makes it easier to add and subtract the right value from the ``VERTEX``. 
+Setting ``e`` to a lower number will increase the level of detail of the normal. 
+
+With ``NORMAL`` calculated the terrain now looks like:
+
+.. image:: img/vertex_displacement_normal.png
+
+This still does not look how we want it to. The issue here is that the noise changes 
+faster than the vertices do. So when we calculate the normal at the point of the 
+``VERTEX`` it does not align with what we see in the final :ref:`Mesh<class_MeshInstance>`. In order to fix
+this we add more vertices. The below image is made with a :ref:`Mesh<class_MeshInstance>` with ``subdivision`` set
+to ``100``.
+
+.. image:: img/vertex_displacement_normal_detailed1.png
+
+Now we can drag the light around and the lighting will update automatically.
+
+.. image:: img/vertex_displacement_normal_detailed2.png
+
+.. image:: img/vertex_displacement_normal_detailed3.png
+
+If you zoom the camera out you can see that the :ref:`Mesh<class_MeshInstance>` now looks like a small terrain.
+
+.. image:: img/vertex_displacement_terrain.png
+
+That is everything for this tutorial. Hopefully you understand the basics of vertex
+shaders in Godot. As a further exercise try changing the ``height_scale`` from gdscript,
+try using different :ref:`Primitive Meshes<class_PrimitiveMesh>`, and try making your
+own functions to calculate ``height``.
+
+For further information on how to use shaders in Godot 
+you should check out the :ref:`doc_shading_language` page.
+