I'm working on procedural planet generation project in Unity. To create a sphere, I use the following technique:
- Create six planes arranged as faces of a cube
- Normalize all vertices' positions to create a unit sphere
- Multiply each vertices' positions by the height value obtained from an
IHeightsProvider
(3D simplex noise in this case) + the planet'sradius
.
All operations necessary for creating a mesh (create vertex positions array, triangle indices array, normals array) are performed in a separate thread for performance reasons. Vertices, triangles, and normals, encapsulated in a MeshData
object, are assigned to Unity's Mesh
object in the main thread.
The problem is, of course, seams between faces of the cubesphere:
These issues are due to the flawed normal computation algorithm, which only takes normals of triangles in the mesh into account, being not aware of the adjacent meshes. Ideally, I'd like to have normals computed regardless of adjacent meshes' presence (i.e no SetNeigbors
), using the same heightsProvider
used for obtaining the mesh's own vertices' heights.
I've been trying to figure it out myself, as well as extensively searching the web, for a few days so far, but the problem doesn't seem to become any clearer.
Here's my code for creating vertices, triangles and normals in a separate thread:
protected override void ThreadFunction (System.Object info)
{
int resolution = parameters.resolution;
int numVertices = resolution * resolution;
float minHeight = parameters.minHeight;
float maxHeight = parameters.maxHeight;
Quaternion faceRotation = parameters.rotation;
// Create vertices
// ---------------
Vector3[] vertices = new Vector3[numVertices];
int vertNum = 0;
for (int x = 0; x < resolution; x++)
{
for (int y = 0; y < resolution; y++)
{
Vector3 posOnCube = GetVertexPosOnCube (x, y);
Vector3 posOnUnitSphere = CubeToSphere (posOnCube);
Vector3 samplePosition = posOnUnitSphere;
float height = GetHeight (samplePosition);
vertices[vertNum++] = posOnUnitSphere * height;
}
}
this.meshData.vertices = vertices;
// Create triangle indices array
// -----------------------------
int[] triangles = new int [(resolution - 1) * (resolution - 1) * 6];
int triangleIndex = 0;
int vertexIndex = 0;
for (int x = 0; x < resolution - 1; x++)
{
for (int y = 0; y < resolution - 1; y++)
{
triangles[triangleIndex] = vertexIndex;
triangles[triangleIndex + 3] = triangles[triangleIndex + 2] = vertexIndex + (resolution - 1) + 1;
triangles[triangleIndex + 4] = triangles[triangleIndex + 1] = vertexIndex + 1;
triangles[triangleIndex + 5] = vertexIndex + (resolution - 1) + 2;
triangleIndex += 6;
vertexIndex++;
}
vertexIndex++;
}
this.meshData.triangles = triangles;
// Calculate normals
// -----------------
Vector3[] normals = new Vector3 [numVertices];
for (int i = 0; i < meshData.triangles.Length; i += 3)
{
int ind0 = meshData.triangles[i + 0];
int ind1 = meshData.triangles[i + 1];
int ind2 = meshData.triangles[i + 2];
Vector3 pos0 = meshData.vertices [ind0];
Vector3 pos1 = meshData.vertices [ind1];
Vector3 pos2 = meshData.vertices [ind2];
Vector3 to1 = pos1 - pos0;
Vector3 to2 = pos2 - pos0;
Vector3 normal = Vector3.Cross (to1, to2).normalized;
normals [ind0] += normal;
normals [ind1] += normal;
normals [ind2] += normal;
}
for (int i = 0; i < numVertices; i += 1)
{
normals [i].Normalize ();
}
this.meshData.normals = normals;
}