- Home /

# How to get a smoothed Icosphere procedurally

Hello,

I'm making a planet generator and to build the sphere procedurally I decided to use an Icosphere because the triangles that compose it all have the same size which is I think the best option for this kind of project to after implement a level of detail system ( please tell me if I'm wrong and if there is a best solution )

So I followed this tutorial here to build the sphere, it works very well but I don't get a smooth sphere even with a lot of triangles.

For exemple here is the Icosphere with 5120 triangles which is a lot I think compare to unity sphere which is smooth for 760 triangles. I guess when I increase the number of triangles I get a better result but I don't think it's a good solution to have more than 100k triangles for a sphere which is not even smooth.

So is there a solution to smooth triangles or to simply have a smooth sphere like the unity sphere with an Icosphere ?

Thanks !

** edit** Copied from Answer:

I'm sorry I thought my code will be very too long to be posted. So here it is, I understand what you said and normally I normalize the points in the *GetVertexToUnitSphere* function in the *Utility* class which is called for each triangle instance, but I'm still learning the mesh system so I guess a part of my code could be wrong

```
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class CelestialShapeSettings
{
[Range(0, 10)]
public int minDepth = 6;
public float radius = 10;
public NoiseLayer[] noiseLayers;
}
public class CelestialShape
{
CelestialShapeSettings settings;
Mesh mesh;
Vector3[] vertices;
List<Triangle> triangles;
NoiseFilter[] noiseFilters;
public CelestialShape(Mesh mesh, CelestialShapeSettings settings)
{
this.mesh = mesh;
this.settings = settings;
noiseFilters = new NoiseFilter[settings.noiseLayers.Length];
for (int i = 0; i < noiseFilters.Length; i++)
{
noiseFilters[i] = new NoiseFilter(settings.noiseLayers[i].settings);
}
SetupVertices();
}
private void SetupVertices()
{
if (vertices == null)
{
// goldenRatio
float t = (1.0f + (float)Math.Sqrt(5.0)) / 2.0f;
vertices = new Vector3[] {
// Trace the four vertices of a Golden rectangle [R1]
Utility.GetVertexToUnitSphere(new Vector3(-1, t, 0)),
Utility.GetVertexToUnitSphere(new Vector3(1, t, 0)),
Utility.GetVertexToUnitSphere(new Vector3(-1, -t, 0)),
Utility.GetVertexToUnitSphere(new Vector3(1, -t, 0)),
// Trace the four verices of a Golden rectangle orthagonal to the last [R2]
Utility.GetVertexToUnitSphere(new Vector3(0, -1, t)),
Utility.GetVertexToUnitSphere(new Vector3(0, 1, t)),
Utility.GetVertexToUnitSphere(new Vector3(0, -1, -t)),
Utility.GetVertexToUnitSphere(new Vector3(0, 1, -t)),
// Trace the four verices of a Golden rectangle orthagonal to the last two [R3]
Utility.GetVertexToUnitSphere(new Vector3(t, 0, -1)),
Utility.GetVertexToUnitSphere(new Vector3(t, 0, 1)),
Utility.GetVertexToUnitSphere(new Vector3(-t, 0, -1)),
Utility.GetVertexToUnitSphere(new Vector3(-t, 0, 1))
};
}
}
private void SetupTriangles()
{
triangles = new List<Triangle> {
// 5 faces around point 0
new Triangle(vertices[0], vertices[11], vertices[5]),
new Triangle(vertices[0], vertices[5], vertices[1]),
new Triangle(vertices[0], vertices[1], vertices[7]),
new Triangle(vertices[0], vertices[7], vertices[10]),
new Triangle(vertices[0], vertices[10], vertices[11]),
// 5 adjacent faces
new Triangle(vertices[1], vertices[5], vertices[9]),
new Triangle(vertices[5], vertices[11], vertices[4]),
new Triangle(vertices[11], vertices[10], vertices[2]),
new Triangle(vertices[10], vertices[7], vertices[6]),
new Triangle(vertices[7], vertices[1], vertices[8]),
// 5 faces around point 3
new Triangle(vertices[3], vertices[9], vertices[4]),
new Triangle(vertices[3], vertices[4], vertices[2]),
new Triangle(vertices[3], vertices[2], vertices[6]),
new Triangle(vertices[3], vertices[6], vertices[8]),
new Triangle(vertices[3], vertices[8], vertices[9]),
// 5 adjacent faces
new Triangle(vertices[4], vertices[9], vertices[5]),
new Triangle(vertices[2], vertices[4], vertices[11]),
new Triangle(vertices[6], vertices[2], vertices[10]),
new Triangle(vertices[8], vertices[6], vertices[7]),
new Triangle(vertices[9], vertices[8], vertices[1])
};
}
public void GenerateMesh()
{
SetupTriangles();
RecurseUniformally(settings.minDepth);
BuildMesh();
}
private void BuildMesh()
{
int trianglesLength = triangles.Count;
int arraysLength = trianglesLength * 3;
Vector3[] meshVertices = new Vector3[arraysLength];
int[] meshTriangles = new int[arraysLength];
for (int i = 0; i < trianglesLength; i++)
{
int aBaseIndex = i * 3;
Triangle t = triangles[i];
meshVertices[aBaseIndex] = PointOnSphere(t.v1);
meshVertices[aBaseIndex + 1] = PointOnSphere(t.v2);
meshVertices[aBaseIndex + 2] = PointOnSphere(t.v3);
meshTriangles[aBaseIndex] = aBaseIndex;
meshTriangles[aBaseIndex + 1] = aBaseIndex + 1;
meshTriangles[aBaseIndex + 2] = aBaseIndex + 2;
}
mesh.Clear();
mesh.vertices = meshVertices;
mesh.triangles = meshTriangles;
mesh.RecalculateNormals();
}
private Vector3 PointOnSphere(Vector3 p)
{
return p * settings.radius;
}
private void RecurseUniformally(int depth)
{
for (int i = 0; i < depth; i++)
{
List<Triangle> toRemove = new List<Triangle>();
int tLength = triangles.Count;
for (int ti = 0; ti < tLength; ti++)
{
var t = triangles[ti];
if (t.isVisible)
RecurseTriangle(t);
toRemove.Add(t);
}
foreach (var t in toRemove)
{
triangles.Remove(t);
}
}
}
private void RecurseTriangle(Triangle triangle)
{
triangles.AddRange(triangle.Recurse());
}
private class Triangle
{
/*
* v2
* *
* * *
* * *
* adj1 * d2 * adj2
* a ----- b
* * | d4 | *
* * | | *
* * d1 | | d3 *
* v1 * * * * c * * * * v3
*
* adj3
*
*/
public Vector3 v1;
public Vector3 v2;
public Vector3 v3;
// Adjacents triangles
public Triangle adj1;
public Triangle adj2;
public Triangle adj3;
// Descendants triangles, or null
Triangle desc1;
Triangle desc2;
Triangle desc3;
Triangle desc4;
// Parent triangle, or null
Triangle parent;
public bool isVisible = true;
// current recursive depth
int depth;
public Triangle(Vector3 v1, Vector3 v2, Vector3 v3)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
}
public Triangle(Vector3 v1, Vector3 v2, Vector3 v3, int depth, Triangle parent)
{
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
this.depth = depth;
this.parent = parent;
}
public Triangle[] Recurse()
{
int newDepth = depth + 1;
Vector3 a = Utility.GetVertexToUnitSphere(Utility.GetMidpoint(v1, v2));
Vector3 b = Utility.GetVertexToUnitSphere(Utility.GetMidpoint(v2, v3));
Vector3 c = Utility.GetVertexToUnitSphere(Utility.GetMidpoint(v1, v3));
desc1 = new Triangle(v1, a, c, newDepth, this);
desc2 = new Triangle(a, v2, b, newDepth, this);
desc3 = new Triangle(c, b, v3, newDepth, this);
desc4 = new Triangle(b, c, a, newDepth, this);
// Adjacencies
isVisible = false;
return new Triangle[] { desc1, desc2, desc3, desc4 };
}
}
private static class Utility
{
// Get a vector to the unit sphere.
public static Vector3 GetVertexToUnitSphere(Vector3 v)
{
return v.normalized;
}
// Find the midpoint between two 3D points.
public static Vector3 GetMidpoint(Vector3 p1, Vector3 p2)
{
return Vector3.Lerp(p1, p2, .5f);
}
}
}
```

**Answer** by Bunny83
·
Jan 17 at 04:34 PM

Well, you haven't shared any of your code so we have no idea how you generate your vertex normals because those are the important things to make it "look" smooth. Though for a sphere around the local center at (0,0,0) the normal is simply the vertext position normalized. Since we see the edges in your image, that's most likely not what you're doing. We also don't know if and how much of the vertices you have actually shared between the triangles. Keep in mind if you're planning of texturing the sphere you will need a UV seam somewhere, otherwise you would have one triangle strip having its texture coordinates flipped. So at least at the seam you would need duplicated vertices.

*edit*

Ok since you have posted your code we can see the problem. You create several completely unconnected triangles and you don't specify any normals yourself. While it is possible to have all triangles disconnected (so no shared vertices at all), however you can not use RecalculateNormals in that case. Each triangle is on its own. So when the normals for the vertices of that triangle are calculated, they would be perpendicular to that triangle face and you get that flat-shading you're seeing at the moment. If the vertices would be shared, RecalculateNormals would work "better". Though at UV seams the automatic calculation would still fail. You need to provide the normals yourself. As I already said the normal for each vertex position is equal to the position, just normalized. Since your positions are already normalized, they are literally the same. That means you don't even need a seperate "normals" array or List as usual but you can simply use the same array for the normals ^^. So instead of doing this:

```
mesh.Clear();
mesh.vertices = meshVertices;
mesh.triangles = meshTriangles;
mesh.RecalculateNormals();
```

you just do this:

```
mesh.Clear();
mesh.vertices = meshVertices;
mesh.normals = meshVertices;
mesh.triangles = meshTriangles;
```

Make sure you remove the "RecalculateNormals" call.

Of course, if you may desire to scale the sphere in the mesh / vertices itself, you should use a seperate array for the normals (which should be normalized) and the positions (which do not need to be normalized).

Note when you have a mesh that has shared vertices, you can use my subdivision routine I wrote over here. I actually made a more advanced version on the unity wiki, which unfortunately is down (though still in the archive). This subdivision routine will try to keep as shared edges shared after the subdivision. Though currently it does only subdivide, it does not renormalize after each subdivision step which is required for an icosphere. UVs are also subdivided linearly. Though currently your "sphere" does not have any texture coordinates, so that might be another task on your list anyways ^^.

@Bunny83 Thank you very much as you said it works very well when I remove the "RecalculateNormals" call and set the normals to my vertices. I will rebuild my system to have shared vertices ( I will base myself on your subdivision routine which is very interested ! ), I think it would be better.

### Your answer

### Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.