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][1] 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.
[191310-capture-decran-2022-01-17-a-195954.png*_|191310]
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 _= new NoiseFilter(settings.noiseLayers*.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 {
// 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*;*
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 toRemove = new List();
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);
}
}
}
*[1]: https://superhedral.com/2020/05/17/the-icospherical-world-model-problem-specification-and-preliminary-data-structure/*_
*