[Unity] Generate vertex normals for the mesh (Mesh.RecalculateNormals calculation exception solution)

background

Meshes that we create dynamically through code will not receive lighting because they have no normals.

Under normal circumstances, call the Mesh.RecalculateNormals method to regenerate the normal.

But under certain circumstances, the vertices calculated by this method are found to be (0, 0, 0). In this case, the normal can only be generated manually.

As shown in the figure below, the object on the left has the correct normal and can receive light information, while the object on the right cannot be normal and cannot receive light.
insert image description here

The reason for the abnormal calculation of RecalculateNormals

After testing, it is found that the calculation of Mesh.RecalculateNormals is abnormal:
if a certain vertex in the Mesh, in the triangle label array, draws both the front mesh and the reverse mesh, it will cause an error in the calculation of RecalculateNormals. The calculation result of the point normal is (0, 0, 0).

Solution 1

For double-sided meshes, the vertex array is doubled to draw the mesh on the opposite side, and put them into the triangle label array of Mesh to ensure that the normal calculation of each point is correct.

Solution 2

Use the Generate Vertex Normals algorithm below to manually calculate the desired normal array.

principle

Normal : A normal is a straight line that is always perpendicular to a plane. In geometry, a normal is a line on a plane perpendicular to the tangent to a curve at a point.

Grid normal array : the normal array stores the normal of each vertex of the Mesh, the size of the array corresponds to the vertex coordinates, and normals[i] corresponds to the normal of vertices[i].

In Max Wagner's "Generating Vertex Normals" article - Strictly speaking, points have no normals. The point's normal is used to calculate lighting when using Phone or Gouraud models. If all the normals on a face are the same, their lighting will be the same, which will produce a flatness effect; and if the normals of each vertex are set differently, it will be smoother.

The meshes we create through code are generally polygons, so assign each vertex the normal of the plane it is on.

Algorithm for generating vertex normals

insert image description here
Create a grid and set the eight points, triangle sequence, uv as shown above

 		Mesh mesh = new Mesh();
        Vector3[] vertices = new Vector3[8];
        vertices[0] = new Vector3(0,0,0);
        vertices[1] = new Vector3(0,1,0);
        vertices[2] = new Vector3(1,0,0);
        vertices[3] = new Vector3(1,1,0);
        
        vertices[4] = new Vector3(1,0,0);
        vertices[5] = new Vector3(1,1,0);
        vertices[6] = new Vector3(2,0,1);
        vertices[7] = new Vector3(2,1,1);
        mesh.vertices = vertices;
        
        int[] triangles = new int[12];
        triangles[0] = 0;
        triangles[1] = 1;
        triangles[2] = 3;
        
        triangles[3] = 2;
        triangles[4] = 0;
        triangles[5] = 3;
        
        triangles[6] = 4;
        triangles[7] = 5;
        triangles[8] = 7;

        triangles[9] = 6;
        triangles[10] = 4;
        triangles[11] = 7;
        
        mesh.triangles = triangles;

        Vector2[] uv = new Vector2[8];
        uv[0] = new Vector2(0,0);
        uv[1] = new Vector2(0,1);
        uv[2] = new Vector2(1,0);
        uv[3] = new Vector2(1,1);
        
        uv[4] = new Vector2(0,0);
        uv[5] = new Vector2(0,1);
        uv[6] = new Vector2(1,0);
        uv[7] = new Vector2(1,1);
        mesh.uv = uv;

Generate normal array

 Vector3[] normals = new Vector3[8];
        Vector3 v1 = Vector3.Cross(vertices[0] - vertices[1], vertices[0] - vertices[3]);
        normals[0] = v1;
        normals[1] = v1;
        normals[2] = v1;
        normals[3] = v1;

        Vector3 v2 = Vector3.Cross(vertices[4] - vertices[5], vertices[4] - vertices[7]);
        normals[4] = v2;
        normals[5] = v2;
        normals[6] = v2;
        normals[7] = v2;
        mesh.normals = normals;
        //mesh.RecalculateNormals();

final effect

insert image description here

(In this case, using Mesh.RecalculateNormal can also generate the correct normal.)

The complete code, added points, and the comment part is to add the back grid

   public Mesh CreatMeshTest()
    {
    
    
        Mesh mesh = new Mesh();
        Vector3[] vertices = new Vector3[12];
        vertices[0] = new Vector3(0,0,0);
        vertices[1] = new Vector3(0,1,0);
        vertices[2] = new Vector3(1,0,0);
        vertices[3] = new Vector3(1,1,0);
        
        vertices[4] = new Vector3(1,0,0);
        vertices[5] = new Vector3(1,1,0);
        vertices[6] = new Vector3(0.5f,0,1);
        vertices[7] = new Vector3(0.5f,1,1);
        
        vertices[8] = new Vector3(0.5f,0,1);
        vertices[9] = new Vector3(0.5f,1,1);
        vertices[10] = new Vector3(0,0,0);
        vertices[11] = new Vector3(0,1,0);
        mesh.vertices = vertices;
        
        int[] triangles = new int[18];
        triangles[0] = 0;
        triangles[1] = 1;
        triangles[2] = 3;
        
        triangles[3] = 2;
        triangles[4] = 0;
        triangles[5] = 3;
        
        triangles[6] = 4;
        triangles[7] = 5;
        triangles[8] = 7;

        triangles[9] = 6;
        triangles[10] = 4;
        triangles[11] = 7;
        
        triangles[12] = 8;
        triangles[13] = 9;
        triangles[14] = 11;

        triangles[15] = 10;
        triangles[16] = 8;
        triangles[17] = 11;
        
        // triangles[18] = 0;
        // triangles[19] = 3;
        // triangles[20] = 1;
        //
        // triangles[21] = 2;
        // triangles[22] = 3;
        // triangles[23] = 0;
        //
        // triangles[24] = 4;
        // triangles[25] = 7;
        // triangles[26] = 5;
        //
        // triangles[27] = 6;
        // triangles[28] = 7;
        // triangles[29] = 4;
        //
        // triangles[30] = 8;
        // triangles[31] = 11;
        // triangles[32] = 9;
        //
        // triangles[33] = 10;
        // triangles[34] = 11;
        // triangles[35] = 8;
        
        mesh.triangles = triangles;

        Vector2[] uv = new Vector2[12];
        uv[0] = new Vector2(0,0);
        uv[1] = new Vector2(0,1);
        uv[2] = new Vector2(1,0);
        uv[3] = new Vector2(1,1);
        
        uv[4] = new Vector2(0,0);
        uv[5] = new Vector2(0,1);
        uv[6] = new Vector2(1,0);
        uv[7] = new Vector2(1,1);
        
        uv[8] = new Vector2(0,0);
        uv[9] = new Vector2(0,1);
        uv[10] = new Vector2(1,0);
        uv[11] = new Vector2(1,1);
        mesh.uv = uv;
        
        Vector3[] normals = new Vector3[12];
        Vector3 v1 = Vector3.Cross(vertices[0] - vertices[1], vertices[0] - vertices[3]);
        normals[0] = v1;
        normals[1] = v1;
        normals[2] = v1;
        normals[3] = v1;

        Vector3 v2 = Vector3.Cross(vertices[4] - vertices[5], vertices[4] - vertices[7]);
        normals[4] = v2;
        normals[5] = v2;
        normals[6] = v2;
        normals[7] = v2;
        
        Vector3 v3 = Vector3.Cross(vertices[8] - vertices[9], vertices[8] - vertices[11]);
        normals[8] = v3;
        normals[9] = v3;
        normals[10] = v3;
        normals[11] = v3;
        mesh.normals = normals;
        //mesh.RecalculateNormals();
        return mesh;

    }

Guess you like

Origin blog.csdn.net/boyZhenGui/article/details/129853731