Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Procedural terrain programming in Unity 3D

I am trying to get along with procedural generation, I definitely not a pro in unity, however I was following this guide: click and got some results. For now I have 2 problems, I have some basic idea how to resolve them, however I would like to hear some other opinions.

(I have already posted this question on Unity answers, however I have got no response at all, if there is some missing information or it is impossible to explain in a few words, please let me know or give some advice how to find this information)

The main issue is : gaps between chunks:

enter image description here

they kinda disappear if I will zoom, however textures remain unfit and it is remarkable.

The second, as you can see I have a problem with red (I don't really know how to call it) marks on the ground. I have tried to use material from guide, but got same effect.

Besides, Perlin Noise ( I know that I can use Diamond square or simplex) is pseudo-random algorithm, so it will return the same values for the same input parameters, so does it mean that my chunks will always be the same?

My code ( I am using LibNoise library from guide):

    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;
    }
}
like image 299
user7803907 Avatar asked Apr 04 '17 09:04

user7803907


1 Answers

1) The main issue is : gaps between chunks

You can stitch terrain together to ensures they get the same LOD with SetNeighbors(Terrain left, Terrain top, Terrain right, Terrain bottom);

From the 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) The second: red marks on the ground

This problem should come from the textures you are using in your terrain object.

like image 57
Ludovic Feltz Avatar answered Sep 28 '22 13:09

Ludovic Feltz