Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Generating Mesh render issues

Discussion in 'Scripting' started by HeyBishop, Jul 5, 2021.

  1. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    I've written a script to create a mesh based on an array of points.

    It's mostly working - but the way light is interacting with it kinda funky. It's like the light is being stretched.
    In this example video, I include a primitive cube to demonstrate how the lighting should work.


    Here is my code:
    Code (CSharp):
    1.     public Vector3[] corners;
    2.     public Material material;
    3.     public int numOfStories = 2;
    4.     public float storyHeight = 3f;
    5.  
    6.  
    7.     private void Start()
    8.     {
    9.         CreateHouse();
    10.     }
    11.  
    12.     void CreateHouse()
    13.     {
    14.         GameObject house = new GameObject("building.name", typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider));
    15.         house.transform.parent = transform;
    16.  
    17.         Vector3[] vertices = new Vector3[corners.Length * 2];
    18.         int[] triangles = new int[corners.Length * 2 * 6];   // num walls * number of triangles per side * num points per
    19.  
    20.        // set vertices
    21.         for (int i = 0; i < corners.Length; i++)
    22.         {
    23.             vertices[i] = new Vector3(corners[i].x, -storyHeight, corners[i].z);
    24.             vertices[i + corners.Length] = new Vector3(corners[i].x, storyHeight * numOfStories, corners[i].z);
    25.         }
    26.  
    27.         // calculates triangles for all but last wall
    28.         for (int vert = 0, tris = 0; tris <= corners.Length * (corners.Length-1);)
    29.         {
    30.             triangles[tris + 0] = vert + 0;
    31.             triangles[tris + 1] = vert + 1;
    32.             triangles[tris + 2] = vert + corners.Length + 1;
    33.             triangles[tris + 3] = vert + corners.Length + 1;
    34.             triangles[tris + 4] = vert + corners.Length;
    35.             triangles[tris + 5] = vert + 0;
    36.             vert++;
    37.             tris += 6;
    38.         }
    39.         // calculates triangles for last wall
    40.         triangles[corners.Length * 6 + 0] = corners.Length - 1;
    41.         triangles[corners.Length * 6 + 1] = 0;
    42.         triangles[corners.Length * 6 + 2] = corners.Length;
    43.         triangles[corners.Length * 6 + 3] = corners.Length;
    44.         triangles[corners.Length * 6 + 4] = corners.Length * 2 - 1;
    45.         triangles[corners.Length * 6 + 5] = corners.Length - 1;
    46.  
    47.  
    48.  
    49.         // Update Mesh
    50.         Mesh mesh = new Mesh();
    51.         MeshCollider meshCollider = house.GetComponent<MeshCollider>();
    52.  
    53.         mesh.Clear();
    54.         mesh.vertices = vertices;
    55.         mesh.triangles = triangles;
    56.         mesh.RecalculateNormals();
    57.         mesh.RecalculateTangents();
    58.  
    59.         house.GetComponent<MeshFilter>().mesh = mesh;
    60.  
    61.         house.GetComponent<MeshRenderer>().material = material;
    62.         mesh.RecalculateBounds();
    63.         meshCollider.sharedMesh = mesh;
    64.    
    65.     }

    Any ideas? I feel like I'm missing something obvious.
     
  2. TheFunnySide

    TheFunnySide

    Joined:
    Nov 17, 2018
    Posts:
    200
    It looks like you are getting smooth edges while wanting hard edges. I dont know how to fix this in unity but google is your friend.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    It's exactly this. I have a scene demoing what you're doing, and what you should be doing.

    You ARE sharing vertices at a corner, you need to NOT share vertices at a corner.

    Look at the
    SharedvsNonSharedVertices.unity
    scene in my MakeGeo project.

    MakeGeo is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/makegeo

    https://github.com/kurtdekker/makegeo

    https://gitlab.com/kurtdekker/makegeo

    https://sourceforge.net/p/makegeo
     
  4. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    ooooooh... I think I understand. Thank you.
    Here I thought I was being 'efficient'. Time to re-think. I'll post again later.
     
    Bunny83 likes this.
  5. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    I suppose sharing vertices on a single face is okay, right?
     
    Bunny83 likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    The key points are:

    - light is calculated from normals

    - if you want a face to be flat (faceted), all normals on it must be identical

    - normals are NOT mapped to faces

    - normals are 1-1 mapped to vertices

    Therefore if two edges share vertex positions AND you want both sides of that edge to be flat (faceted), then that edge must have a set of vertices for each face.
     
    HeyBishop likes this.
  7. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    Woohoo! I've got it working!
    Thank you again, @Kurt-Dekker
    Code (CSharp):
    1.     public Vector3[] corners;
    2.     public Material material;
    3.     public int numOfStories = 2;
    4.     public float storyHeight = 3f;
    5.  
    6.     private void Start()
    7.     {
    8.         CreateHouse();
    9.     }
    10.  
    11.     void CreateHouse()
    12.     {
    13.         GameObject house = new GameObject("building.name", typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider));
    14.         house.transform.parent = transform;
    15.  
    16.         Vector3[] vertices = new Vector3[corners.Length * 6];
    17.         int[] triangles = new int[corners.Length * 2 * 6];   // num walls * number of triangles per side * num points per
    18.  
    19.         // calculates verticies for each face
    20.         for (int corner = 0, face = 0; corner < corners.Length;)
    21.         {
    22.             int nextCorner = corner + 1;
    23.             if (nextCorner == corners.Length)
    24.                 nextCorner = 0;
    25.             //first triangle
    26.             vertices[face + 0] = new Vector3(corners[corner].x, -storyHeight, corners[corner].z);
    27.             vertices[face + 1] = new Vector3(corners[nextCorner].x, -storyHeight, corners[nextCorner].z);
    28.             vertices[face + 2] = new Vector3(corners[nextCorner].x, storyHeight * numOfStories, corners[nextCorner].z);
    29.             //second triangle
    30.             vertices[face + 3] = new Vector3(corners[nextCorner].x, storyHeight * numOfStories, corners[nextCorner].z);
    31.             vertices[face + 4] = new Vector3(corners[corner].x, storyHeight * numOfStories, corners[corner].z);
    32.             vertices[face + 5] = new Vector3(corners[corner].x, -storyHeight, corners[corner].z);
    33.  
    34.             corner++;
    35.             face += 6;
    36.         }
    37.  
    38.  
    39.         // initializes triangles for all
    40.         for (int i = 0; i < vertices.Length; i++) ///triangles.Length;)
    41.         {
    42.             triangles[i] = i;
    43.         }
    44.  
    45.  
    46.  
    47.         // Update Mesh
    48.         Mesh mesh = new Mesh();
    49.         MeshCollider meshCollider = house.GetComponent<MeshCollider>();
    50.  
    51.         mesh.Clear();
    52.         mesh.vertices = vertices;
    53.         mesh.triangles = triangles;
    54.         mesh.RecalculateNormals();
    55.  
    56.         house.GetComponent<MeshFilter>().mesh = mesh;
    57.  
    58.         house.GetComponent<MeshRenderer>().material = material;
    59.         mesh.RecalculateBounds();
    60.         meshCollider.sharedMesh = mesh;
    61.     }
    62. }
    63.  
     
    Bunny83 and Kurt-Dekker like this.
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,919
    Kudos, many people who come across this fact expect to keep their logic and somehow what the world to adapt to their code because they think that's how it should work :) You instantly understood the reason behind the issue and are willing to change your code, that's how you make progress.

    Philosophically 100% of the errors are in your own code and it's where you have to search for the root of the problem. Even when the engine has an actual error / mistake, it's still your code that was build on false assumptions on your side. That assumption may have been based on the documentation or common sense, however it doesn't change how the engine or a system actually behaves at that moment^^. So the root of most issues is missing knowledge / false assumptions. Of course we always hope that actual errors in the engine are fixed soon, however praying to the Unity devs will not get you a fix faster ^^. So for the time being you probably want to look for a workaround.

    Back to the topic: Yes shared vertices are indeed the issue. Though I'd like to add that there are ways to render flat-shaded meshes with shared vertices. However there are several disadvantages. You can use a special shader that uses the screen space partial derivatives (ddx and ddy) in the fragment shader to calculate the face normal on the fly for each fragment. The issues with this approach are:
    • Not every hardware supports those operations
    • You are forced to use a custom shader
    • Performance is probably worse than splitting vertices
    • In many cases, especially when using textures, you need to split the vertices anyways
    • GPUs are designed to have a high throughput by applying the same operations on a lot of data. So the amount of data is not that important as a lot of things can be done in parallel on the GPU. So reducing the amount of data slightly and instead using a more expensive operation may not give you any benefits.
    Just for completeness, here's how you can achieve flat shading with shared vertices.
     
    HeyBishop and Kurt-Dekker like this.
  9. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    Thank you for the kind words @Bunny83! I think in game development, one must always remain humble. There is so much to learn. I've been at this for about four years now, and I still feel like a rookie.

    I think your philosophy about bugs in our code vs. the engine's code is smart. Yes, there might be bugs courtesy of Unity - but the time frame on when (or if) they get fixed is unknowable, so just stop whining and find your way around them.
     
    Kurt-Dekker likes this.