Making a procedurally generated mesh smooth

Hi,

I am procedurally generating a mesh, mostly following Morten Nobel’s tutorial. I need to produce a smooth surface that is not too computationally heavy to generate and update for WebGL. I have used unique, not shared vertices.

Below I have two images: In the first, I am using Unity’s RecalculateNormals(), and the result is not smooth at all. In the second image, I used schemingdeveloper’s script, which gives a smooth result. However, as I need live updating with sliders, his method is quite slow and updating is jagged.

If anyone has a suggestion how I could create a smooth surface that can be updated relatively fast for WebGL, I would be very grateful.

There’s 2 things I see after a quick look;

  1. Remove the angle limit code in the 2nd loop. Just replace the if statement with the true case (sum += triNormals[…]). You don’t seem to be using it in the pictures, and Mathf.Acos is quite slow.

The author is being a bit silly here. He’s calling Mathf.Round and then casting to longs for the VertexKey struct. You can safely compare floats after calling Mathf.Round, as that gets rid of the small inconsistencies which are the reason not the check. Replacing the longs with the floats may improve performance.

  1. isn’t so important, and probably only helps a bit. 1) may double the speed or so. (hopefully)

Thank you!

Here’s what I did:

        foreach (var value in dictionary.Values) {
            for (var i = 0; i < value.Count; ++i) {
                var sum = new Vector3();
                for (var j = 0; j < value.Count; ++j) {
//                    if (value.VertexIndex[i] == value.VertexIndex[j]) {
//                        sum += triNormals[value.TriangleIndex[j]];
//                    } else {
//                        float dot = Vector3.Dot(
//                            triNormals[value.TriangleIndex[i]],
//                            triNormals[value.TriangleIndex[j]]);
//                        dot = Mathf.Clamp(dot, -0.99999f, 0.99999f);
//                        float acos = Mathf.Acos(dot);
//                        if (acos <= angle) {
//                            sum += triNormals[value.TriangleIndex[j]];
//                        }
//                    }

                    sum += triNormals[value.TriangleIndex[j]];
                }

                normals[value.VertexIndex[i]] = sum.normalized;
            }
        }

Unfortunately, this shows the mesh completely black. I am not really understanding what is going on in this code. Perhaps I misunderstood your instruction?

Since I am very new to how meshes are represented, I have to ask: Is there a more straightforward way to procedurally create a mesh than optimising this guy’s code? I am not sure if shared vertices could help, since they would allow me to drastically reduce the verts count, possibly at the expense of normals information. Or was my initial approach of unique (as opposed to shared) vertices a good idea?

Ah, the way you stated you’re using unique vertices in the OP made me think this was an algorithm/given thing.
Weird that it goes black, I must’ve missed something.
Shared vertices are a solution here. Then you should be able to call mesh.recalculatenormals. Generally you should use shared vertices wherever possible. Often this isn’t the case due to the need for complex uv coordinates for textures and light mapping.

Thanks Zuntatos,

you’ve been very helpful! I finally got around to writing my surface with shared rather than unique vertices and applying the usual mesh.recalculatenormals(), which seems to work very well.

However, since the normals are averaged for every vertex, I cannot simply use the same vertices to describe front and back of a surface. My understanding is that I can either double the number of vertices to describe the inside of the surface (with shared vertices), or use a double sided (cull off) shader, which will get the lighting wrong. So far I opted for the first option.

This is the reason. You can’t have a smooth look unless those verts are shared. A workaround would be to make the 2 verts occupying the same space have identical normals.