Unity 3D 中的程序地形编程

Procedural terrain programming in Unity 3D

我正在尝试与程序生成相处,我绝对不是统一专家,但是我遵循了这个指南:click 并得到了一些结果。目前我有两个问题,我对如何解决它们有一些基本的想法,但是我想听听其他的意见。

(这个问题我已经在Unity answers上发过了,但是一直没有回复如何找到这些信息)

主要问题是:块之间的间隙:

如果我缩放,它们会消失,但纹理仍然不合适,这很了不起。

第二个,如您所见,我对地面上的红色(我真的不知道怎么称呼)标记有疑问。我尝试使用指南中的 material,但效果相同。

此外,Perlin Noise(我知道我可以使用 Diamond square 或 simplex)是伪随机算法,所以对于相同的输入参数它会 return 相同的值,所以这是否意味着我的块将永远相同?

我的代码(我正在使用指南中的 LibNoise 库):

    void Awake()
    {
        var settings = new TerrainChunkSettings(129, 129, 100, 40, FlatTexture, SteepTexture, TerrainMaterial);
        var noiseProvider = new NoiseProvider();
        for (var i = 0; i < 4; i++)
            for (var j = 0; j < 4; j++)
                new TerrainChunk(settings, noiseProvider, i, j).CreateTerrain();
    }
public class TerrainChunkSettings
{
    public int HeightmapResolution { get; private set; }

    public int AlphamapResolution { get; private set; }

    public int Length { get; private set; }

    public int Height { get; private set; }

    public Texture2D FlatTexture { get; private set; }

    public Texture2D SteepTexture { get; private set; }

    public Material TerrainMaterial { get; private set; }

    public TerrainChunkSettings(int heightmapResolution, int alphamapResolution, int length, int height, Texture2D flatTexture, Texture2D steepTexture, Material terrainMaterial)
    {
        HeightmapResolution = heightmapResolution;
        AlphamapResolution = alphamapResolution;
        Length = length;
        Height = height;
        FlatTexture = flatTexture;
        SteepTexture = steepTexture;
        TerrainMaterial = terrainMaterial;
    }
}

public class TerrainChunk
{
    private Terrain Terrain { get; set; }

    private TerrainChunkSettings Settings { get; set; }

    private NoiseProvider NoiseProvider { get; set; }

    public int X { get; private set; }

    public int Z { get; private set; }

    private TerrainData Data { get; set; }

    private float[,] Heightmap { get; set; }
    public TerrainChunk(TerrainChunkSettings settings, NoiseProvider noiseProvider, int x, int z)
    {
        X = x;
        Z = z;
        Settings = settings;
        NoiseProvider = noiseProvider;
    }

    public void CreateTerrain()
    {
        var terrainData = new TerrainData();
        terrainData.heightmapResolution = Settings.HeightmapResolution;
        terrainData.alphamapResolution = Settings.AlphamapResolution;

        var heightmap = GetHeightmap();
        terrainData.SetHeights(0, 0, heightmap);
        ApplyTextures(terrainData);
        terrainData.size = new Vector3(Settings.Length, Settings.Height, Settings.Length);

        var newTerrainGameObject = Terrain.CreateTerrainGameObject(terrainData);
        newTerrainGameObject.transform.position = new Vector3(X * Settings.Length, 0, Z * Settings.Length);
        Terrain = newTerrainGameObject.GetComponent<Terrain>();
        Terrain.Flush();
    }

    private float[,] GetHeightmap()
    {
        var heightmap = new float[Settings.HeightmapResolution, Settings.HeightmapResolution];

        for (var zRes = 0; zRes < Settings.HeightmapResolution; zRes++)
        {
            for (var xRes = 0; xRes < Settings.HeightmapResolution; xRes++)
            {
                var xCoordinate = X + (float)xRes / (Settings.HeightmapResolution - 1);
                var zCoordinate = Z + (float)zRes / (Settings.HeightmapResolution - 1);

                heightmap[zRes, xRes] = NoiseProvider.GetValue(xCoordinate, zCoordinate);
            }
        }

        return heightmap;
    }

    private void ApplyTextures(TerrainData terrainData)
    {
        var flatSplat = new SplatPrototype();
        var steepSplat = new SplatPrototype();

        flatSplat.texture = Settings.FlatTexture;
        steepSplat.texture = Settings.SteepTexture;

        terrainData.splatPrototypes = new SplatPrototype[]
        {
            flatSplat,
            steepSplat
        };

        terrainData.RefreshPrototypes();

        var splatMap = new float[terrainData.alphamapResolution, terrainData.alphamapResolution, 2];

        for (var zRes = 0; zRes < terrainData.alphamapHeight; zRes++)
        {
            for (var xRes = 0; xRes < terrainData.alphamapWidth; xRes++)
            {
                var normalizedX = (float)xRes / (terrainData.alphamapWidth - 1);
                var normalizedZ = (float)zRes / (terrainData.alphamapHeight - 1);

                var steepness = terrainData.GetSteepness(normalizedX, normalizedZ);
                var steepnessNormalized = Mathf.Clamp(steepness / 1.5f, 0, 1f);

                splatMap[zRes, xRes, 0] = 1f - steepnessNormalized;
                splatMap[zRes, xRes, 1] = steepnessNormalized;
            }
        }

        terrainData.SetAlphamaps(0, 0, splatMap);
    }
}

public interface INoiseProvider
{
    float GetValue(float x, float z);
}
public class NoiseProvider : INoiseProvider
{
    private Perlin PerlinNoiseGenerator;

    public NoiseProvider()
    {
        PerlinNoiseGenerator = new Perlin();
    }

    public float GetValue(float x, float z)
    {
        return (float)(PerlinNoiseGenerator.GetValue(x, 0, z) / 2f) + 0.5f;
    }
}

出现间隙是因为您正在创建不同的地形对象。使用地形的要点是拥有一个可以根据需要优化的程序网格。

引擎通过降低它们的 LOD 来优化地形,你离它们越远。这意味着两个相邻的块可能会得到两个不同的 LOD,这实际上意味着一个具有另一个的一半的顶点分辨率,这会导致间隙。如果您密切注意,您会注意到它们似乎是如何与其他每个顶点一起发生的。它似乎也近距离消失了,因为你近距离看不到任何 LOD。

引擎解决这个问题的方法是将额外的顶点缝合到地形的低分辨率部分。但是,它不能在您的情况下执行此操作,因为存在单独的地形对象并且它们不知道彼此的存在。您可能想要解决该问题的方法是将所有较小的地形对象合并为一个大地形。

1) 主要问题是:块之间的间隙

您可以将地形缝合在一起以确保它们获得与 SetNeighbors(Terrain left, Terrain top, Terrain right, Terrain bottom);

相同的 LOD

来自documentation:

Lets you setup the connection between neighboring Terrains.

This ensures LOD matches up on neighboring terrains. Note that it is not enough to call this function on one Terrain, you need to set the neighbors of each terrain.

2)第二个:地上的红印

这个问题应该来自您在地形对象中使用的纹理。