Can someone help with applying a generated texture onto a generated mesh?
Help! I am trying to create a procedurally generated terrain, and a procedurally generated texture and apply the texture to the terrain. I already have the terrain generation down, along with the texture generation. The issue I am having is whenever I apply the texture to the terrain, only the first pixel of the texture is showing up, but it is showing up on the entire terrain. I figured it has something to do with the UV map i've created for the terrain's mesh. I've verified that it is only the first pixel of the texture, by writing the first pixel of the texture to magenta, and the whole terrain turns magenta. So I'm guessing that there is something wrong with the UVs and that the UV map is making a bunch of the same first pixel over and over again. I don't know though.
I didn't include the code dealing with the triangles for the created mesh, because the mesh works just fine. I'm able to have proper collisions and shading with the generated terrain. It just won't have the WHOLE texture applied to it. The terrain image is generated based on the generateHeight() function, and also works just fine. I can view the generated picture in the inspector and it matches exactly what the terrain height looks like.
Any assistance would be greatly appreciated! I've been searching through google for days now. This is the first time I've asked a question here, so my apologies if I haven't included everything you need, please let me know!
My terrain is x units long by z units deep. The texture is x by z pixels, so that one pixel should correspond to each vertex of the terrain. I understand that the UV map should go from 0-1 over the entire terrain, so that would be 0-1 divided by the number of vertices in the x and z, or in other words U/xUnits by V/zUnits. Some snippets of code:
Vector2[] uvsArray; //Array to hold the UV coordinates
Vector3[] verticesArray; //Array to hold the terrain vertices
public int verticesX = 10; //Total width of the terrain
public int verticesZ = 10; //Total depth of the terrain
void CreateUVsArray()
{
verticesArray = new Vector3[(verticesX +1) * (verticesZ+1)];
uvsArray = new Vector2[(verticesX +1) * (verticesZ+1)];
//Start with setting the uvsArray size to the same size as the vertices array for the terrain
for (int i = 0, z = 0; z < verticesZ; z++)
{
for(int x = 0; x < verticesX; x++)
{
// Loop through and generate the verticesArray values, works just fine
verticesArray[i] = new Vector3(x, generateHeight(), z);
// Create a UV Array with the points of the terrain mesh divided by the total vertices for each x and z
uvsArray[i] = new Vector2(x / verticesX, z / verticesZ);
i++;
}
}
}
I update the created mesh in the following
void UpdateMesh()
{
terrainMesh.Clear();
terrainMesh.vertices = verticesArray;
terrainMesh.triangles = trianglesArray;
terrainMesh.uv = uvsArray;
terrainMesh.RecalculateNormals();
terrainTexture.Apply();
terrainTexture.wrapMode = TextureWrapMode.Clamp;
}
Answer by Zoobidoobie · Apr 28, 2020 at 03:22 PM
Solved! Here's the code.
I figured it out, was missing some key components. What it boiled down to was that I forgot to set the meshRenderer materials maintexture. I thought that since I could see it in the inspector, that it was already set. But since this is all being created at runtime, I guess that wasn't the case. I'm honestly still not sure on the technicalities of why I could see it in the inspector if it wasn't set, but hey it works now. Hope it helps for anyone wanting to create a procedural mesh, a procedural texture, and apply the two together.
void Awake()
{
meshRenderer = GetComponent<Renderer>();
terrainMesh = new Mesh();
GetComponent<MeshFilter>().mesh = terrainMesh;
CreateMesh();
UpdateMesh();
MeshCollider meshCollider = gameObject.GetComponent<MeshCollider>();
meshCollider.sharedMesh = terrainMesh;
}
//******************************************************
//Generate The Terrain Mesh
//******************************************************
void CreateMesh()
{
//******************************************************
//Create Vertices Array
//******************************************************
CreateVerticesArray();
//******************************************************
//Create Triangles Array
//******************************************************
CreateTrianglesArray();
//******************************************************
//Create UVs Array
//******************************************************
CreateUVsArray();
//******************************************************
//Create Colors Array
//******************************************************
CreateColorsArray();
}
//******************************************************
//Create UVs Array
//******************************************************
void CreateUVsArray()
{
//UVs count must always be the same amount as the vertices!
uvsArray = new Vector2[(verticesX + 1) * (verticesZ + 1)];
for(int i =0, z = 0; z <= verticesZ; z++)
{
for (int x = 0; x <= verticesX; x++)
{
//Scale the UV positions by the current vertices position / (the total vertices * the map scale)
uvsArray[i] = new Vector2(verticesArray[i].x/ (verticesX * mapScale), verticesArray[i].z / (verticesZ * mapScale));
i++;
}
}
}
//******************************************************
//Create Colors Array
//******************************************************
void CreateColorsArray()
{
//My texture is a one to one for the vertices array, so the color array should be too
colorsArray = new Color[(verticesX + 1) * (verticesZ + 1)];
terrainTexture = new Texture2D((verticesX + 1) , (verticesZ + 1));
for (int i = 0, z = 0; z <= verticesZ; z++)
{
for (int x = 0; x <= verticesX; x++)
{
float heightOfVertex = Mathf.InverseLerp(minimumTerrainHeight, maximumTerrainHeight, verticesArray[i].y);
// Specify gradient here if you want a gradient applied to your terrain
colorsArray[i] = terrainGradient.Evaluate(heightOfVertex);
//Using setPixel for each pixel worked, whereas using setPixels later with the whole array didn't.
terrainTexture.SetPixel(x, z, colorsArray[i]);
i++;
}
}
}
//******************************************************
//Create Vertices Array
//******************************************************
void CreateVerticesArray()
{
verticesArray = new Vector3[(verticesX + 1) * (verticesZ + 1)];
for (int i = 0, z = 0; z <= verticesZ; z++)
{
for (int x = 0; x <= verticesX; x++)
{
float y = GenerateTerrainHeight(x, z);// change this function to be a terrain generation function
verticesArray[i] = new Vector3(x * mapScale, y, z * mapScale); // Scale Goes in here
i++;
}
}
}
//******************************************************
//Create Triangles Array
//******************************************************
void CreateTrianglesArray()
{
trianglesArray = new int[verticesX * verticesZ * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < verticesZ; z++)
{
for (int x = 0; x < verticesX; x++)
{
trianglesArray[tris + 0] = (vert + 0);
trianglesArray[tris + 1] = (vert + verticesX + 1);
trianglesArray[tris + 2] = (vert + 1);
trianglesArray[tris + 3] = (vert + 1);
trianglesArray[tris + 4] = (vert + verticesX + 1);
trianglesArray[tris + 5] = (vert + verticesX + 2);
vert++;
tris += 6;
}
vert++;
}
}
//******************************************************
//Update The Mesh
//******************************************************
void UpdateMesh()
{
//Cleared the mesh to discard any unwanted legacies, not sure if needed though
terrainMesh.Clear();
//Set the mesh vertices
terrainMesh.vertices = verticesArray;
//Set the mesh triangles
terrainMesh.triangles = trianglesArray;
//Set the mesh UVs
terrainMesh.uv = uvsArray;
//Recalculate all the normals for the mesh
terrainMesh.RecalculateNormals();
//This was key, making the meshRenderer material set to the generated texture
this.meshRenderer.material.mainTexture = terrainTexture;
//Don't forget to apply the texture, this is a second step that was key.
terrainTexture.Apply();
//Set your wrap mode to the desired mode. I needed it clamped.
terrainTexture.wrapMode = TextureWrapMode.Clamp;
}
Your answer
![](https://koobas.hobune.stream/wayback/20220612232641im_/https://answers.unity.com/themes/thub/images/avi.jpg)