Skip to content

Unity mesh generation: vertices, triangles, winding

This is the first blogpost in a planned series dealing with procedurally generated terrain in Unity. I am gearing up to a project that will use terrain in a reactive and interactive way. I have only limited experience with Unity and am starting from scratch with procedurally generated terrain. As such I thought it would be interesting to share my progress as we go along as a sort of tutorial series in an attempt to solidify the concepts for myself. This first entry is all about vertices, triangles, and winding. So let’s get started…


Project Setup

We will start with a blank, 3D Unity project.

Let’s set up a new 3D Unity project

Then we add an empty GameObject to the scene where we can attach our scripts. You can put it anywhere you like in your scene, I will put it at the origin.

Add an empty GameObject that will be our mesh generator

I will rename it to “SingleFace” to keep things organized since I plan to add more functionality to the same scene in subsequent posts/videos. Next, we create an empty script by clicking “Add component” while the “SingleFace” GameObject is selected. Start typing “script” into the search bar and click on “New script” and name it “SingleFace”, hit or click “Create and Add”. We can see it auto attaches to our empty GameObject.

Add a new C# script

Double-click the file to open it in Visual Studio (or whatever environment you have set for editing your scripts. You can set this under Preferences, External Tools).

The heart of any terrain is the Mesh object. Mesh() is a class that allows a script to create, interact, and modify the mesh data to be rendered by Unity via scripting… meaning by writing code.

We will need to step back a bit to talk about the makeup of a mesh and how to deal with the data associated with it, but first let’s quickly finish the setup of our “SingleFace” GameObject so it is ready to go once we have written the code to generate a mesh.

First we need to add a MeshFilter object. This is the interface that allows us to upload and store our new mesh data to be rendered by the Unity Graphics API. While the “SingleFace” GameObject is selected, click “Add Component”, and like before start typing “MeshFilter” into the search bar until it shows up, click it and attach it to “SingleFace”.

Next, we add a MeshRenderer… its function is self-explanatory and we find and attach it the same was as we did with the Script and MeshFilter above.

One last thing to add is a material to the MeshRenderer itslef, let’s use Default-Diffuse for now. This will give us a plain, white mesh. To do this, once again make sure “SingleFace” is selected in your Hierarchy. Select and if needed expand the MeshRenderer component. You might also have to expand the Materials submenu within the MeshRenderer component. The slot next to “Material 0” should say “None (Material)”. Here we are going to add the “Default-Diffuse” material by clicking the tiny circle with a dot in it, which is just to the right of the textfield (I always wondered if whoever was coding this was into mensural notation, but I digress…). This will bring up a floating window with all the available materials. Find and click “Default-Diffuse”.

Attach a MeshFilter and MeshRenderer

Now that we are able to interact with the graphics renderer by both uploading and rendering content we can step back and talk about how meshes work in Unity.

What is a Mesh?

A mesh is a collection of simple, connected shapes. In Unity the standard shape is a triangle (although certain other types can be used as well). To create a larger shape many small triangles are tiled together.

A mesh is made up of many triangles

To generate any kind of mesh we need 3 different components: vertices, triangles, and a material (we already set the material we will be using above, so only 2 things left to do).

A vertex is simply a point in space where two (or more) line segments meet. So looking at this example triangle, each corner is a vertex since 2 lines meet at each point.

A triangle has vertices to position it in space

Unity expects vertices, or any point really to be described in terms of 3D euclidian coordinates: X, Y, and Z. To simplify we are going to deal with 2D space for now, so X and Y only. For our triangle this would mean (going from the bottom left in clockwise rotation):

        (0, 0), (0, 1), (1, 0)
Vertices are positioned in Euclidian space

3 points are a good start, but we are still falling short in terms of correctly representing a triangle. We could start at the bottom right (BR), go to the top (TL), then the bottom left (BL). Or we could start BR, go to BL, and then to TL. Or we could start TL, go to BL, then the BR. etc…

So we need a data structure that will tell Unity which way we would like to look at the vertices in terms of the triangle we want it to draw. SPOILER WARNING: the starting vertex does not matter, ONLY THE WINDING MATTERS!!! I will demonstrate this in code in a bit. By winding we mean how the triangle is traversed: in either clockwise or counter clockwise motion. So BL, TL, BR would be clockwise motion, whereas BL, BR, TL would be counter clockwise motion.

It is very important to think about the winding when defining our triangles . Unity expects clockwise winding!!

Data Structures

First we need to create data structures to hold our vertices and triangles.
Vertices are coordinates, since we are now dealing with 3D space we create a Vector3 array (Vector3[]) and call it “vertices”. I am declaring them as public so we can see and change them in the inspector in Unity later on.

Since triangles only tell us the winding of the vertices, they, in essence contain the order in which we need to access the vertices stored in our Vector3 array. This means an array of integer values, which we will name “triangles”. Once again public so we can inspect them in order to see the differences in windings later on.

        public Vector3[] vertices;
        public int[] triangles;

Now let’s fill these arrays with our data from above. We will keep the mesh as a 2-dimensional shape (we might deal with terrain height, i.e. mountains, in a later tutorial). However, we defined vertices as a Vector3 array since Unity functions in 3D Euclidean space. We will set the Y component – which, is the UP direction in Unity to 0 (see the little graphical representation in the top right of the scene window if you are not sure which axis you need to deal with).

Check which way is up!

First instantiate the vertices array to a new Vector3 array of size 3. Next let’s assign the coordinates we had above to each of the vertices, making sure that X and Z correspond to the numbers above and that Y is always 0.

    vertices = new Vector3[3];

    vertices[0] = new Vector3(0, 0, 0); //BL
    vertices[1] = new Vector3(1, 0, 0); //BR
    vertices[2] = new Vector3(0, 0, 1); //TL

Now to the conceptually more difficult part, the triangles. First, like above, create a new array of type int of size 3. We need clockwise winding (more on that in a bit). As mentioned above the starting point doesn’t matter so let’s simply start with the first point stored in the vertices array at index 0. triangles[0] = 0 then means that our triangle starts at to location STORED at index 0 of the vertices array. This is the conceptually difficult part. The triangles array holds no data as such but defines which points are to be rendered as triangles. Finishing up the triangles assignments we need point 3 (index 2 of the vertices array), and finally we can close the triangle by moving back down and over to point 2 (index 1).

        triangles = new int[3];

        triangles[0] = 0; //BL
        triangles[1] = 2; //TL
        triangles[2] = 1; //BR
Using the index number of the vertices we are winding our triangles clockwise!

That’s it. Now we have enough information that Unity can render a mesh containing a single triangle. However, if we click play in Unity right now not much will happen since we haven’t told the MeshFilter anything about our awesome, handcrafted mesh yet.

Mesh assignment

So far we have two data structures holding our vertices and triangles but no mesh. So let’s create a mesh and assign our data to it.

First let’s jump up to our global variables and create an “awesomeMesh” variable of type Mesh. This is the data structure where we store our vertices, triangle, materials, etc. which we will then pass on to the MeshFilter for rendering. Then inside our Start() routine we instantiate it with:

        awesomeMesh = new Mesh(); 

Now we have our mesh structure. Next we need to access the MeshFilter and assign our awesomeMesh to it. The syntax for accessing things attached to our GameObject, like scripts or references to other GameObjects, is a bit odd at first. Here we go:

        GetComponent<MeshFilter>().sharedMesh = awesomeMesh;

GetComponent<>() looks for an attached component of the GameObject the script is attached to (remember how at the beginning we attached a MeshFilter to SingleFace?). In our case we want to have access to the MeshFilter. Specifically we want to have access to the sharedMesh of the MeshFilter to assign our awesomeMesh to it (you might notice that there is the option of “sharedMesh” and simply “mesh”; there are some differences between the two and cases where it will make a difference when you use which, but for now and for most cases “sharedMesh” is the way to go).
Now this assignment is BY REFERENCE (!!!), which means we only have to do it once in the Start() routine! This is an important point and took me quite some time to figure out. While experimenting I assigned the awesomeMesh to the MeshFilter every time I updated the awesomeMesh. This is NOT necessary (as we will see shortly). Assign it once (by reference) and MeshFilter has in effect learned about awesomeMesh’s existence and will render the contents of awesomeMesh until told otherwise. All we have to do is keep awesomeMesh… well awesome!

Now we have dealt with our MeshFilter it is time to assign our data to awesomeMesh so we finally have something to marvel at on screen!

    awesomeMesh.Clear();
    awesomeMesh.vertices = vertices;
    awesomeMesh.triangles = triangles;

First, make sure awesomeMesh is empty by calling Clear() on it so we don’t get any nasty surprises when rendering. Always assign vertices FIRST, then triangles. That’s it… hit play in Unity and you will see a beautiful, kind of gray-ish triangle.

Creating our own Mesh with the data from vertices and triangles
Mesh before calling RecalculateNormal()

Up above we used the Default-Diffuse Material, which should render a completely white triangle. We need to add the following line of code after we assign the triangles to the awesomeMesh:

         awesomeMesh.RecalculateNormals();

This will fix how our triangle is lighted. Hit play again… marvel in our first white triangle.

Mesh after adjusting lighting with RecalculateNormal()

Finishing Touches

A couple of more quick things to finish up. I said I would show how winding and not starting vertex is important for mesh creation.

Let’s make some quick changes to the code to demonstrate this:

First let’s make a UpdateMesh() function so we can update the mesh from anywhere in the code:

public void UpdateMesh()
{
       awesomeMesh.Clear();
       awesomeMesh.vertices = vertices;
       awesomeMesh.triangles = triangles;

       awesomeMesh.RecalculateNormals();
}

Add the following to the global variables above the Start() routine:

    [Range(0, 2)]
    public int rotate = 0;

[Range(0, 2] is an attribute that tells the editor in Unity to render a slider with a range of 0-2 for the variable “rotate”.
Now let’s add code to the Update() routine that will rotate the starting point for the triangle:

private void Update()
{
        //clockwise winding
    triangles[0] = (0 + rotate) % 3;
    triangles[1] = (2 + rotate) % 3;
    triangles[2] = (1 + rotate) % 3;

    UpdateMesh();
}

Using the slider in the inspector will change the starting point of the triangle. See the “Triangles” fields to verify that the starting point is actually changing. Hit play and move the slider… the triangle does not change.

Rotate the vertex starting point of the triangle

Now let’s change the winding. Add the following global variable:

public bool clockwise = true;

Then add an if() statement to the Update() routine and add the counter clockwise winding:

private void Update()
{
    if (clockwise)
    {
        triangles[0] = (0 + rotate) % 3;
        triangles[1] = (2 + rotate) % 3;
        triangles[2] = (1 + rotate) % 3;
    }
    else
    {
        triangles[0] = (0 + rotate) % 3;
        triangles[1] = (1 + rotate) % 3;
        triangles[2] = (2 + rotate) % 3;
    }

    UpdateMesh();
}

Hit play in Unity and un-click the Clockwise checkbox. The triangle disappears. Click the Clockwise checkbox again and the triangle is back.
The winding of the triangle does the following: if the winding is clockwise the Material is rendered on top of the triangle and the bottom is transparent; if the winding is counter clockwise the Material is rendered on the bottom of the triangle and the top is transparent. If you switch from game view to scene view and move above and below the triangle you can see this in action.

Change the winding of the triangle

These are the basics of Mesh generation in Unity. Next time we can add loops to generate many vertices and calculate the triangles form there…

Interactive vertex manipulation fun…

Download link to Unity Project and info on Gizmos!

Become a Patron!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.