Unity 3D中的程序性地形编程

时间:2017-04-04 09:12:48

标签: c# unity3d perlin-noise procedural-generation

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

(我已经在Unity答案中发布了这个问题,但是我根本没有回复,如果有一些遗漏信息或者几句话无法解释,请告诉我或者给出一些建议如何找到这个信息)

主要问题是:块之间的差距:

enter image description here

如果我要缩放它们就会消失,但纹理仍然不合适而且非常值得。

第二个,正如你所看到的那样,我遇到了红色问题(我真的不知道如何调用它)。我试图使用指南中的材料,但效果相同。

此外,Perlin Noise(我知道我可以使用Diamond square或simplex)是伪随机算法,所以它会为相同的输入参数返回相同的值,所以它是否意味着我的块总是相同的?

我的代码(我正在使用指南中的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;
    }
}

2 个答案:

答案 0 :(得分:3)

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

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

获得相同的LOD

来自documentation:

  

让您在相邻的Terrains之间建立连接。

     

这可确保LOD与相邻地形相匹配。请注意它是   不足以在一个地形上调用此功能,你需要设置   每个地形的邻居。

2)第二个:地面上的红色标记

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

答案 1 :(得分:2)

由于您正在创建不同的地形对象,因此会出现间隙。使用地形的主要目的是拥有一个可以根据需要进行优化的程序网格。

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

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