Unity generates mesh from grayscale height image and calculates UVs
![Unity generates mesh from grayscale height image and calculates UVs Unity generates mesh from grayscale height image and calculates UVs](http://up-free-imgs.azimiao.com/wp-content/uploads/2020/09/dixingshengcheng1.jpg)
design sketch
Basic knowledge
1. Point, line, surface and mesh
2. Unity foundation
3. Gray scale height map
Calculate vertices, triangles and normals
1. Vertex
2. Triangular surface
-
Triangle 1: (A, C, D) -
Triangle 2: (A, D, B)
-
B:(o + 1) -
C:(o + n) -
D:(o + n + 1)
3. Normal
Calculate UVs
Code
using JetBrains.Annotations; using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapGenerator : MonoBehaviour { ///Blog( https://www.azimiao.com ) ///Refer to Zhihu article for code structure: https://zhuanlan.zhihu.com/p/53355843 /// <summary> ///The number of sampling times is actually the number of cells divided horizontally and vertically. The more points, the more samples, but not more than the width of the image, and limited by the maximum number of vertices in a single mesh /// </summary> [Header ("Sampling Times")] public int gridW = 250; public int gridH = 250; /// <summary> ///Overall size /// </summary> [Header ("Size")] public int width = 1080; /// <summary> ///We have scaled the length and width, but not the height. This value is used to scale the height /// </summary> [Header ("Y Scale")] public float yScale = 120; public Texture2D heightMapTex; public Texture2D textureMap; public Gradient renderColor; private int hVertCount = 0, wVertCount = 0; private Vector3[] _vertices; private Vector2[] _uvs; private int[] _triangles; private Mesh _mesh; private Color[] _color; public void GeneratorTerrain() { float wStep = (heightMapTex.width - 1) / (float)gridW; float hStep = (heightMapTex.height - 1) / (float)gridH; wVertCount = gridW + 1; hVertCount = gridH + 1; _vertices = new Vector3[wVertCount * hVertCount]; _color = new Color[_vertices.Length]; for (int x = 0; x < wVertCount; x++) { for (int y = 0; y < hVertCount; y++) { //Set Each Vertex int nowIndex = x + y * wVertCount; //Set vertex position _vertices[nowIndex].x = x * width / gridW; _vertices[nowIndex].z = y * width / gridH; //Set vertex position Y Color cc = heightMapTex.GetPixel(Mathf. FloorToInt(x * wStep), Mathf.FloorToInt(y * hStep)); float height = cc.grayscale * yScale; Color t = renderColor.Evaluate(cc.grayscale); _color[nowIndex] = t; _vertices[nowIndex].y = height; } } this.SetTrianglesData(); this.SetUVData(); this.DrawMesh(); this.DrawTexture(); } /// <summary> ///Set triangle data by each small square /// </summary> public void SetTrianglesData() { int triangleNum = gridH * gridW; int triangleVertNum = triangleNum * 6; _triangles = new int[triangleVertNum]; int index = 0; for (int x = 0; x < gridW; x++) { for (int y = 0; y < gridH; y++) { int nowIndex = x + y * wVertCount; //Debug. Log("x:" + x + "|y:" + y + "|index:" + nowIndex); //Triangle 1 _triangles[index] = nowIndex; _triangles[index + 1] = nowIndex + wVertCount; _triangles[index + 2] = _triangles[index + 1] + 1; //Triangle 2 _triangles[index + 3] = nowIndex; _triangles[index + 4] = _triangles[index + 2]; _triangles[index + 5] = nowIndex + 1; //Six vertices per grid index += 6; } } } /// <summary> ///Set vertex UV data /// </summary> public void SetUVData() { _uvs = new Vector2[hVertCount * wVertCount]; float w = 1.0f / gridW; float h = 1.0f / gridH; for (int x = 0; x < gridW; x++) { for (int y = 0; y < gridH; y++) { int nowIndex = x + y * wVertCount; _uvs[nowIndex] = new Vector2(x * w, y * h); } } } /// <summary> ///Set Mesh /// </summary> public void DrawMesh() { if (gameObject. GetComponent<MeshFilter>() == null) { _mesh = new Mesh(); _mesh.name = "ssss"; gameObject.AddComponent<MeshFilter>().sharedMesh = _mesh; } else { _mesh = gameObject. GetComponent<MeshFilter>().sharedMesh; } _mesh. Clear(); _mesh.vertices = _vertices; _mesh.uv = _uvs; _mesh.colors = _color; _mesh.triangles = _triangles; _mesh. RecalculateNormals(); _mesh. RecalculateBounds(); _mesh. RecalculateTangents(); } /// <summary> ///Set Map /// </summary> public void DrawTexture() { if (gameObject. GetComponent<MeshRenderer>() == null) { gameObject.AddComponent<MeshRenderer>(); } //Material diffuseMap = new Material(Shader.Find("Particles/Standard Unlit")); Material diffuseMap = new Material(Shader. Find("Particles/Standard Surface")); diffuseMap.SetTexture("_MainTex", this.textureMap); gameObject.GetComponent<Renderer>().material = diffuseMap; } #if UNITY_EDITOR [UnityEditor.CustomEditor(typeof(MapGenerator))] public class ViveInputAdapterManagerEditor : UnityEditor.Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); if (! Application.isPlaying) { var targetNode = target as MapGenerator; if (targetNode == null) { return; } GUILayout.BeginHorizontal(); if (GUILayout. Button("Generator_Mesh")) { targetNode.GeneratorTerrain(); } GUILayout.EndHorizontal(); } } } #endif }
other
-
Note that a single mesh has a limit of less than 65000 vertices. -
Because there is no corresponding terrain map, the particle shader supporting mesh. color is used.