I am making procedural terrain and now that i made the mesh for it ( vertices , normals , triangles ) i applied a material to it, for some reason the materials mesh unwraps correctly to the mesh except for 2 black lines on top and bottom of the texture. [SEE PICTURE 1] PICTURE 1 Spoiler Also, the mesh isn't one sided as it was supposed to be, and has the texture and a weird triangle pattern on the back [SEE PICTURE 2] PICTURE 2 Spoiler For generation i use 1 script which uses a custom terrain class with 3 scripts in it. SCRIPT Spoiler Code (csharp): using UnityEngine; using System.Collections; public class CreateTerrain : MonoBehaviour { public Vector3[] TerrainArray; public Vector2[] TerrainUVs; // Use this for initialization void Start () { Mesh mesh = gameObject.AddComponent<MeshFilter>().mesh; gameObject.AddComponent<MeshRenderer>(); TerrainArray = STerrain.CreateField(20,20); TerrainUVs = STerrain.CreateUVs(TerrainArray,20,20); mesh = STerrain.CreateMesh(TerrainArray,TerrainUVs,20,20); mesh.RecalculateBounds(); mesh.RecalculateNormals(); } } CLASS STerrain Spoiler Code (csharp): using UnityEngine; using System.Collections; using System.Collections.Generic; public class STerrain : MonoBehaviour { public static Vector3[] CreateField(int width,int length) { List<Vector3> List3D = new List<Vector3>(); for( int x = 0 ; x < width ; x++ ) { for( int z = 0 ; z < length ; z++ ) { List3D.Add ( new Vector3( x, 0, z ) ); } } return List3D.ToArray(); } public static Vector2[] CreateUVs( Vector3[] field, int width, int length) { Vector2[] uvs = new Vector2[width * length]; for( int x = 0 ; x < width ; x++ ) { for( int z = 0 ; z < length ; z++ ) { uvs[( x * width ) + z].x = field[( x * width ) + z ].x / width; uvs[( x * width ) + z].y = field[( x * width ) + z ].z / length; } } return uvs; } public static Mesh CreateMesh( Vector3[] field, Vector2[] uvs, int width, int length) { Mesh mesh = new Mesh(); int index; Vector3 point1; Vector3 point2; Vector3 point3; Vector3 point4; List<int> tris = new List<int>(); for( int x = 0 ; x < width ; x++ ) { for( int z = 0 ; z < length ; z++ ) { index = ( x * width ) + z; while(true) { point1 = field[index]; try { point2 = field[index + 1]; } catch(System.IndexOutOfRangeException e) { break; } try { point3 = field[index + width]; } catch(System.IndexOutOfRangeException e) { break; } try { point4 = field[index + width + 1]; } catch(System.IndexOutOfRangeException e) { break; } tris.Add(index); tris.Add(index + 1); tris.Add(index + width); tris.Add(index + width); tris.Add(index + width + 1); tris.Add(index); break; } } } mesh.vertices = field; mesh.uv = uvs; mesh.triangles = tris.ToArray(); mesh.RecalculateBounds(); mesh.RecalculateNormals(); return mesh; } } Thank you for even looking at this thread, and even more if you have any suggestions on what could be causing this error ! Dmajster_
The single-sidedness might be a choice of shader. For instance I believe the sprite shader and possibly some other unlit shaders to not respect backface culling. As for the top/bottom bands, look at things like the wrap mode (Clamp vs Repeat), and perhaps toggling mipmaps on/off. Another thing to do is bring the UVs in so they only show the upper left 25% of the texture and see what you see, just to get some clues about what is going on. Also, make a better test texture (search for "uv test texture" on google) so you can see edges more clearly.
Kurt thank you for your fast response! Currently i have no shaders in my project so i would assume that backface culling is already turned on? Also thank you for your suggestion on the UV test texture! as for the settings i will play around with it and report back !
Just create a diffuse material, assign the test texture to that and assign it to the mesh renderer's material property. Diffuse should definitely remove back faces. Also, like I said, try other uv cords, like from 0 to 0.25 or so, see what part of your texture you see. edit:typos
Hello Kurt! I had no luck with the materials, as the black lines persisted even on a diffuse material, but i did manage to solve the problem by rewriting my code to work in 2D vector3 arrays instead of a 1D vector3 array. Thank you for your help non the less! *Edit* For anyone who found himself in a similar situation, here is the code i use now which is simpler and most importantly WORKS! CUSTOM CLASS Spoiler Code (csharp): using UnityEngine; using System.Collections; using System.Collections.Generic; public class STerrain : MonoBehaviour { public static Vector3[,] CreateField(int width,int length) { Vector3[,] field = new Vector3[width +1,length +1]; for( int z = 0 ; z < length +1 ; z++ ) { for( int x = 0 ; x < width +1 ; x++ ) { field[x,z] = new Vector3( x, 0, z ); } } return field; } public static Mesh CreateMesh( Vector3[,] field) { int width = field.GetLength(0); int length = field.GetLength(1); Debug.Log("DEBUG >> Terrain width is: " + System.Convert.ToString( width )); Debug.Log("DEBUG >> Terrain length is: " + System.Convert.ToString( length )); List<Vector3> verts = new List<Vector3>(); List<Vector2> uvs = new List<Vector2>(); //Calculate UVs for( int z = 0 ; z < length ; z++ ) { for( int x = 0 ; x < width ; x++ ) { float uvx = field[x,z].x / (width-1); float uvz = field[x,z].z / (length-1); uvs.Add( new Vector2( uvx, uvz ) ); verts.Add( new Vector3( field[x,z].x, 0, field[x,z].z ) ); } } Vector3 point1; Vector3 point2; Vector3 point3; Vector3 point4; List<int> tris = new List<int>(); for( int z = 0 ; z < length ; z++ ) { for( int x = 0 ; x < width ; x++ ) { while(true) { point1 = field[x,z]; try { point2 = field[x+1,z]; } catch(System.IndexOutOfRangeException e) { break; } try { point3 = field[x+1,z+1]; } catch(System.IndexOutOfRangeException e) { break; } try { point4 = field[x,z+1]; } catch(System.IndexOutOfRangeException e) { break; } tris.Add( (z+1) * width + x ); // 4 tris.Add( z * width + x +1 ); // 2 tris.Add( z * width + x ); // 1 tris.Add( (z+1) * width + x ); // 4 tris.Add( (z+1) * width + x+1 ); // 3 tris.Add( z * width + x +1 ); // 2 break; } } } Mesh mesh = new Mesh(); mesh.vertices = verts.ToArray(); mesh.uv = uvs.ToArray(); mesh.triangles = tris.ToArray(); mesh.RecalculateBounds(); mesh.RecalculateNormals(); return mesh; } } SCRIPT EXAMPLE OF USAGE Spoiler Code (csharp): using UnityEngine; using System.Collections; public class CreateTerrain : MonoBehaviour { public Vector3[,] TerrainArray; public int lotWidth; public int lotLength; public Material lotTexture; void Start () { TerrainArray = STerrain.CreateField( lotWidth, lotLength ); Mesh mesh = STerrain.CreateMesh( TerrainArray ); MeshFilter meshFilter = gameObject.AddComponent<MeshFilter>(); gameObject.AddComponent<MeshRenderer>(); gameObject.GetComponent<MeshRenderer>().material = lotTexture; meshFilter.mesh = mesh; } }
Hey Dmajster_, that's very nice how you broke down the logical creation of the "field" and separated that step from the creation of the mesh to represent it. Gives you good flexibility if something needs to change.
Hey Kurt, this is actually what i was aiming for, as my terrain won't be static, and making the mesh independent of the "field" will be crucial for having a nice and flexible code onwards.