Explaination about application of UVs on procedural cube generation

Hi there, I’m playing with procedural generation. I’m starting with something simple, just a cube. I’m following this guide.

I have a question about it: why do we need to use the following vertex order to apply UVs correctly?

Vector3[] vertices = {
            new Vector3(0, size, 0),
            new Vector3(0, 0, 0),
            new Vector3(size, size, 0),
            new Vector3(size, 0, 0),

            new Vector3(0, 0, size),
            new Vector3(size, 0, size),
            new Vector3(0, size, size),
            new Vector3(size, size, size),

            new Vector3(0, size, 0),
            new Vector3(size, size, 0),

            new Vector3(0, size, 0),
            new Vector3(0, size, size),

            new Vector3(size, size, 0),
            new Vector3(size, size, size),
        };

Particularly, I don’t understand why we set the following 3 couples in this specific order:

...
...
new Vector3(0, size, 0),
new Vector3(size, size, 0),

new Vector3(0, size, 0),
new Vector3(0, size, size),

new Vector3(size, size, 0),
new Vector3(size, size, size),
...
...

I’ve tried to change vertices order (and also UVs) but I’ve only got messed results…

Is there a specific reason why only the following solution works to create a cube with correct UVs?

Thank you :slight_smile:

Because by convention in Unity, the vertex array is in precise synchrony with the UV array, the normal array, colors, tangents, and probably more I can’t think of off the top of my head.

Unity is also left-hand-rule, so triangle windings are left-handed. If that doesn’t mean anything to you, start googling.

If you want to see more UVs and procedural geometry generation code, check out my MakeGeo project:

MakeGeo is presently hosted at these locations:

https://bitbucket.org/kurtdekker/makegeo

1 Like

There is a possibility that the image and the code are not in relation.
What you can do is just draw only one triangle
and then a face. Just the “front” face or first surface.
After you get the visual feedback you add the second “back” surface.
And so on.

Thanks for the answer!

The fact is that I don’t understand why in the article starts from 0-2, continues through 1-3, 4-5, 6-7, 8-9 (that are equals to 0-2, again), and finally uses 0-6 (why???) and 2-7 (why???). Why this specific order?

I tried to change it but results are completely graphically incorrect.

7435859--911093--upload_2021-8-20_23-37-9.png


I tried to do it by myself, changing order of vertices and I got the result, but the top face is wrong, I don’t understand why.

7435883--911117--upload_2021-8-20_23-49-38.png7435883--911120--upload_2021-8-20_23-49-38.png

This is the code made to generate such cube

GameObject test = new GameObject();
        test.transform.name = "test";
        test.AddComponent<MeshRenderer>();
        test.AddComponent<MeshFilter>();

        Material material = Resources.Load("Vehicle/TestMaterial") as Material;

        Mesh mesh = new Mesh();
        // vertices
        int rings = 2;                                                              // CHANGED
        //int rings = 3;                                                            // ORIGINAL
        var vertices = new List<Vector3>(4 * rings + (2 + 2 + 2));                  // CHANGED
        //var vertices = new List<Vector3>(4 * rings);                              // ORIGINAL
        // uvs
        var uvs = new List<Vector2>(vertices.Capacity);
        //float[] csX = new[] { -1f, 1f, 2f };                                      // CHANGED
        float[] csX = new[] { -1f, 1f };                                            // ORIGINAL
        float[] csY = new[] { -1f, 1f };
        float[] csZ = new[] { -1f, 1f };

        #region original
        foreach (float z in csZ)
        {
            foreach (float y in csY)
            {
                foreach (float x in csX)
                {
                    vertices.Add(new Vector3(x, y, z));
                }
            }
        }

        #region Extra vertices for UVs
        vertices.Add(vertices[2]);              // 2 - EXTRA
        vertices.Add(vertices[3]);              // 3 - EXTRA

        vertices.Add(vertices[2]);              // 2 - EXTRA
        vertices.Add(vertices[6]);              // 6 - EXTRA

        vertices.Add(vertices[3]);              // 3 - EXTRA
        vertices.Add(vertices[7]);              // 7 - EXTRA
        #endregion
        #endregion


        // triangles
        var triangles = new List<int>(((4 * (csX.Length - 1)) + 2) * 2 * 3);
        int[] aa = new[] { 1,2,4, 2,4,1, 4,1,2 }; // three directions to get face vertices                // ORIGINAL
        //int[] aa = new[] { 1,3,6, 1,2,4, 3,6,1, 6,1,3 }; // three directions to get face vertices         // CHANGED

        for (int a = 0; a < aa.Length; a += 3)
        {
            int[] quad = new[] { 0, aa[a], aa[a + 1], aa[a] + aa[a + 1] }; // aa{1,2,4} -> {0,1,2,3};  aa{2,4,1} -> {0,2,4,6};  aa{4,1,2} -> {0,4,1,5}
            int s = aa[a + 2];
            // holes
            bool skipFrontFace = (2 > 1 && a == 3);
            bool skipBackFace = (2 > 0 && a == 3) || (2 > 2 && a == 0);
          
            // Original
            #region original
            if (!skipFrontFace)
                triangles.AddRange(new[] { 0, 2, 3, 0, 3, 1 }.Select(i => quad[i]));
            if (!skipBackFace)
                triangles.AddRange(new[] { 3, 2, 0, 3, 0, 1 }.Select(i => quad[i] + s));
            #endregion
        }


        // uvs
        uvs.Add(new Vector2(0.25f, 0.66f));                 // 0
        uvs.Add(new Vector2(0.25f, 0.33f));                 // 1
        uvs.Add(new Vector2(0, 0.66f));                     // 2
        uvs.Add(new Vector2(0, 0.33f));                     // 3

        uvs.Add(new Vector2(0.5f, 0.66f));                  // 4
        uvs.Add(new Vector2(0.5f, 0.33f));                  // 5
        uvs.Add(new Vector2(0.75f, 0.66f));                 // 6
        uvs.Add(new Vector2(0.75f, 0.33f));                 // 7

        uvs.Add(new Vector2(1, 0.66f));                     // 8
        uvs.Add(new Vector2(1, 0.33f));                     // 9

        uvs.Add(new Vector2(0.25f, 1));                     // 10
        uvs.Add(new Vector2(0.5f, 1));                      // 11

        uvs.Add(new Vector2(0.25f, 0));                     // 12
        uvs.Add(new Vector2(0.5f, 0));                      // 13


        mesh.SetVertices(vertices.ToArray());
        mesh.SetTriangles(triangles.ToArray(), 0);
        mesh.SetUVs(0, uvs);
        mesh.Optimize();
        mesh.RecalculateNormals();

        test.GetComponent<MeshFilter>().mesh = mesh;
        test.GetComponent<MeshRenderer>().material = material;

7435883--911096--upload_2021-8-20_23-42-43.png
7435883--911105--upload_2021-8-20_23-46-28.png
7435883--911117--upload_2021-8-20_23-49-38.png
7435883--911120--upload_2021-8-20_23-49-38.png

If you share a vertex you also share a normal, a UV, a color, etc.

This means it is NOT possible to wrap a single 2D sheet of paper around a cube without some type of discontinuity if you are sharing all vertices.

Traditionally in a cube this is handled by having separate vertices for each of the three faces that a corner shares. You can make some number of them shared if you want continuous mapping, obviously.

Yes, the light is not correct because the vertex normal is not normal to the surface and since is sharing, is making an average. to inspect your mesh you can export it as FBX using FBX exporter to for example Blender. And in it inspect the vertex normal as an example: LINK
I often use youtube to search for solutions: https://www.youtube.com/results?search_query=unity+c#+cube+mesh
Maybe useful

Thank you for the answer. This is a good information, thank you!

So, considering what you said, the discontinuity you’re talking about is made by adding 8-9, 10-11, 12-13 vertices? Right?

That is not the case with the example I have proposed, is it?

Thank you for the answer.

Are you talking about light cause I made this post ?

Thank you for the content, it could be really helpful!

I have never made a mesh via code.
In a 3D editor application (Blender and Rhino),
I make a mesh with 2 different surfaces. Apply UV mapping.
I save it as a Blender file.
I also export it in some other extension and zip it
Enclosed you will find the file so that you can inspect them.
I do not know if is useful but the meshes are 90° and correct normals.
You can export your mesh out from Unity to blender using FBX exporter.
Import your mesh to Blender
Export your selected mesh as for example obj
And compare the difference to understand how: UV mapping and normals are applied.

7445261–912908–TwoTriangles90Degree.zip (101 KB)