我正在开展一个个人项目,以提高自己并学习更高级的东西,但我目前陷入困境,无法找到任何解决方案。基本上,我有两个独立的问题,其中一个问题的解决方案会带来另一个问题,它们是:
“线”的右侧是较高详细的块,左侧是较低详细的块。
这在播放模式中也非常明显,无论较低的详细块有多远(例如网格中块之间的空白空间):
下面的代码是我在每个块生成时添加到它们的脚本,其中包括网格生成和 LOD 系统:
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
public class Chunk : MonoBehaviour
{
private ChunkGenerator chunkGenerator;
private MeshRenderer meshRenderer;
private MeshFilter meshFilter;
private MeshCollider meshCollider;
private bool isActive = false;
private TerrainSettings terrainSettings;
private Mesh chunkMesh;
private bool isMeshGenerated = false;
private Mesh LODMesh;
private bool isLODMeshGenerated = false;
public Vector2Int ChunkCoord { get; private set; }
// adds all the necessary components
public void Initialize(Vector2Int chunkCoord, ChunkGenerator generator)
{
ChunkCoord = chunkCoord;
chunkGenerator = generator;
terrainSettings = generator.terrainSettings;
if (!TryGetComponent<MeshFilter>(out meshFilter))
{
meshFilter = gameObject.AddComponent<MeshFilter>();
}
if (!TryGetComponent<MeshRenderer>(out meshRenderer))
{
meshRenderer = gameObject.AddComponent<MeshRenderer>();
}
if (!TryGetComponent<MeshCollider>(out meshCollider))
{
meshCollider = gameObject.AddComponent<MeshCollider>();
}
isMeshGenerated = false;
}
private void Update()
{
UpdateLODMeshes();
}
public void Activate()
{
if (!isActive)
{
gameObject.SetActive(true);
UpdateLODMeshes();
isActive = true;
}
}
// very simple LOD logic
private void UpdateLODMeshes()
{
Vector2Int playerChunk = chunkGenerator.GetPlayerChunk();
int distanceX = Mathf.Abs(ChunkCoord.x - playerChunk.x);
int distanceZ = Mathf.Abs(ChunkCoord.y - playerChunk.y);
int lodDistance = chunkGenerator.LODDistance;
if (distanceX <= lodDistance && distanceZ <= lodDistance)
{
if (!isMeshGenerated)
{
chunkMesh = GenerateMesh(1);
isMeshGenerated = true;
SetMaterial(terrainSettings.terrainMaterial);
}
SetMesh(chunkMesh);
SetChunkScale(1);
}
else
{
if (!isLODMeshGenerated)
{
LODMesh = GenerateMesh(2);
isLODMeshGenerated = true;
SetMaterial(terrainSettings.terrainMaterial);
}
SetMesh(LODMesh);
SetChunkScale(1.0225f);
}
}
public void Deactivate()
{
if (isActive)
{
gameObject.SetActive(false);
isActive = false;
}
}
// mesh generation
public Mesh GenerateMesh(int resolution)
{
Mesh mesh = new();
int chunkSize = chunkGenerator.chunkSize;
// offset calculation
Vector2Int offset = new(ChunkCoord.x * (chunkSize - resolution), ChunkCoord.y * (chunkSize - resolution));
// noise map generation
float[,] noiseMap = NoiseGenerator.GenerateNoiseMap(
terrainSettings.noiseScale, terrainSettings.frequency, terrainSettings.octaves, terrainSettings.persistence, terrainSettings.lacunarity,
terrainSettings.groundLevel, terrainSettings.groundFlatness, terrainSettings.mountainLevel, terrainSettings.mountainFlatness,
terrainSettings.mountainHeightMultiplier, terrainSettings.generateIslands, terrainSettings.islandFrequency,
terrainSettings.islandRadiusRandomizer, terrainSettings.islandRadius, terrainSettings.islandHeightMultiplier, chunkSize, chunkSize, offset
);
// generating mesh data
GenerateMeshData(chunkSize, resolution, out Vector3[] vertices, out Vector2[] uvs, out int[] triangles, noiseMap);
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
meshCollider.sharedMesh = mesh;
return mesh;
}
// mesh data generation
public void GenerateMeshData(int chunkSize, int resolution, out Vector3[] vertices, out Vector2[] uvs, out int[] triangles, float[,] noiseMap)
{
int reducedSize = chunkSize / resolution;
int numVertices = reducedSize * reducedSize;
int numTriangles = (reducedSize - 1) * (reducedSize - 1) * 6;
vertices = new Vector3[numVertices];
uvs = new Vector2[numVertices];
triangles = new int[numTriangles];
int vertexIndex = 0;
int triangleIndex = 0;
// vertex generation
for (int y = 0; y < reducedSize; y++)
{
for (int x = 0; x < reducedSize; x++)
{
int xPos = x * resolution;
int zPos = y * resolution;
float yPos = noiseMap[xPos, zPos] * terrainSettings.heightScale;
vertices[vertexIndex] = new Vector3(xPos, yPos, zPos);
uvs[vertexIndex] = new Vector2(x / (float)(reducedSize - 1), y / (float)(reducedSize - 1));
// triangle generation
if (x < reducedSize - 1 && y < reducedSize - 1)
{
// Define the indices of the vertices for the triangles
int topLeft = vertexIndex;
int topRight = vertexIndex + 1;
int bottomLeft = vertexIndex + reducedSize;
int bottomRight = vertexIndex + reducedSize + 1;
triangles[triangleIndex] = topLeft;
triangles[triangleIndex + 1] = bottomLeft;
triangles[triangleIndex + 2] = topRight;
triangles[triangleIndex + 3] = topRight;
triangles[triangleIndex + 4] = bottomLeft;
triangles[triangleIndex + 5] = bottomRight;
triangleIndex += 6;
}
vertexIndex++;
}
}
}
private void SetMesh(Mesh mesh)
{
meshFilter.mesh = mesh;
}
private void SetMaterial(Material material)
{
if (meshRenderer != null)
{
meshRenderer.material = material;
}
}
private void SetChunkScale(float scale)
{
transform.localScale = new(scale, 1, scale);
}
}
当我进行偏移计算时,就会出现问题。
Vector2Int offset = new(ChunkCoord.x * (chunkSize - resolution), ChunkCoord.y * (chunkSize - resolution));
Vector2Int offset = new(ChunkCoord.x * (chunkSize - 1), ChunkCoord.y * (chunkSize - 1));
另外,下面的代码是噪声生成脚本:
using UnityEngine;
public static class NoiseGenerator
{
private const float Half = 0.5f;
private const float Zero = 0f;
private const float One = 1f;
public static float[,] GenerateNoiseMap(TerrainSettings settings, int mapWidth, int mapHeight, Vector2 offset)
{
float[,] noiseMap = new float[mapWidth, mapHeight];
float invNoiseScale = One / settings.noiseScale;
float halfWidth = mapWidth * Half;
float halfHeight = mapHeight * Half;
for (int y = 0; y < mapHeight; y++)
{
for (int x = 0; x < mapWidth; x++)
{
float sampleX = (x - halfWidth + offset.x) * invNoiseScale;
float sampleY = (y - halfHeight + offset.y) * invNoiseScale;
float amplitude = One;
float frequency = One;
float noiseHeight = Zero;
for (int i = 0; i < settings.octaves; i++)
{
float perlinValue = Mathf.PerlinNoise(sampleX * frequency, sampleY * frequency);
noiseHeight += perlinValue * amplitude;
amplitude *= settings.persistence;
frequency *= settings.lacunarity;
}
float groundFlatness = Mathf.Clamp01((settings.groundLevel - noiseHeight) * settings.groundFlatness);
float groundSmoothTransition = SmoothStep(Zero, One, groundFlatness);
noiseHeight = Mathf.Lerp(noiseHeight, settings.groundLevel, groundSmoothTransition);
float mountainFlatness = Mathf.Clamp01((noiseHeight - settings.mountainLevel) * settings.mountainFlatness);
float mountainSmoothTransition = SmoothStep(Zero, One, mountainFlatness);
noiseHeight = Mathf.Lerp(noiseHeight, settings.mountainLevel, mountainSmoothTransition);
if (noiseHeight > settings.mountainLevel)
{
float mountainMultiplier = settings.mountainHeightMultiplier * (noiseHeight - settings.mountainLevel);
noiseHeight += mountainMultiplier;
}
if (settings.generateIslands)
{
float islandNoise = Mathf.PerlinNoise(sampleX * settings.islandFrequency, sampleY * settings.islandFrequency);
float islandMultiplier = Mathf.Clamp01(islandNoise - settings.islandRadiusRandomizer) * settings.islandRadius;
noiseHeight += islandMultiplier * settings.islandHeightMultiplier;
}
noiseMap[x, y] = noiseHeight;
}
}
return noiseMap;
}
private static float SmoothStep(float edge0, float edge1, float x)
{
x = Mathf.Clamp01((x - edge0) / (edge1 - edge0));
return x * x * (3 - 2 * x);
}
}
我知道我的代码有点混乱而且不是很优化,但我真的被困在这里,在解决这个问题之前我无法继续处理这个问题。我知道“解决方案”并不是真正的解决方案,因为它们带来了另一个问题。我尝试了偏移计算并对方程式尝试了太多东西,但我知道这不能通过猜测并希望找到正确的方程式来解决。我知道存在一个严重错误,但我找不到它,我需要帮助。预先感谢您的所有帮助。
在我发布问题后不久,我找到了解决方案。我确信问题要么出在偏移计算中,要么出在噪声产生中。值得庆幸的是,在我遇到这是另一篇相关文章之后,我变得足够聪明,可以寻找其他东西。
在我实现 LOD 系统之前,我的块生成器中存在“块之间的空白空间”问题,因此我通过以下块位置计算(通过从块大小中减去 1)过早地解决了它:
float posX = x * (chunkSize - 1);
float posZ = z * (chunkSize - 1);
由于我只处理一级细节,因此以这种方式“解决”它是有意义的。解决方案是从计算中删除“- 1”并替换“Chunk”脚本中的这一行:
...
Mesh mesh = new();
int chunkSize = chunkGenerator.chunkSize;
Vector2Int offset = new(ChunkCoord.x * (chunkSize - 1), ChunkCoord.y * (chunkSize - 1));
...
有了这个:
...
Mesh mesh = new();
int chunkSize = chunkGenerator.chunkSize + resolution;
Vector2Int offset = new(ChunkCoord.x * (chunkSize - resolution), ChunkCoord.y * (chunkSize - resolution));
...
在偏移计算中用“chunkSize”减去“分辨率”是正确的方法,但我们还向 chunkSize 本身添加“分辨率”,以防止问题中列出的所有问题。