Search code examples
c#unity-game-enginetexture2dprocedural

Unity3d C# -> generating a perlin noise texture with layers


I am trying to generate a noise texture with the LibNoise library using perlin and some fractal to create 6 layers that i can add together in one image.

I have problems that i can't seem to fix: (3 problems that show in the debug log.)

1) The image it generates is just white. and with reason, a number of 23170.49 was printed :/

2) All the other prints are NaN, which should not happen... (over 65535 of that in the log)

3) I have an error:

IndexOutOfRangeException: Array index is out of range. HeightController.getTerrainData (Vector2 position) (at Assets/Scripts/TerrainGenerator/HeightController.cs:28) TerrainMesh.Start () (at Assets/Scripts/TerrainGenerator/TerrainMesh.cs:21)

But that error should not even occur, as it is in the bounds, so can't see why it tells me that...

Here are the codes i use,

1) HeightController

using UnityEngine;
using System.Collections;

public class HeightController : MonoBehaviour
{
    public static int layers = 6;
    public static float[] layersOpacity = {0.5f, 0.3f, 0.2f, 0.15f, 0.1f, 0.05f};

    private static int layerSize = 256;
    //private static int terrainSize = 2048;    //build heightmap here?

    private static int amplitude = 128; //divided by 2
    private static int frequency = 4;       //multiplied by 2       = obtaining a smaller height, but faster frequence.

    //when you want to generate a terrain, just get the
    //data array containing the stuffs to build a heightmap out of it.
    //this here does the calculation of layers with opacity etc.
    public static float[,] getTerrainData(Vector2 position) //the vector2 takes the SQUARES offset of terrains, 0,1 | 22,13 (the terraid ID)
    {
        float[,] finalLayersData = new float[layerSize,layerSize];
        for (int i = 0; i < layers; ++i)                //the entire array will be set an opacity then adds to the final.
        {
            float[,] currentLayerData = Calculate(position);    //there should be a less laggy way here?

            //multiplies each data with it's respective opacity.
            for (int a = 0; a < layerSize; ++a)
                for (int b = 0; b < layerSize; ++a)
                    currentLayerData[a,b] *= layersOpacity[i];

            //adds to the final data array
            for (int a = 0; a < layerSize; ++a)
                for (int b = 0; b < layerSize; ++a)
                    finalLayersData[a,b] += currentLayerData[a,b];
        }
        return finalLayersData;
    }

    private static float[,] Calculate(Vector2 position)
    {
        float[,] data = new float[layerSize,layerSize];
        int offsetX = (int)position.x*layerSize;
        int offsetY = (int)position.y*layerSize;

        Perlin perlin = new Perlin();

        //the 1/4 then 1/16 then 1/32 etc.
        FractalNoise fractal = new FractalNoise((1f/4f), frequency, amplitude, perlin);
        for (var y = 0; y < layerSize; y++)
        {
            for (var x = 0; x < layerSize; x++)
            {
                float result = fractal.HybridMultifractal(x + offsetX, y + offsetY, layerSize);
                data[x, y] = result;
                print(result);
            }
        }
        return data;
    }
}

2) TerrainMesh

using UnityEngine;
using System.Collections;

public class TerrainMesh : MonoBehaviour
{
    public static Transform[] TextureList = new Transform[64];
    //public static long seed = 34;
    public int Size = 256;

    public Mesh mesh;
    public MeshFilter meshFilter;
    public MeshCollider meshCollider;
    public Material material;
    public float[,] data;

    public Texture2D fun;

    public void Start()
    {
        fun = new Texture2D(256, 256);
        data = HeightController.getTerrainData(new Vector2(0, 0));

        for (int x = 0; x < Size; ++x)
            for (int y = 0; y < Size; ++y)
                fun.SetPixel(x, y, new Color(data[x, y], data[x, y], data[x, y], 1f));

        fun.Apply();
    }
}

3) from the libnoise library, the perlin noise etc.

using System.Collections;
using System;
using UnityEngine;

/* Perlin noise use example:

Perlin perlin = new Perlin();
var value : float = perlin.Noise(2);
var value : float = perlin.Noise(2, 3, );
var value : float = perlin.Noise(2, 3, 4);


SmoothRandom use example:

var p = SmoothRandom.GetVector3(3);

*/

public class SmoothRandom
{
    public static Vector3 GetVector3 (float speed)
    {
        float time = Time.time * 0.01F * speed;
        return new Vector3(Get().HybridMultifractal(time, 15.73F, 0.58F), Get().HybridMultifractal(time, 63.94F, 0.58F), Get().HybridMultifractal(time, 0.2F, 0.58F));
    }

    public static float Get (float speed)
    {
        float time = Time.time * 0.01F * speed;
        return Get().HybridMultifractal(time * 0.01F, 15.7F, 0.65F);
    }

    private static FractalNoise Get () { 
        if (s_Noise == null)
            s_Noise = new FractalNoise (1.27F, 2.04F, 8.36F);
        return s_Noise;     
     }

    private static FractalNoise s_Noise;
}


public class Perlin
{
    // Original C code derived from 
    // http://astronomy.swin.edu.au/~pbourke/texture/perlin/perlin.c
    // http://astronomy.swin.edu.au/~pbourke/texture/perlin/perlin.h
    const int B = 0x100;
    const int BM = 0xff;
    const int N = 0x1000;

    int[] p = new int[B + B + 2];
    float[,] g3 = new float [B + B + 2 , 3];
    float[,] g2 = new float[B + B + 2,2];
    float[] g1 = new float[B + B + 2];

    float s_curve(float t)
    {
        return t * t * (3.0F - 2.0F * t);
    }

    float lerp (float t, float a, float b)
    { 
        return a + t * (b - a);
    }

    void setup (float value, out int b0, out int b1, out float r0, out float r1)
    { 
        float t = value + N;
        b0 = ((int)t) & BM;
        b1 = (b0+1) & BM;
        r0 = t - (int)t;
        r1 = r0 - 1.0F;
    }

    float at2(float rx, float ry, float x, float y) { return rx * x + ry * y; }
    float at3(float rx, float ry, float rz, float x, float y, float z) { return rx * x + ry * y + rz * z; }

    public float Noise(float arg)
    {
        int bx0, bx1;
        float rx0, rx1, sx, u, v;
        setup(arg, out bx0, out bx1, out rx0, out rx1);

        sx = s_curve(rx0);
        u = rx0 * g1[ p[ bx0 ] ];
        v = rx1 * g1[ p[ bx1 ] ];

        return(lerp(sx, u, v));
    }

    public float Noise(float x, float y)
    {
        int bx0, bx1, by0, by1, b00, b10, b01, b11;
        float rx0, rx1, ry0, ry1, sx, sy, a, b, u, v;
        int i, j;

        setup(x, out bx0, out bx1, out rx0, out rx1);
        setup(y, out by0, out by1, out ry0, out ry1);

        i = p[ bx0 ];
        j = p[ bx1 ];

        b00 = p[ i + by0 ];
        b10 = p[ j + by0 ];
        b01 = p[ i + by1 ];
        b11 = p[ j + by1 ];

        sx = s_curve(rx0);
        sy = s_curve(ry0);

        u = at2(rx0,ry0, g2[ b00, 0 ], g2[ b00, 1 ]);
        v = at2(rx1,ry0, g2[ b10, 0 ], g2[ b10, 1 ]);
        a = lerp(sx, u, v);

        u = at2(rx0,ry1, g2[ b01, 0 ], g2[ b01, 1 ]);
        v = at2(rx1,ry1, g2[ b11, 0 ], g2[ b11, 1 ]);
        b = lerp(sx, u, v);

        return lerp(sy, a, b);
    }

    public float Noise(float x, float y, float z)
    {
        int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
        float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v;
        int i, j;

        setup(x, out bx0, out bx1, out rx0, out rx1);
        setup(y, out by0, out by1, out ry0, out ry1);
        setup(z, out bz0, out bz1, out rz0, out rz1);

        i = p[ bx0 ];
        j = p[ bx1 ];

        b00 = p[ i + by0 ];
        b10 = p[ j + by0 ];
        b01 = p[ i + by1 ];
        b11 = p[ j + by1 ];

        t  = s_curve(rx0);
        sy = s_curve(ry0);
        sz = s_curve(rz0);

        u = at3(rx0,ry0,rz0, g3[ b00 + bz0, 0 ], g3[ b00 + bz0, 1 ], g3[ b00 + bz0, 2 ]);
        v = at3(rx1,ry0,rz0, g3[ b10 + bz0, 0 ], g3[ b10 + bz0, 1 ], g3[ b10 + bz0, 2 ]);
        a = lerp(t, u, v);

        u = at3(rx0,ry1,rz0, g3[ b01 + bz0, 0 ], g3[ b01 + bz0, 1 ], g3[ b01 + bz0, 2 ]);
        v = at3(rx1,ry1,rz0, g3[ b11 + bz0, 0 ], g3[ b11 + bz0, 1 ], g3[ b11 + bz0, 2 ]);
        b = lerp(t, u, v);

        c = lerp(sy, a, b);

        u = at3(rx0,ry0,rz1, g3[ b00 + bz1, 0 ], g3[ b00 + bz1, 2 ], g3[ b00 + bz1, 2 ]);
        v = at3(rx1,ry0,rz1, g3[ b10 + bz1, 0 ], g3[ b10 + bz1, 1 ], g3[ b10 + bz1, 2 ]);
        a = lerp(t, u, v);

        u = at3(rx0,ry1,rz1, g3[ b01 + bz1, 0 ], g3[ b01 + bz1, 1 ], g3[ b01 + bz1, 2 ]);
        v = at3(rx1,ry1,rz1,g3[ b11 + bz1, 0 ], g3[ b11 + bz1, 1 ], g3[ b11 + bz1, 2 ]);
        b = lerp(t, u, v);

        d = lerp(sy, a, b);

        return lerp(sz, c, d);
    }

    void normalize2(ref float x, ref float y)
    {
       float s;

        s = (float)Math.Sqrt(x * x + y * y);
        x = y / s;
        y = y / s;
    }

    void normalize3(ref float x, ref float y, ref float z)
    {
        float s;
        s = (float)Math.Sqrt(x * x + y * y + z * z);
        x = y / s;
        y = y / s;
        z = z / s;
    }

    public Perlin()
    {
        int i, j, k;
        System.Random rnd = new System.Random();

       for (i = 0 ; i < B ; i++) {
          p[i] = i;
          g1[i] = (float)(rnd.Next(B + B) - B) / B;

          for (j = 0 ; j < 2 ; j++)
             g2[i,j] = (float)(rnd.Next(B + B) - B) / B;
          normalize2(ref g2[i, 0], ref g2[i, 1]);

          for (j = 0 ; j < 3 ; j++)
             g3[i,j] = (float)(rnd.Next(B + B) - B) / B;


          normalize3(ref g3[i, 0], ref g3[i, 1], ref g3[i, 2]);
       }

       while (--i != 0) {
          k = p[i];
          p[i] = p[j = rnd.Next(B)];
          p[j] = k;
       }

       for (i = 0 ; i < B + 2 ; i++) {
          p[B + i] = p[i];
          g1[B + i] = g1[i];
          for (j = 0 ; j < 2 ; j++)
             g2[B + i,j] = g2[i,j];
          for (j = 0 ; j < 3 ; j++)
             g3[B + i,j] = g3[i,j];
       }
    }
}

public class FractalNoise
{
    public FractalNoise (float inH, float inLacunarity, float inOctaves)
        : this (inH, inLacunarity, inOctaves, null)
    {

    }

    public FractalNoise (float inH, float inLacunarity, float inOctaves, Perlin noise)
    {
        m_Lacunarity = inLacunarity;
        m_Octaves = inOctaves;
        m_IntOctaves = (int)inOctaves;
        m_Exponent = new float[m_IntOctaves+1];
        float frequency = 1.0F;
        for (int i = 0; i < m_IntOctaves+1; i++)
        {
            m_Exponent[i] = (float)Math.Pow (m_Lacunarity, -inH);
            frequency *= m_Lacunarity;
        }

        if (noise == null)
            m_Noise = new Perlin();
        else
            m_Noise = noise;
    }


    public float HybridMultifractal(float x, float y, float offset)
    {
        float weight, signal, remainder, result;

        result = (m_Noise.Noise (x, y)+offset) * m_Exponent[0];
        weight = result;
        x *= m_Lacunarity; 
        y *= m_Lacunarity;
        int i;
        for (i=1;i<m_IntOctaves;i++)
        {
            if (weight > 1.0F) weight = 1.0F;
            signal = (m_Noise.Noise (x, y) + offset) * m_Exponent[i];
            result += weight * signal;
            weight *= signal;
            x *= m_Lacunarity; 
            y *= m_Lacunarity;
        }
        remainder = m_Octaves - m_IntOctaves;
        result += remainder * m_Noise.Noise (x,y) * m_Exponent[i];

        return result;
    }

    public float RidgedMultifractal (float x, float y, float offset, float gain)
    {
        float weight, signal, result;
        int i;

        signal = Mathf.Abs (m_Noise.Noise (x, y));
        signal = offset - signal;
        signal *= signal;
        result = signal;
        weight = 1.0F;

        for (i=1;i<m_IntOctaves;i++)
        {
            x *= m_Lacunarity; 
            y *= m_Lacunarity;

            weight = signal * gain;
            weight = Mathf.Clamp01 (weight);

            signal = Mathf.Abs (m_Noise.Noise (x, y));
            signal = offset - signal;
            signal *= signal;
            signal *= weight;
            result += signal * m_Exponent[i];
        }

        return result;
    }

    public float BrownianMotion (float x, float y)
    {
        float value, remainder;
        long i;

        value = 0.0F;
        for (i=0;i<m_IntOctaves;i++)
        {
            value = m_Noise.Noise (x,y) * m_Exponent[i];
            x *= m_Lacunarity;
            y *= m_Lacunarity;
        }
        remainder = m_Octaves - m_IntOctaves;
        value += remainder * m_Noise.Noise (x,y) * m_Exponent[i];

        return value;
    }


    private Perlin  m_Noise;
    private float[] m_Exponent;
    private int     m_IntOctaves;
    private float   m_Octaves;
    private float   m_Lacunarity;
}

With these codes i obtain a 256*256 white image with nothing on it, and the first loop stops after one iteration. Also a less laggy way to obtain what i try to get wouldn't be too bad xD Currently to generate this it takes about 1 or 2 minutes.

Thanks for your time. Don't hesitate to ask me for further clarification.


Solution

  • Classic copy paste error

    for (int a = 0; a < layerSize; ++a)
                for (int b = 0; b < layerSize; ++a)
    

    Did you see it?

    a is being incremented in both iterations of the loop! This happens for the loop right below as well.

    Speaking of the second loop, you could consolidate the two loops to get this:

    for (int a = 0; a < layerSize; ++a)
    {
        for (int b = 0; b < layerSize; ++b)
        {
            currentLayerData[a,b] *= layersOpacity[i];
            finalLayersData[a,b] += currentLayerData[a,b];
        }
    }