- Home /
Subdivision and normalization of procedural mesh vertices destroying UV.
I have a class that I have created that generates a cube, subdivides that cube and then normalizes those points to create a cube mapped sphere. Everything works as expected except that the UVs of the mesh are unusable and shaders cannot be applies effectively. I was wondering if there was a simple fix that could be in either the subdivide action script that I did not create, or my sphere generation script.
EDIT: I have uploaded the project here. The scene that would be easiest to see my problem is BetterPlanetwithRadiusDependentNoiseValues and you can apply the test shaders included to see the problem.
Subdivide:
// C#
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MeshHelper
{
static List<Vector3> vertices;
static List<Vector3> normals;
// [... all other vertex data arrays you need]
static List<int> indices;
static Dictionary<uint,int> newVectices;
static int GetNewVertex(int i1, int i2)
{
// We have to test both directions since the edge
// could be reversed in another triangle
uint t1 = ((uint)i1 << 16) | (uint)i2;
uint t2 = ((uint)i2 << 16) | (uint)i1;
if (newVectices.ContainsKey(t2))
return newVectices[t2];
if (newVectices.ContainsKey(t1))
return newVectices[t1];
// generate vertex:
int newIndex = vertices.Count;
newVectices.Add(t1,newIndex);
// calculate new vertex
vertices.Add((vertices[i1] + vertices[i2]) * 0.5f);
normals.Add((normals[i1] + normals[i2]).normalized);
// [... all other vertex data arrays]
return newIndex;
}
public static void Subdivide(Mesh mesh)
{
newVectices = new Dictionary<uint,int>();
vertices = new List<Vector3>(mesh.vertices);
normals = new List<Vector3>(mesh.normals);
// [... all other vertex data arrays]
indices = new List<int>();
int[] triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i += 3)
{
int i1 = triangles[i + 0];
int i2 = triangles[i + 1];
int i3 = triangles[i + 2];
int a = GetNewVertex(i1, i2);
int b = GetNewVertex(i2, i3);
int c = GetNewVertex(i3, i1);
indices.Add(i1); indices.Add(a); indices.Add(c);
indices.Add(i2); indices.Add(b); indices.Add(a);
indices.Add(i3); indices.Add(c); indices.Add(b);
indices.Add(a ); indices.Add(b); indices.Add(c); // center triangle
}
mesh.vertices = vertices.ToArray();
mesh.normals = normals.ToArray();
// [... all other vertex data arrays]
mesh.triangles = indices.ToArray();
// since this is a static function and it uses static variables
// we should erase the arrays to free them:
newVectices = null;
vertices = null;
normals = null;
// [... all other vertex data arrays]
indices = null;
}
}
and here is my script that generates a sphere.
using System.Collections.Generic;
using LibNoise.Unity;
using LibNoise.Unity.Generator;
using LibNoise.Unity.Operator;
[RequireComponent (typeof (MeshFilter))]
[RequireComponent (typeof (MeshRenderer))]
public class RandPlanet : MonoBehaviour {
public int passes = 2;
public float radius = 10f;
private float divizor;
int sec = System.DateTime.Now.Second;
int day = System.DateTime.Now.Day;
int millisec = System.DateTime.Now.Millisecond;
int PsuedoRand;
private double frequency;
public int NoOceanChance = 90;
private int octaves;
private RidgedMultifractal noise;
private int zscalar = 1;
private bool hasOcean;
void Start () {
MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
Mesh mesh = new Mesh ();
meshFilter.mesh = mesh;
int BetterRand = (int)Random.Range (1, 2487);
int OceanRand = (int)Random.Range (0, 100);
divizor = (50/radius);
frequency = (1.25/radius);
octaves = (int)(radius/10);
if (OceanRand > NoOceanChance)
{
hasOcean = true;
}
if (OceanRand < NoOceanChance)
{
hasOcean = false;
}
mesh.vertices = new Vector3[]{
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
};
int faces = 6;
List<int> triangles = new List<int>();
List<Vector2> uvs = new List<Vector2>();
for (int i = 0; i < faces; i++) {
int triangleOffset = i*4;
triangles.Add(0+triangleOffset);
triangles.Add(2+triangleOffset);
triangles.Add(1+triangleOffset);
triangles.Add(0+triangleOffset);
triangles.Add(3+triangleOffset);
triangles.Add(2+triangleOffset);
uvs.Add(new Vector2(0,0));
uvs.Add(new Vector2(1,0));
uvs.Add(new Vector2(1,1));
uvs.Add(new Vector2(0,1));
}
mesh.triangles = triangles.ToArray();
mesh.uv = uvs.ToArray();
mesh.RecalculateNormals();
mesh.RecalculateBounds ();
mesh.Optimize();
noise = new RidgedMultifractal();
mesh = GetComponent<MeshFilter>().mesh;
PsuedoRand = (sec * day * millisec);
noise.Seed = PsuedoRand * BetterRand;
noise.Frequency = frequency;
noise.OctaveCount = octaves;
noise.Quality = QualityMode.High;
Noise2D Noise2DTex = new Noise2D(512, 512, noise);
Texture2D NoiseTex = Noise2DTex.GetTexture(LibNoise.Unity.Gradient.Terrain);
for(int i = 0; i < passes; i++)
{
MeshHelper.Subdivide(mesh);
}
Vector3[] vertices = mesh.vertices;
Vector3[] verticesN = mesh.vertices;
Vector3[] normals = mesh.normals;
for (int i = 0; i < vertices.Length; i++)
{
Vector3 terrmod = vertices[i].normalized * radius;
verticesN[i] = vertices[i].normalized * (radius + ((float)noise.GetValue(terrmod.x, terrmod.y, terrmod.z*zscalar)/divizor));
}
if (hasOcean == true)
{
GameObject oceanObject = new GameObject();
oceanObject.AddComponent("MeshFilter");
oceanObject.AddComponent("MeshRenderer");
MeshFilter oceanFilter = oceanObject.GetComponent<MeshFilter>();
Mesh oceanmesh = new Mesh ();
oceanFilter.mesh = oceanmesh;
oceanmesh.vertices = new Vector3[]{
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(-0.5f,0.5f,-0.5f),
new Vector3(0.5f,0.5f,-0.5f),
new Vector3(0.5f,0.5f,0.5f),
new Vector3(-0.5f,0.5f,0.5f),
new Vector3(-0.5f,-0.5f,-0.5f),
new Vector3(-0.5f,-0.5f,0.5f),
new Vector3(0.5f,-0.5f,0.5f),
new Vector3(0.5f,-0.5f,-0.5f),
};
int Ofaces = 6;
List<int> Otriangles = new List<int>();
List<Vector2> Ouvs = new List<Vector2>();
for (int i = 0; i < Ofaces; i++) {
int OtriangleOffset = i*4;
Otriangles.Add(0+OtriangleOffset);
Otriangles.Add(2+OtriangleOffset);
Otriangles.Add(1+OtriangleOffset);
Otriangles.Add(0+OtriangleOffset);
Otriangles.Add(3+OtriangleOffset);
Otriangles.Add(2+OtriangleOffset);
Ouvs.Add(new Vector2(0,0));
Ouvs.Add(new Vector2(1,0));
Ouvs.Add(new Vector2(1,1));
Ouvs.Add(new Vector2(0,1));
}
oceanmesh.triangles = triangles.ToArray();
oceanmesh.uv = uvs.ToArray();
oceanObject.renderer.material = new Material(Shader.Find("FX/Water (simple)"));
oceanmesh.RecalculateNormals();
oceanmesh.RecalculateBounds ();
oceanmesh.Optimize();
for(int i = 0; i < passes; i++)
{
MeshHelper.Subdivide(oceanmesh);
}
Vector3[] Overtices = oceanmesh.vertices;
Vector3[] OverticesN = oceanmesh.vertices;
Vector3[] Onormals = oceanmesh.normals;
for (int i = 0; i < Overtices.Length; i++)
{
OverticesN[i] = Overtices[i].normalized * (radius);
}
oceanmesh.vertices = OverticesN;
oceanmesh.RecalculateNormals();
oceanmesh.Optimize();
}
renderer.material.mainTexture = NoiseTex;
mesh.vertices = verticesN;
mesh.RecalculateNormals();
mesh.Optimize();
}
}
So the "all the other vertex arrays" stuff, does that include UVs? Because you need to subdivide those too....
I realize that now, but I have no clue how to go about subdividing the UVs. Would the act of normalizing the cube to create a sphere also have an effect on the UVs or would it still preserve the cube map?
I would have thought you'd apply the same process as you do to the vertices
Answer by Aras · May 15, 2013 at 04:54 AM
All the mesh data is "per vertex". If you produce more vertices, then you should also produce more UVs (subdivided in some way, e.g. linearly interpolated between the original ones).
Your answer
![](https://koobas.hobune.stream/wayback/20220613102159im_/https://answers.unity.com/themes/thub/images/avi.jpg)