Unity - 块之间的无限地形间隙?
Unity - Infinite terrain gaps betwean chunks?
所以我在创造一个无尽的地形。
我可以创建地形,但我的块之间有间隙并且它们没有正确对齐。
我认为问题可能是由我的噪声生成脚本引起的,但我不确定。
这是我的噪声生成脚本
public static class Noise_GENERATOR
{
public static float[,] GenerateNoise(int chunkSize, int octaves, int seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
float[,] noiseMap = new float[chunkSize, chunkSize];
System.Random prng = new System.Random(seed);
Vector2[] octaveOffsets = new Vector2[octaves];
float maxPossibleHeight = 0;
float amplitude = 1;
float frequency = 1;
for (int i = 0; i < octaves; i++)
{
float offsetX = prng.Next(-100000, 100000) + offset.x;
float offsetY = prng.Next(-100000, 100000) + offset.y;
octaveOffsets[i] = new Vector2(offsetX, offsetY);
maxPossibleHeight += amplitude;
amplitude *= persistence;
}
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float maxLocalNoiseHeight = float.MinValue;
float minLocalNoiseHeight = float.MaxValue;
float halfWidth = chunkSize / 2f;
float halfHeight = chunkSize / 2f;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
amplitude = 1;
frequency = 1;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x-halfWidth + octaveOffsets[i].x) / noiseScale * frequency;
float sampleY = (y-halfHeight + octaveOffsets[i].y) / noiseScale * frequency;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
noiseHeight += perlinValue * amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
if (noiseHeight > maxLocalNoiseHeight)
{
maxLocalNoiseHeight = noiseHeight;
}
else if (noiseHeight < minLocalNoiseHeight)
{
minLocalNoiseHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
float normalizedHeight = (noiseMap[x, y] + 1) / (maxPossibleHeight / 0.9f);
noiseMap[x, y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
}
}
return noiseMap;
}
}
为了生成网格的高度,我使用动画曲线并将其乘以 elevationScale 变量。
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
我考虑过访问每个 Terrain 块并获取边缘的高度并将它们匹配在一起,但这看起来真的很奇怪,我不知道如何正确地做到这一点。
编辑:以防万一我的网格生成器脚本以及我如何创建地形块
public static class Mesh_GENERATOR
{
public static MeshData GenerateChunkMesh(int chunkSize,float[,] noiseMapData,float elevationScale,AnimationCurve terrainCurve,int LODlevel )
{
float[,] noiseMap = noiseMapData;
AnimationCurve heightCurve = new AnimationCurve(terrainCurve.keys);
//Setup variables
Vector3[] vertices = new Vector3[chunkSize * chunkSize];
int[] triangles = new int[(chunkSize - 1) * (chunkSize - 1) * 6];
Vector2[] uvs = new Vector2[chunkSize * chunkSize];
int triangle = 0;
int levelOfDetailIncrement = (LODlevel == 0) ? 1 : LODlevel * 2;
int numberOfVerticesPerRow = (chunkSize) / levelOfDetailIncrement + 1;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
int i = y * chunkSize + x;
//Create vertices at position and center mesh
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
Vector2 percentPosition = new Vector2(x / (chunkSize - 1f), y / (chunkSize -1f ));
Vector3 vertPosition = new Vector3(percentPosition.x * 2 - 1, 0, percentPosition.y * 2 - 1) * chunkSize/2;
vertPosition.y = height;
vertices[i] = vertPosition;
uvs[i] = new Vector2((float)x / chunkSize, (float)y / chunkSize);
//Construct triangles
if (x != chunkSize - 1 && y != chunkSize - 1)
{
triangles[triangle + 0] = i + chunkSize;
triangles[triangle + 1] = i + chunkSize + 1;
triangles[triangle + 2] = i;
triangles[triangle + 3] = i + chunkSize + 1;
triangles[triangle + 4] = i + 1;
triangles[triangle + 5] = i;
triangle += 6;
}
}
}
MeshData meshData = new MeshData(chunkSize, vertices, triangles, uvs);
return meshData;
}
}
public class MeshData
{
public int chunkSize;
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
public Mesh mesh;
public MeshData(int chunkSize,Vector3[] vertices,int[] triangles, Vector2[] uvs)
{
this.chunkSize = chunkSize;
this.vertices = vertices;
this.triangles = triangles;
this.uvs = uvs;
}
public Mesh CreateMesh()
{
if(mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); }
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
这是我的 TerrainChunk
public class TerrainChunk
{
GameObject meshObject;
Vector2 position;
Bounds bounds;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
public TerrainChunk(Vector2 coord, int chunkSize, Transform parent,Material terrainMaterial)
{
position = coord * chunkSize;
bounds = new Bounds(position, Vector2.one * chunkSize);
Vector3 positionV3 = new Vector3(position.x , 0, position.y );
Debug.Log("CHUNK: COORD" + coord + "POSITION" + position + "POSITION3V" + positionV3);
meshObject = new GameObject("Terrain Chunk");
meshFilter = meshObject.AddComponent<MeshFilter>();
meshRenderer = meshObject.AddComponent<MeshRenderer>();
meshRenderer.material = terrainMaterial;
meshObject.transform.position = positionV3;
meshObject.transform.parent = parent;
SetVisible(false);
worldGenerator.RequestMapData(position,OnNoiseDataReceived);
}
void OnNoiseDataReceived(MapData mapData)
{
worldGenerator.RequestMeshData(mapData, OnMeshDataReceived);
}
void OnMeshDataReceived(MeshData meshData)
{
meshFilter.mesh = meshData.CreateMesh();
}
public void UpdateTerrainChunk(Vector2 viewerPosition, int maxRenderDistance)
{
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxRenderDistance;
SetVisible(visible);
}
public void SetVisible(bool visible)
{
meshObject.SetActive(visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
}
如果我正确理解你所有的值和变量。
问题可能出在噪音发生器上。
您需要将 chunkSize 创建为大 1,因此如果您传递 250,则需要传递 251,因为噪声生成器中的 for 循环停止在 249 而不是 250。(我可能错了this ), 如果您这样做,网格生成器现在将具有正确的计算值。
所以你的 chunksize 变量应该是这样的
chunkSize = chunkSize + 1;
现在仍然会有更小的缝隙,网格会相互夹穿,所以要解决这个问题,您需要定位块并按此方式进行 ->
(如果您的坐标用作从您的世界生成器对象创建块的方向 -> 例如指向北方的块将具有值 x:0 y:1, 指向西的块将是 x:-1 y:0, NorthWest 块 x:-1 y:-1 等等),你可能需要将 0.5f 更改为你的值,以便块正确对齐。
Vector3 positionV3 = new Vector3(position.x + (coord.x + 0.5f), 0, position.y + (coord.y + 0.5f) );
地形中仍然可能存在一些可见的较小间隙,但这可以通过调整值来解决,或者您可以尝试访问每个块并获取边缘顶点及其高度并将块连接在一起方式。
所以我在创造一个无尽的地形。
我可以创建地形,但我的块之间有间隙并且它们没有正确对齐。
我认为问题可能是由我的噪声生成脚本引起的,但我不确定。
这是我的噪声生成脚本
public static class Noise_GENERATOR
{
public static float[,] GenerateNoise(int chunkSize, int octaves, int seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
float[,] noiseMap = new float[chunkSize, chunkSize];
System.Random prng = new System.Random(seed);
Vector2[] octaveOffsets = new Vector2[octaves];
float maxPossibleHeight = 0;
float amplitude = 1;
float frequency = 1;
for (int i = 0; i < octaves; i++)
{
float offsetX = prng.Next(-100000, 100000) + offset.x;
float offsetY = prng.Next(-100000, 100000) + offset.y;
octaveOffsets[i] = new Vector2(offsetX, offsetY);
maxPossibleHeight += amplitude;
amplitude *= persistence;
}
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float maxLocalNoiseHeight = float.MinValue;
float minLocalNoiseHeight = float.MaxValue;
float halfWidth = chunkSize / 2f;
float halfHeight = chunkSize / 2f;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
amplitude = 1;
frequency = 1;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x-halfWidth + octaveOffsets[i].x) / noiseScale * frequency;
float sampleY = (y-halfHeight + octaveOffsets[i].y) / noiseScale * frequency;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
noiseHeight += perlinValue * amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
if (noiseHeight > maxLocalNoiseHeight)
{
maxLocalNoiseHeight = noiseHeight;
}
else if (noiseHeight < minLocalNoiseHeight)
{
minLocalNoiseHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
float normalizedHeight = (noiseMap[x, y] + 1) / (maxPossibleHeight / 0.9f);
noiseMap[x, y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
}
}
return noiseMap;
}
}
为了生成网格的高度,我使用动画曲线并将其乘以 elevationScale 变量。
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
我考虑过访问每个 Terrain 块并获取边缘的高度并将它们匹配在一起,但这看起来真的很奇怪,我不知道如何正确地做到这一点。
编辑:以防万一我的网格生成器脚本以及我如何创建地形块
public static class Mesh_GENERATOR
{
public static MeshData GenerateChunkMesh(int chunkSize,float[,] noiseMapData,float elevationScale,AnimationCurve terrainCurve,int LODlevel )
{
float[,] noiseMap = noiseMapData;
AnimationCurve heightCurve = new AnimationCurve(terrainCurve.keys);
//Setup variables
Vector3[] vertices = new Vector3[chunkSize * chunkSize];
int[] triangles = new int[(chunkSize - 1) * (chunkSize - 1) * 6];
Vector2[] uvs = new Vector2[chunkSize * chunkSize];
int triangle = 0;
int levelOfDetailIncrement = (LODlevel == 0) ? 1 : LODlevel * 2;
int numberOfVerticesPerRow = (chunkSize) / levelOfDetailIncrement + 1;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
int i = y * chunkSize + x;
//Create vertices at position and center mesh
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
Vector2 percentPosition = new Vector2(x / (chunkSize - 1f), y / (chunkSize -1f ));
Vector3 vertPosition = new Vector3(percentPosition.x * 2 - 1, 0, percentPosition.y * 2 - 1) * chunkSize/2;
vertPosition.y = height;
vertices[i] = vertPosition;
uvs[i] = new Vector2((float)x / chunkSize, (float)y / chunkSize);
//Construct triangles
if (x != chunkSize - 1 && y != chunkSize - 1)
{
triangles[triangle + 0] = i + chunkSize;
triangles[triangle + 1] = i + chunkSize + 1;
triangles[triangle + 2] = i;
triangles[triangle + 3] = i + chunkSize + 1;
triangles[triangle + 4] = i + 1;
triangles[triangle + 5] = i;
triangle += 6;
}
}
}
MeshData meshData = new MeshData(chunkSize, vertices, triangles, uvs);
return meshData;
}
}
public class MeshData
{
public int chunkSize;
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
public Mesh mesh;
public MeshData(int chunkSize,Vector3[] vertices,int[] triangles, Vector2[] uvs)
{
this.chunkSize = chunkSize;
this.vertices = vertices;
this.triangles = triangles;
this.uvs = uvs;
}
public Mesh CreateMesh()
{
if(mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); }
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
这是我的 TerrainChunk
public class TerrainChunk
{
GameObject meshObject;
Vector2 position;
Bounds bounds;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
public TerrainChunk(Vector2 coord, int chunkSize, Transform parent,Material terrainMaterial)
{
position = coord * chunkSize;
bounds = new Bounds(position, Vector2.one * chunkSize);
Vector3 positionV3 = new Vector3(position.x , 0, position.y );
Debug.Log("CHUNK: COORD" + coord + "POSITION" + position + "POSITION3V" + positionV3);
meshObject = new GameObject("Terrain Chunk");
meshFilter = meshObject.AddComponent<MeshFilter>();
meshRenderer = meshObject.AddComponent<MeshRenderer>();
meshRenderer.material = terrainMaterial;
meshObject.transform.position = positionV3;
meshObject.transform.parent = parent;
SetVisible(false);
worldGenerator.RequestMapData(position,OnNoiseDataReceived);
}
void OnNoiseDataReceived(MapData mapData)
{
worldGenerator.RequestMeshData(mapData, OnMeshDataReceived);
}
void OnMeshDataReceived(MeshData meshData)
{
meshFilter.mesh = meshData.CreateMesh();
}
public void UpdateTerrainChunk(Vector2 viewerPosition, int maxRenderDistance)
{
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxRenderDistance;
SetVisible(visible);
}
public void SetVisible(bool visible)
{
meshObject.SetActive(visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
}
如果我正确理解你所有的值和变量。
问题可能出在噪音发生器上。
您需要将 chunkSize 创建为大 1,因此如果您传递 250,则需要传递 251,因为噪声生成器中的 for 循环停止在 249 而不是 250。(我可能错了this ), 如果您这样做,网格生成器现在将具有正确的计算值。
所以你的 chunksize 变量应该是这样的
chunkSize = chunkSize + 1;
现在仍然会有更小的缝隙,网格会相互夹穿,所以要解决这个问题,您需要定位块并按此方式进行 ->
(如果您的坐标用作从您的世界生成器对象创建块的方向 -> 例如指向北方的块将具有值 x:0 y:1, 指向西的块将是 x:-1 y:0, NorthWest 块 x:-1 y:-1 等等),你可能需要将 0.5f 更改为你的值,以便块正确对齐。
Vector3 positionV3 = new Vector3(position.x + (coord.x + 0.5f), 0, position.y + (coord.y + 0.5f) );
地形中仍然可能存在一些可见的较小间隙,但这可以通过调整值来解决,或者您可以尝试访问每个块并获取边缘顶点及其高度并将块连接在一起方式。