Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

procedural mesh

Discussion in 'Editor & General Support' started by jujulien, Oct 15, 2014.

  1. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    Hi everybody

    warning google translation

    I try to produce randomly generated islands and , i don' t find a way to build normals.
    The island is divided into chunk exactly like minecraft with a little diference, the angles are slightly softens and the toon shader render is not smooth enough.
    If someone succeeds in understanding my english, any help will be welcome.


    http://hpics.li/fea706a


    http://hpics.li/005481c


    Thank you.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,738
  3. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    yes I am currently using this method, but I'm looking for a manually generate normal.
    as you can see, procedural mesh have not smoothing angle as import setting.
     
  4. Deon-Cadme

    Deon-Cadme

    Joined:
    Sep 10, 2013
    Posts:
    288
    Simplified explenation:

    Both the Mesh and MeshFilter objects got arrays for normals.
    Mesh.normals
    MeshFilter.mesh.normals.

    One normal is expected per vertex in the mesh that you are creating.
    Just calculate the normals like you want them and insert them into an array... provide them to the MeshFilter the same way you provide vertexes, uv etc...
     
  5. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    i can see an example please ?
     
  6. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    Conceptually, what you want to do is average the normals on either side of the vertex for each vertex.

    Currently I am also trying to figure out the process of smoothing normals. If I make any progress I will try get back to you on the matter.
     
  7. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
    The important thing for smoothing to work is that vertices should be shared. This means that two triangles that are joined by one edge should reference the same vertices for that edge.

    So in this case there would be only 4 vertices for two triangles, v1 - v4.
    Lets pretend that the shared edge is between v2 and v3.
    The triangles / indices list would look like this:

    // First triangle
    v1
    v2
    v3

    // Second triangle
    v3
    v2
    v4

    Since they share two vertices, the triangles also share normals. Then it should just automatically work when using calculated normals.

    What Unity does in response to you changing the import smoothing settings on a mesh is to split vertices. Every edge that should be sharp will have two separate sets of vertices, so that it can also have different normals.
    For two triangles that are joined by a hard edge, you would use 6 vertices and normals instead of 4.

    So, to recap:

    - if the normals should be smooth, use only one vertex / normal per position in space. Reference these shared vertices in your list of triangles, meaning same position references same vertex regardless of which triangle is using it.

    - if the normals should be sharp, use separate vertices for each triangle, even if multiple triangles share physical position in space.

    If you rather would like to avoid caring about smoothing when generating you vertices and triangles, do this instead:

    - Use separate vertices for each triangle.

    - When you have created a list of vertices / triangles, loop through them and calculate normals for each vertex. Do this by finding out which triangles meet at the current vertex position, and calculate the normal of each of those triangles. Then average the calculated face normals by adding them together and divide by the number of face nromals ( (fN1 + fN2 + fN3) / 3) if there are three triangles that meet at the current location). Use this averaged normal as vertex normal for all vertices that have this position in space.

    To calculate a normal yourself, use this http://docs.unity3d.com/Manual/ComputingNormalPerpendicularVector.html

    Note that if you use the last method, smooth geometry will use more vertices and normals than really needed, so the performance will not be as good.
     
  8. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    @Mistale
    From my own tests I discovered I was tripling up on normals. I'll have to do a run through and edit my code so it will remove any excess.



    Which explains why the normals made the mesh so 'clunky'
     
  9. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
    @Myhijim : Exactly, you'll get better performance AND looks if you avoid duplicating vertices and normals for smooth objects.
     
  10. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    Thanks for the heads up, I was doing some smoothing before I decided I would draw the normals and see what was happening.

    Was stupidly wondering what it was all about, I had a little bit of a clue, but when you said it, made me go "Ah of course". And I just recently found something very similar to what you said in the docs.

    So yeah, thanks a bunch.
     
  11. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    @Mistale Just an update


    Removed all the verts and sheesh it runs faster, although it still has some bits, I may do a runthrough and check, if not, I'll have to average the normals anyway.
     
  12. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
    Hmm, there are some duplicates left there for sure. But since I don't know eactly how you are building you mesh, it's hard to give any pointers as to how to best make sure you're sharing all verts.
     
  13. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    There is still some dupes, but only one or 2 per 10, so this is frankly annoying. I'll have to do a cycle and figure out what is wrong.

    As for generation, I am using fractal noise in volumetric data to Marching Cubes algorithm. But shouldn't make a difference to the normals/verts.

    Edit : It also seems there are a small amount of extra tris being created..... hmmmn

    Code (CSharp):
    1.  
    2. int sameVertIndex = 0;
    3.                          
    4. //Loop?
    5. if (verticies [currentVert - 1] == newVert) {
    6. sameVertIndex = 1;
    7. }
    8. if (verticies [currentVert - 2] == newVert) {
    9. sameVertIndex = 2;
    10. }
    11. if (verticies [currentVert - 3] == newVert) {
    12. sameVertIndex = 3;
    13. }
    14.                                          
    15.                          
    16. if (sameVertIndex == 0) {
    17. verticies.Add (newVert);
    18. }
    19. triangles.Add (currentVert - sameVertIndex);
    I'll probably just have to generate all the verticies and then cycle through all and remove those that are equal, and setting the triangle index at that point to where it is in the cycle. Shouldn't be too hard, I was only doing it this way for hopeful performance.
     
    Last edited: Oct 16, 2014
  14. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
    @Myhijim : Yes, you're proably just missing a few cases where the vertices that should be shared are further apart in the list than 3 steps.
    I'm actually working on the exact same thing, but for runtime import of Collada files.
    The only really safe way to find all possible shared vertices is to loop through your already added vertices from n to 0 or until you find a match. It's not fast, but if the vertex positions are not following a pattern, I think it's the only way.
    In my case, I'm doing this but with separate buffers for position, normal, vertexcolor, uv1 and uv2. All buffers have to be merged into a single list of unique vertex attributes where no combination of attributes are ever duplicated.
    I do this for 300k+ vertex meshes on an iPad Air, and it only takes about a second, so if it doesn't have to be done each frame, I say go for a complete loop.

    In my case, since I have so many checks to make in each iteration, I first compare against a hashed value before checking each attribute for equality. It may not be worth it for you, but you can try and see if it makes a difference. Just store the sum of x+y+z as a float in a separate array, and compare with this number before checking if the vectors are equal. If the hashed values are different, there's bo need to check the vector.
    And I also found out the hard way that it was faster to check x == otherx && y == othery && z == otherz than vector1 == vector2. Probably just because there can be overhead in calling Vector3's equality operator.
     
  15. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    Yes, I thought it may be something about the verticies not being uniform. Lucily I shouldn't see too much overhead as technically there will be a max of 5 triangles in the worst case scenario per frame. Although it will get much slower at the higher levels, however the data is only in a 16x16x16 grid and is chunked into different meshes.

    Will update soon, currently fixing it. Thanks for the heads up about the Vector3 overhead.

    @Mistale Yep that did it. Just doing a loop through the existing verts and breaking when finding one works just fine. It appears to have no impact on mesh generation time surprisingly, if there is any impact it is trivial.

    Some code, that really needs to be reworked. But for now it serves it purpose.
    Code (csharp):
    1.  
    2. for (int v=0; v<3; v++)
    3. {
    4. Vector3 newVert = edgeIntersections [MCLookup.triTable [lookupIndex] [i + v]];
    5.  
    6. int currentVert = verticies.Count;
    7.  
    8.            int equalIndex = 0;
    9.            bool equalVerts = false;
    10.  
    11.                    for(int j=0; j<currentVert; j++)
    12.                    {
    13.                    equalVerts = newVert.x == verticies[j].x && newVert.y == verticies[j].y && newVert.z == verticies[j].z;                                              
    14.                                          
    15.                                   if(equalVerts)
    16.                                   {equalIndex = j;        break;}
    17.                     }
    18.                                          
    19.                     if(equalVerts)
    20.                     {
    21.                     triangles.Add (equalIndex);
    22.                     }
    23.                     else
    24.                     {
    25.                     verticies.Add (newVert);
    26.                     triangles.Add (currentVert);
    27.                      }
    28. }
    29.  
    Works a dream :



    EDIT : It is still producing a few more triangles than necessary, but it is probably an issue with my code. I'll find it really soon.
     
    Last edited: Oct 17, 2014
  16. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
  17. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    i am lost :/
    normals will drive me crazy

     
  18. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    Look at that picture, it's not necessarily right. When you think about it, when the mesh is being created, many of the vertices are doubled.

    With something like this pseudo-code you should be able to understand. This would go at the end of generation, before Mesh.RecalculateNormals(). (Ofcourse it would be better to do something like it DURING generation).

    Code (CSharp):
    1.  
    2. for(int i=0; i<verticies.length; i++)
    3. for(int j=0; j<verticies.length; j++)
    4. {
    5.      if(verticies[i] == verticies[j])
    6.       {
    7.           verticies.RemoveAt(j);
    8.           triangles[j] = triangles[i];
    9.       }
    10. }
    So when you look at this all you are doing is cycling through the verts and seeing if any of them equal. If they do equal, it will remove unnecessary vertices (thus improving the recalculation of normals) and set the triangles to the point where the original(not duplicated) vertex was
     
  19. Mistale

    Mistale

    Joined:
    Apr 18, 2012
    Posts:
    173
    I still don't know if you want smooth normals or hard? But judging from the picture, you share vertices for multiple triangles.

    If so, further down you'll find some code to get you started.
    The easiest way to calculate smooth normals is to avoid duplicate vertices.

    Use the same vertex for all corners of triangles that meet at the same place.
    Don't assign normals for the mesh at all, just assign vertices, triangles, colors, uv or whatever you need.
    Then use mesh.RecalculateNormals() after you've set the vertices and triangles for your mesh.

    If you still want to calculate the normals yourself, there's code for that in my example as well. It gives the same result as with mesh.RecalculateNormals(), but you get a Vector3 array with normals that you can use.

    If you want to have hard edges in some places, then DON'T use the same vertices for the triangles that meet on that edge, instead make duplicates for one of the triangles.
    That's also covered in my example, with the extra vertices and triangles that I added.

    If you have duplicate vertices and want to get rid of them to be able to get smooth normals, just look at mine and Myhijim's discussion in this thread about looping through all vertices and just adding the unique vertices to a new list or array (and also changing the references for all triangles to point to the correct vertices).

    Example output from my code:

    Picture

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MeshExample : MonoBehaviour
    5. {  
    6.     void Start ()
    7.     {
    8.         // Set this to false to use Mesh.RecalculateNormals()
    9.         bool    calculateNormalsManually    = true;
    10.  
    11.         Vector3    []    vertices    = new Vector3[] {
    12.                                             new Vector3(0.1f, 1.2f, 0.9f),     // vert_0
    13.                                             new Vector3(1.1f, 1.1f, 0.8f),     // vert_1
    14.                                             new Vector3(0.8f, 0.9f, 0.3f),     // vert_2
    15.                                             new Vector3(0.2f, 1.1f, 0.2f),     // vert_3
    16.                                             new Vector3(0.0f, 0.1f, 0.1f),     // vert_4
    17.                                             new Vector3(1.1f, 0.1f, 0.0f),     // vert_5
    18.                                             new Vector3(1.2f, 0.1f, 1.2f),     // vert_6
    19.  
    20.                                             // Added four vertices to show how to achieve
    21.                                             // sharp normals
    22.                                             new Vector3(0.1f, 1.2f, 0.9f),     // vert_7, copy of vert_0
    23.                                             new Vector3(1.1f, 1.1f, 0.8f),     // vert_8, copy of vert_1
    24.                                             new Vector3(0.1f, 2.2f, 0.9f),     // vert_9, same as vert_7 but higher
    25.                                             new Vector3(1.1f, 2.1f, 0.8f)    // vert_10, same as vert_8 but higher
    26.                                         };
    27.  
    28.         int    []    triangles    = new int[] {
    29.                                             0, 1, 2,    // triangle 0
    30.                                             0, 2, 3,    // triangle 1
    31.                                             2, 5, 3,    // triangle 2
    32.                                             3, 5, 4,    // triangle 3
    33.                                             2, 6, 5,    // triangle 4
    34.                                             6, 2, 1,    // triangle 5
    35.  
    36.                                             // Added two triangles to show how to achieve
    37.                                             // sharp normals
    38.                                             8, 7, 9,    // triangle 6
    39.                                             9, 10, 8    // triangle 7
    40.                                         };
    41.  
    42.         // Create a procedural mesh
    43.         Mesh    mesh    = new Mesh();
    44.  
    45.         mesh.vertices    = vertices;
    46.         mesh.triangles    = triangles;
    47.  
    48.         // Setup normals
    49.         if (calculateNormalsManually)
    50.         {
    51.             mesh.normals    = GetVertexNormals(vertices, triangles);
    52.         }
    53.         else
    54.         {
    55.             mesh.RecalculateNormals();
    56.         }
    57.  
    58.         // Log all normals. Fetching from
    59.         // mesh so that it works even with
    60.         // mesh.RecalculateNormals()
    61.         Vector3    []    meshNormals    = mesh.normals;
    62.  
    63.         for (int i = 0; i < meshNormals.Length; i++)
    64.         {
    65.             Debug.Log("Normal for vertex " + i + ": " + meshNormals[i]);
    66.         }
    67.  
    68.         // Add Meshrenderer and MeshFilter to display the mesh
    69.         MeshRenderer    mr    = gameObject.AddComponent<MeshRenderer>();
    70.         MeshFilter        mf    = gameObject.AddComponent<MeshFilter>();
    71.  
    72.         mf.sharedMesh    = mesh;
    73.         mr.material        = new Material(Shader.Find("Diffuse"));
    74.     }
    75.  
    76.  
    77.     /// <summary>
    78.     /// Calculates vertex normals for the supplied vertices.
    79.     /// If a vertex is shared between multiple triangles, it's
    80.     /// normal will be smooth. Otherwise it will be the same
    81.     /// as the triangle's normal.
    82.     /// </summary>
    83.     /// <returns>The vertex normals.</returns>
    84.     /// <param name="vertices">Vertices.</param>
    85.     /// <param name="triangles">Triangle indices.</param>
    86.     Vector3    []    GetVertexNormals(Vector3 [] vertices, int [] triangles)
    87.     {
    88.         Vector3    []    normals        = new Vector3[vertices.Length];
    89.         int        []    normalSum    = new int[normals.Length];
    90.  
    91.         // Calculate face normals for each triangle and add them
    92.         // to each vertex normal used in each triangle
    93.         for (int i = 0; i < triangles.Length; i+= 3)
    94.         {
    95.             int    index0    = triangles[i];            // Index to vertex 0 in this triangle
    96.             int    index1    = triangles[i + 1];        // Index to vertex 1 in this triangle
    97.             int    index2    = triangles[i + 2];        // Index to vertex 2 in this triangle
    98.  
    99.             // Calculate side vectors for triangel (vert0 => vert1, and vert1 => vert2)
    100.             Vector3    side1    = vertices[index1] - vertices[index0];
    101.             Vector3    side2    = vertices[index2] - vertices[index1];
    102.  
    103.             // Calculate face normal of the triangle.
    104.             // Do not normalize it yet, that will happen later
    105.             Vector3    normal    = Vector3.Cross(side1, side2);
    106.  
    107.             // Add this normal to each vertex normal in the triangle
    108.             normals[index0]    += normal;
    109.             normals[index1]    += normal;
    110.             normals[index2]    += normal;
    111.  
    112.             // Add 1 to the sum of each vertex normal to let us keep track
    113.             // of how many face normals are summed into each vertex normal
    114.             normalSum[index0]++;
    115.             normalSum[index1]++;
    116.             normalSum[index2]++;
    117.         }
    118.  
    119.         // Step through all vertex normals and calculate an average of all
    120.         // summed face normals, to get a proper vertex normal.
    121.         // If the count is 1 for a specific vertex normal, that means that
    122.         // no other triangles are using that vertex and therefore it will
    123.         // essentially become a face normal, i.e "sharp"
    124.         for (int i = 0; i < normals.Length; i++)
    125.         {
    126.             normals[i]    = (normals[i] / normalSum[i]).normalized;
    127.         }
    128.  
    129.         return normals;
    130.     }
    131. }
    132.  
     
  20. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    thank you for spending time on my problem :)

    i want smooth normals and don't share vertices for multiple triangles, sorry not being specific enough.
    i build cube by cube like minecraft tuto and move vertices to have more rounded shapes.
     
  21. Myhijim

    Myhijim

    Joined:
    Jun 15, 2012
    Posts:
    1,148
    If you are making the mesh into 'chunks' you just need to remove the duplicated vertices to make the normals smoother
     
  22. jujulien

    jujulien

    Joined:
    Aug 26, 2012
    Posts:
    8
    thank you comm-UNITY 3D

    problem solved

     
    Mistale likes this.
  23. rndrobin

    rndrobin

    Joined:
    Aug 13, 2014
    Posts:
    5