- Home /
How to Procedurally Generate Smooth Meshes
This is somewhat related to my tangent smoothing angle question which I now don't think is what the problem is, so let me elaborate a bit. Basically I am procedurally generating a hex-sphere but it doesn't look smooth when it has six times the amount of vertices as the unity primitives sphere which looks far smoother. There is a screenshot below. My sphere starts as a cube then every vertex of the cube is normalized then set a specific distance (the radius of the sphere) from the center of it, in order to "spherize" it. My question is how can I get my sphere to look smooth like the unity primitives one?
EDIT: Okay so one thing I tried was not recalculating normals when I did this and my result was smooth but the lighting was messed up - maybe I should also mention that my sphere is made out of six different planes initially arranged as a cube. The six faces of the sphere seem to be under different lighting conditions. Screenshot below.
Answer by nesis · Feb 02, 2014 at 03:01 PM
The normals for each vertex should be the average of the faces that vertex is a part of. Surface shading comes from the normals of a mesh (which I'm guessing you know), but what you might not know is that, for each triangle, the intensity of shadowing is interpolated from vertex to vertex.
Now you just need to figure out a way a) get the triangles a vertex is part of, b) calculate the normals of those triangles, c) get the average of those normals, and d) set the vertex's normal to that average.
This was my interpretation of what you said, I'm pretty sure I misunderstood. The code below doesn't improve anything though.
void CalculateNormals ($$anonymous$$esh mesh) {
Vector3[] normals = mesh.normals;
int[] trigs = mesh.triangles;
for(int i = 0; i < trigs.Length; i+=3) {
Vector3 avg = (normals[trigs[i]] + normals[trigs[i+1]] + normals[trigs[i+2]])/3;
normals[trigs[i]] = avg;
normals[trigs[i+1]] = avg;
normals[trigs[i+2]] = avg;
}
}
Perhaps the fact that my sphere is separated into six faces makes that solution still not work properly.
In pseudocode:
//In C#, you'd add `using System.Collections.Generic` to the top of your file for List<> to work
Vector3[] normals = mesh.normals;
List<Vector3>[] vertexNormals = new List<Vector3>[normals.Length]; //array of lists, so each element stores a list of normals for that vertex, to be averaged later
//create list of normals for each vertex
for (int i=0; i<trigs.Length; i+=3) {
Vector3 currNormal = /*calculate current triangle's normal*/;
vertexNormals[trigs[i]].Add(currNormal);
vertexNormals[trigs[i+1]].Add(currNormal);
vertexNormals[trigs[i+2]].Add(currNormal);
}
//now we have the lists, calculate average normal for each vertex from its list of normals
for (int i=0; i<vertexNormals.Length; i++) {
normals[i] = Vector3.zero; //ensure the normal starts as a zero vector
//declared as float so we can divide as float with it later, might be ok as int though?
float numNormals = vertexNormals[i].Count;
for (int j=0; j<numNormals; j++) {
normals[i] += vertexNormals[i][j];
}
//gets the average of the normals now they're added together
normals[i].Scale(1f/numNormals,1f/numNormals,1f/numNormals);
}
mesh.normals = normals;
Thank you! Here is pseudo converted to C# :
Vector3[] normals = new Vector3[verts.Length];
List<Vector3>[] vertexNormals = new List<Vector3>[verts.Length];
for (int i = 0; i < vertexNormals.Length; i++)
{
vertexNormals[i] = new List<Vector3>();
}
for (int i = 0; i < triangles.Length; i += 3)
{
Vector3 currNormal = Vector3.Cross(
(verts[triangles[i + 1]] - verts[triangles[i]]).normalized,
(verts[triangles[i + 2]] - verts[triangles[i]]).normalized);
vertexNormals[triangles[i]].Add(currNormal);
vertexNormals[triangles[i + 1]].Add(currNormal);
vertexNormals[triangles[i + 2]].Add(currNormal);
}
for (int i = 0; i < vertexNormals.Length; i++)
{
normals[i] = Vector3.zero;
float numNormals = vertexNormals[i].Count;
for (int j = 0; j < numNormals; j++)
{
normals[i] += vertexNormals[i][j];
}
normals[i] /= numNormals;
}
mesh.normals = normals;
Thanks for sharing this code! I can get it to work, but it doesn't do anything visible to the mesh in my tries. Did you ever succeed?