Sunday 6 November 2016

Perlin noise for ocean waves

Apologies for the tardiness of this post, I was busy the last few weeks as I was doing some work experience with a local software company. I had a chance to learn a few things about game development on consoles using C++.

Meanwhile, I put together a quick example on how to use Perlin noise to create basic waves in order to simulate ocean movement. The example you will see also allow to modify specular highlights, which was part of the project I was working on for the aforementioned company. The water texture came form here.

This is a picture of the final result:



Setting up

Drag in several planes and places them one next to each other and create a larger plane with all those combined of whatever size you want. 

For the shader, I used a slightly modified version of a standard pixel lighting shader, which you can find here. This post is about the wave movement, so I will not post anything about the shader itself, also because I pretty much used the one I linked.

The "Wave" script

We only need one script and, just like we previously saw in the other project which involved Perlin noise, it's very easy.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Waves : MonoBehaviour {

 public float scale = 10.0f;
 public float speed = 1.0f;
 public float resolution = 1;

 Mesh mesh;
 MeshCollider mc;

 Vector3[] vertices;
 Vector3[] newvertices;

 float randomizer = 1.0f;

 void Start () {

 
  mc = GetComponent<MeshCollider> ();
  mesh = GetComponent<MeshFilter> ().mesh; 
 
 }
 

 void Update () {

  randomizer = -Time.time/2;
  vertices = mesh.vertices;
  newvertices = new Vector3[vertices.Length];

  if (resolution <= 0)
   resolution = 0;


  for (int i = 0; i < vertices.Length; i++) {

   Vector3 ver = vertices [i];
   ver.y = Mathf.PerlinNoise ( (speed * randomizer) +(vertices [i].x + transform.position.x) / resolution, -(speed * randomizer) + (vertices [i].z+ transform.position.z) / resolution) * scale;
   newvertices [i] = ver;

  }
                mesh.vertices = newvertices
  mc.sharedMesh = mesh;
  mesh.RecalculateBounds ();
  mesh.RecalculateNormals ();
 
 }
}

The 3 public variables will allow you to change the waves a bit, as you will see later.

The "randomizer" float parameter is nothing but a time variable.

As we can see, in the Start method we simply get our plane mesh and the mesh collider.
 We can choose whether to update the mesh collider or not and we'll see what difference will make in a moment.

In the Update method we update the randomizer variable first. We then get the vertices of the current mesh and create a new array of Vector3 which will store the new vertices. In the for loop we simply iterate through the vertices of the mesh and assign a new y value to them, in order to create height. This new vertex is then places in the recently created array which, at the end of the for loop, will replace the current mesh vertex array (line 41). On the next line we update the mesh collider as well, based on the just updated plane mesh.

If we were to omit line 42, which updates the mesh collider, this is what we will see in the scene editor:


Even though the plane mesh has waves, the collider is still flat. Obviously, that happens because we did not update the mesh collider which, currently, is still using the standard plane mesh.

If we assign the new plane mesh (with waves) to the mesh collider, the collider itself will match the shape of our plane.

Conclusion

This is an easy way to create ocean movement. Using the standard planes provided by Unity is no the best idea, we should use ocean meshes, which normally have much greater vertex density which allow for much smoother undulation.

Example scene here.

4 comments:

  1. hey, thanks for the provided example of the perlin noise, it creates some amazing waves. But I would need a little help with standard pixel lighting shader, on which GameObject do I need to apply it. If I create a material out of the shader, and apply it one the plane mesh I get One weird gradient (yellow - white) color. Do I need to do any modifications on the shader?

    ReplyDelete
  2. Hello, thank for your reply.

    Well, it depends on what you are trying to achieve. In this example I think I wrote a shader for the water, but it's ultimately irrelevant to the creation of the waves. I would need to take a look at your shader code to see what's happening.

    What you should try to do is to create a material and a very simple shader, (that simply displays a texture) and than expand it little by little (by adding colors, lighting calculations and so on).

    ReplyDelete
  3. Hmm, it is so that I've downloaded Cg Per_pixel lightning shader for which you provided a link in your tutorial. I did not change the shader at all, I've just created a new material using Cg Per_pixel lightning shader. But I don't know on which object I should apply it? Is this kind a image effect shader, meaning I need to add it to the main camera and manipulate the camera image processing in this way: Graphics.Blit(source, destination, _mat); ?

    ReplyDelete
    Replies
    1. No, that's a "regular" shader, which is to be applied to materials for objects. You should be able to select the shader when you create the material. Check the name of the shader on the very first line in the shader code: Shader "MyAmazingShader", in this case, "MyAmazingShader" is the name of your shader. Select it when you create the material and, if I understood correctly, you apply it to the water object. I'll try to find the project of this post, I'll send it to you if I can get it if you need.

      Delete