I have problem with normalized values when creating procedural grid in Unity. I have been following great tutorial from catlikecoding and I dumped in to weird behaving when I tried to use normalized values for my vertices. In some cases of xSize and ySize grid combinations all works, but in other combinations mesh get deformed. Let me give you couple of examples
xSize = 35; ySize = 25; // OK
xSize = 350; ySize = 250; // NOT OK
xSize = 150; ySize = 250; // OK
xSize = 350; ySize = 200; // NOT OK
xSize = 1000; ySize = 750; // NOT OK
First 2 cases I illustrated with sphere representing each 10th vertices.
35x25 case
350x250 case
I am using Unity3d 2018.3
private void Generate()
{
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
mesh.name = "Procedural Grid";
vertices = new Vector3[(xSize + 1) * (ySize + 1)];
Vector2[] uv = new Vector2[vertices.Length];
float multX = 1 / (float)xSize;
float multY = 1 / (float)ySize;
for (int i = 0, y = 0; y <= ySize; y++)
{
for (int x = 0; x <= xSize; x++, i++)
{
//vertices[i] = new Vector3(x, y);
var xNormalized = x * multX;
var yNormalized = y * multY;
vertices[i] = new Vector3(xNormalized, yNormalized);
uv[i] = new Vector2(xNormalized, yNormalized);
}
}
mesh.vertices = vertices;
mesh.uv = uv;
var triangles = new int[xSize * ySize * 6];
for (int ti = 0, vi = 0, y = 0; y < ySize; y++, vi++)
{
for (int x = 0; x < xSize; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + xSize + 1;
triangles[ti + 5] = vi + xSize + 2;
}
}
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
I expect the mesh be 1x1 in every case, no matter which xSize or ySize of the grid I use. Anybody can advise how to achieve that?
So my friend explained me, that by default meshes have a 65535 vertices limit in Unity. And I have to nicely ask if I want more.
I had to add
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
after
mesh.name = "Procedural Grid";
here is more..
Suddenly all works as expected. Thank you all for support.
Related
I've been making procedural terrain height maps with the diamond square algorithm and the mesh with the triangulation method below:
public Map GenerateMap()
{
Mesh mapMesh = new();
vertices = new Vector3[(Resolution + 1) * (Resolution + 1)];
Vector2[] uv1 = new Vector2[vertices.Length];
Vector2[] uv2 = new Vector2[vertices.Length];
Vector2[] uv3 = new Vector2[vertices.Length];
DiamondSquare diamondSquare = new(Resolution, Roughness, Seed, HeightLevels);
float[,] heightFloatMap = diamondSquare.DoDiamondSquare();
tex = new Texture2D(Resolution, Resolution);
for (int y = 0, i = 0; y <= Resolution; y++)
{
for (int x = 0; x <= Resolution; x++, i++)
{
//float height = heightMap.GetPixel(x,y).r;
float height = heightFloatMap[x, y];
vertices[i] = new Vector3(x * CellSize.x, height * CellSize.y, y * CellSize.z);
tex.SetPixel(x, y, new Color(height, height, height, 1));
if (height == 0)
uv1[i] = new Vector2(vertices[i].x, vertices[i].z);
else if (height < 0.4)
uv2[i] = new Vector2(vertices[i].x, vertices[i].z);
else if (height < 0.4)
uv3[i] = new Vector2(vertices[i].x, vertices[i].z);
}
}
mapMesh.vertices = vertices;
mapMesh.uv = uv1;
mapMesh.uv2 = uv2;
int[] triangles = new int[Resolution * Resolution * 6];
Cell[,] cellMap = new Cell[Resolution / 4, Resolution / 4];
for (int ti = 0, vi = 0, y = 0; y < Resolution; y++, vi++)
{
for (int x = 0; x < Resolution; x++, ti += 6, vi++)
{
triangles[ti] = vi;
triangles[ti + 3] = triangles[ti + 2] = vi + 1;
triangles[ti + 4] = triangles[ti + 1] = vi + Resolution + 1;
triangles[ti + 5] = vi + Resolution + 2;
Vector3[] cellVerts = new Vector3[]
{
vertices[vi], vertices[vi + 1], vertices[vi + Resolution + 1], vertices[vi + Resolution + 2]
};
Cell cell = new(new Vector2Int(x, y), cellVerts, CalculateCellGeometry(cellVerts));
cellMap[x / 4, y / 4] = cell;
}
}
mapMesh.triangles = triangles;
mapMesh.RecalculateNormals();
mapMesh.RecalculateTangents();
mapMesh.RecalculateBounds();
Map map = new(mapMesh, cellMap, heightFloatMap, vertices);
return map;
}
}
This works fine with grid sizes 16x16, 32x32... 256x256 but breaks when I try it on 512x512 or above
256x256
Mesh is perfect
512x512
It successfully triangulates up until the rows starting y=128
On the underside of the terrain there are these bars
I've mapped out the vertices generated from 512x512 and above resolutions and they are all good so I'm 99% sure its down to the triangulation.
I'm new to procedural meshes and am stumped by this issue, any help would be greatly appreciated.
Turns out it wasn't triangulation, the vertex limit was being reached as my mesh was set to use a 16-bit index buffer.
I added this line
mapMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
and the issue is fixed. An annoying oversight on my part but that's part of the learning process!
I've made a little mesh generator in Unity by following brackeys tutorial series, but I think one was deleted or taken down because there are pieces missing. I'm trying to learn how to use octaves with perlin noise to create more interesting terrain and I cant figure it out. Here's my code if you can help me:
[RequireComponent(typeof(MeshFilter))]
public class MeshGenerator : MonoBehaviour
{
Mesh mesh;
Vector3[] vertices;
int[] triangles;
Color[] colors;
public Gradient gradient;
float minTerrainHeight;
float maxTerrainHeight;
public int xSize = 20;
public int zSize = 20;
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
colors = new Color[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float y = Mathf.PerlinNoise(x * 0.3f, z * 0.3f) * 3f;
if (y > maxTerrainHeight)
{
maxTerrainHeight = y;
}
if (y < minTerrainHeight)
{
minTerrainHeight = y;
}
float height = Mathf.InverseLerp(minTerrainHeight, maxTerrainHeight, vertices[i].y);
colors[i] = gradient.Evaluate(height);
vertices[i] = new Vector3(x, y, z);
i++;
}
}
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
}
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.colors = colors;
mesh.RecalculateNormals();
}
}
So I am trying to create my own version of Sebastian Lauge's marching cubes coding adventures (https://www.youtube.com/watch?v=M3iI2l0ltbE) and I have a script that generates perlin noise into terrain on the Y-axis ant Im trying to make it so it creates perlin noise for the x, y, and z axis and compile vertices from that perlin noise into a mesh. Rightnow I'm in the process of adding the code to decides which points should be put into the mesh The problem is that I have to change the variable y in lines 49 - 56 to the variable ypn (declared on line 45) and leave the y variables out of that area alone the problem is that if i change one of those y variables (don't know which one) it will make the mesh flat and that's not what I want then it gets worse. If you try to revert the variable you changed back to y it will not change a thing. It will always make the mesh flat unless you make a new script. My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGen : MonoBehaviour
{
Mesh mesh;
Material material;
Vector3[] vertices;
int[] triangles;
Vector2[] uvs;
Vector3[] interest;
public int xSize = 20;
public int zSize = 20;
public int ySize = 20;
[Range(-1, 1)]
public float Surface = 0;
public float Weight = 1;
float minHeight = 0;
float maxHeight = 0;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
Texture2D texture = new Texture2D(xSize, zSize, TextureFormat.ARGB32, false);
for (int y = 0; y <= ySize; y++)
{
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float ypn = (Mathf.PerlinNoise(x * .3f, z * .3f) * 2f) * Weight;
vertices[i] = new Vector3(x, ypn, z);
if (y <= minHeight)
minHeight = y;
else
{
if (y >= maxHeight)
maxHeight = y;
}
var result = Mathf.Lerp(minHeight, maxHeight, Mathf.InverseLerp(0, 1, y));
texture.SetPixel(x, z, new Color(y, x, 1));
i++;
}
}
}
texture.Apply();
GetComponent<Renderer>().material.mainTexture = texture;
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
uvs = new Vector2[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
uvs[i] = new Vector2((float)x / xSize, (float)z / zSize);
i++;
}
}
}
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
MeshCollider meshc = gameObject.AddComponent(typeof(MeshCollider)) as MeshCollider;
meshc.sharedMesh = mesh;
}
}
Note: using unity 2019.2.15
Script is attached to empty Game object with a mesh renderer and a mesh filter on default unity values
In Unity C#, I'm using a procedular mesh extrusion, based on a flat polygon's 2D vector points. This works great using the code below, except for one detail: seemingly every second triangle which connects the front from the backside of the extruded mesh is flipped, as the image shows (using a double-sided shader, I can see all the triangles do exist fine, though). How would I fix the flip of these bridging normals? Thanks!
// Extrusion functionality via
// https://forum.unity.com/threads/trying-extrude-a-2d-polygon-to-create-a-mesh.102629/
// with Triangulator based on
// http://wiki.unity3d.com/index.php?title=Triangulator
public static Mesh GetExtrudedMeshFromPoints(Vector2[] points, float depth)
{
const float frontVertex = 0f;
Triangulator triangulator = new Triangulator(points);
int[] tris = triangulator.Triangulate();
Mesh m = new Mesh();
Vector3[] vertices = new Vector3[points.Length*2];
for (int i = 0; i < points.Length; i++)
{
vertices[i].x = points[i].x;
vertices[i].y = points[i].y;
vertices[i].z = frontVertex;
vertices[i+points.Length].x = points[i].x;
vertices[i+points.Length].y = points[i].y;
vertices[i+points.Length].z = depth;
}
int[] triangles = new int[tris.Length*2+points.Length*6];
int count_tris = 0;
// Front vertices
for (int i = 0; i < tris.Length; i += 3)
{
triangles[i] = tris[i];
triangles[i+1] = tris[i+1];
triangles[i+2] = tris[i+2];
}
count_tris += tris.Length;
// Back vertices
for (int i = 0; i < tris.Length; i += 3)
{
triangles[count_tris+i] = tris[i+2] + points.Length;
triangles[count_tris+i+1] = tris[i+1] + points.Length;
triangles[count_tris+i+2] = tris[i] + points.Length;
}
count_tris += tris.Length;
// Triangles around the perimeter of the object
for (int i = 0; i < points.Length; i++)
{
int n = (i+1) % points.Length;
triangles[count_tris] = i;
triangles[count_tris+1] = i + points.Length;
triangles[count_tris+2] = n;
triangles[count_tris+3] = n;
triangles[count_tris+4] = n + points.Length;
triangles[count_tris+5] = i + points.Length;
count_tris += 6;
}
m.vertices = vertices;
m.triangles = triangles;
m.RecalculateNormals();
m.RecalculateBounds();
m.Optimize();
return m;
}
If a face isn't shown it is as you already noticed flipped, this is due to the winding order which in unity is clockwise. The link of your triangluator already states under Troubleshooting:
"If you can't see a polygon created with this utility, remember to check if the polygon is facing the opposite direction. If it is, you can change that by constructing your mesh with the vertex indices in reverse order."
EDIT:
for further clarification: in your code this would mean you have to switch
// Triangles around the perimeter of the object
for (int i = 0; i < points.Length; i++)
{
int n = (i + 1) % points.Length;
triangles[count_tris] = i;
triangles[count_tris + 1] = i + points.Length;
triangles[count_tris + 2] = n;
triangles[count_tris + 3] = n;
triangles[count_tris + 4] = n + points.Length;
triangles[count_tris + 5] = i + points.Length;
count_tris += 6;
}
to
// Triangles around the perimeter of the object
for (int i = 0; i < points.Length; i++)
{
int n = (i + 1) % points.Length;
triangles[count_tris] = n;
triangles[count_tris + 1] = i + points.Length;
triangles[count_tris + 2] = i;
triangles[count_tris + 3] = n;
triangles[count_tris + 4] = n + points.Length;
triangles[count_tris + 5] = i + points.Length;
count_tris += 6;
}
But be careful because the order depends on your depth (if it is higher or lower than you frontVertex)
2nd EDIT:
the normal of a triangle depends on the winding order, this means that there is a difference in the ordering.
An example:
1: Vector2(1f, 1f);
2: Vector2(1f, 0f);
3: Vector2(0f, 0f);
the triangle of
Triangle 1,2,3
and
Triangle 1,3,2
have different normal.
You have to make sure that the winding order is consistent for every triangle you draw.
Alternativly you could tell your shader to disable culling (with Cull Off),as you already said above. But this comes with a cost of computation time. Which in most cases shouldn't matter that much, but it always depends on your purpose. Besides most of the time you dont want the side effects of disabling culling.
I have arrays of coordinates x and y
x = new int[18];
y = new int[15];
x[0] = -404;
y[0] = -226;
for (int i = 1; i < 18; i++)
x[i] = x[i - 1] + 30;
for (int i = 1; i < 15; i++)
y[i] = y[i - 1] + 30;
I setup random coordinates from arrays. But they're incorrect when I start the program. Mostly numbers are out of arrays. Can't understand why. May be I setup position incorrect?
int xCor = x[(int)Random.Range(0, x.Length - 1)];
int yCor = y[(int)Random.Range(0, y.Length - 1)];
transform.position = new Vector2(xCor, yCor);
I need to setup new coordinates. E.g. x = 24, y = 50.
Apple is out of the green area:
Use RectTransform.anchoredPosition property instead of transform.position, like this
RectTransform rectTransform = GetComponent<RectTransform>();
rectTransform.anchoredPosition = new Vector2(xCor, yCor);
You are using a canvas GameObject which doesn't have a normal transform component.