Hi!
Im following a tutorial about procedural terrain generation using planes. Right now everything is fine, except the seams on Terrain tiles. Hope someone can lead me in the right direction. As far as i understand the code, the edges of the corresponding planes will get the same heights to stitch the planes together. That leads to a really unusable result as you can see in the attached image. I was thininkig about linear interpolating and normalizing should do it better, but no idea how to achieve this. I
m not a programmer, just interseted in learning.
NoiseMapGeneration.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Wave
{
public float seed;
public float frequency;
public float amplitude;
}
public class NoiseMapGeneration : MonoBehaviour
{
public float[,] GeneratNoiseMap(int mapDepth,int mapWidth, float scale, float offsetX, float offsetY, Wave [] waves)
{
float[,] noiseMap = new float[mapDepth, mapWidth];
for (int zIndex =0; zIndex< mapDepth; zIndex++)
{
for (int xIndex =0; xIndex <mapWidth; xIndex++)
{
float sampleX = (xIndex+offsetX) / scale;
float sampleZ = (zIndex + offsetY) / scale;
float noise = 0f;
float normalization = 0f;
foreach (Wave wave in waves) {
noise += wave.amplitude * Mathf.PerlinNoise(sampleX * wave.frequency + wave.seed, sampleZ * wave.frequency + wave.seed);
normalization += wave.amplitude;
}
noise /= normalization;
noiseMap[zIndex, xIndex] = noise;
}
}
return noiseMap;
}
}
TileGeneration.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class TerrainType
{
public string name;
public float height;
public Color color;
}
public class TileGeneration : MonoBehaviour
{
[SerializeField]
TerrainType[] terrainTypes;
[SerializeField]
NoiseMapGeneration noiseMapGeneration;
[SerializeField]
private MeshRenderer tileRenderer;
[SerializeField]
private MeshFilter meshFilter;
[SerializeField]
private MeshCollider meshCollider;
[SerializeField]
private float mapScale;
[SerializeField]
private float heightMultiplier;
[SerializeField]
private AnimationCurve heightCurve;
[SerializeField]
private Wave[] waves;
private void UpdateMeshVertices(float [,] heightMap)
{
int tileDepth = heightMap.GetLength(0);
int tileWidth = heightMap.GetLength(1);
Vector3[] meshVertices = this.meshFilter.mesh.vertices;
int vertexIndex = 0;
for (int zIndex = 0; zIndex < tileDepth; zIndex++)
{
for (int xIndex = 0; xIndex < tileWidth; xIndex++)
{
float height = heightMap[zIndex, xIndex];
Vector3 vertex = meshVertices[vertexIndex]
; meshVertices[vertexIndex] = new Vector3(vertex.x, this.heightCurve.Evaluate(height) * this.heightMultiplier, vertex.z);
vertexIndex++;
}
}
this.meshFilter.mesh.vertices = meshVertices;
this.meshFilter.mesh.RecalculateBounds();
this.meshFilter.mesh.RecalculateNormals();
this.meshCollider.sharedMesh = this.meshFilter.mesh;
}
// Start is called before the first frame update
void Start()
{
GenerateTile();
}
private void GenerateTile()
{
Vector3[] meshVertices = this.meshFilter.mesh.vertices;
int tileDepth = (int)Mathf.Sqrt(meshVertices.Length);
int tileWidth = tileDepth;
float offsetX = -this.gameObject.transform.position.x;
float offsetY = -this.gameObject.transform.position.z;
float[,] heightMap = this.noiseMapGeneration.GeneratNoiseMap(tileDepth, tileWidth, this.mapScale,offsetX,offsetY,waves);
Texture2D tileTexture = BuildTexture(heightMap);
this.tileRenderer.material.mainTexture = tileTexture;
UpdateMeshVertices(heightMap);
}
private Texture2D BuildTexture(float[,] heightMap)
{
int tileDepth = heightMap.GetLength(0);
int tileWidth = heightMap.GetLength(1);
Color[] colorMap = new Color[tileDepth * tileWidth];
for (int zIndex = 0; zIndex < tileDepth; zIndex ++)
{
for (int xIndex =0; xIndex < tileWidth; xIndex++)
{
int colorIndex = zIndex * tileWidth + xIndex;
float height = heightMap[zIndex, xIndex];
TerrainType terrainType = ChooseTerrainType(height);
colorMap[colorIndex] = terrainType.color;
}
}
Texture2D tileTexture = new Texture2D(tileWidth, tileDepth);
tileTexture.wrapMode = TextureWrapMode.Clamp;
tileTexture.SetPixels(colorMap);
tileTexture.Apply();
return tileTexture;
}
TerrainType ChooseTerrainType(float height)
{
foreach (TerrainType terrainType in terrainTypes)
{
if (height < terrainType.height)
{
return terrainType;
}
}
return terrainTypes[terrainTypes.Length - 1];
}
}
LevelGeneration.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelGeneration : MonoBehaviour
{
[SerializeField]
private int mapWidthInTiles, mapDespthInTiles;
[SerializeField]
private GameObject tilePrefab;
// Start is called before the first frame update
void Start()
{
GenerateMap();
}
// Update is called once per frame
void GenerateMap()
{
Vector3 tileSize = tilePrefab.GetComponent<MeshRenderer>().bounds.size;
int tileWidth = (int)tileSize.x;
int tileDepth = (int)tileSize.z;
for (int xTileIndex = 0; xTileIndex < mapWidthInTiles; xTileIndex++)
{
for (int zTileIndex =0; zTileIndex < mapDespthInTiles; zTileIndex++ )
{
Vector3 tilePosition = new Vector3(this.gameObject.transform.position.x + xTileIndex * tileWidth,
this.gameObject.transform.position.y,
this.gameObject.transform.position.z + zTileIndex * tileDepth);
GameObject tile = Instantiate(tilePrefab, tilePosition, Quaternion.identity) as GameObject;
}
}
}
}
Thanks in advance for your help.