0
\$\begingroup\$

Using the Unity docs page on Perlin noise, I made this script:

// Width and height of the texture in pixels.
public int pixWidth;
public int pixHeight;

// The origin of the sampled area in the plane.
public float xOrg;
public float yOrg;

// The number of cycles of the basic noise pattern that are repeated
// over the width and height of the texture.
public float scale = 1.0F;

private Texture2D noiseTex;
private Color[] pix;
private Renderer rend;

void Start()
{
    rend = GetComponent<Renderer>();
    scale = Random.Range(15, 20);
    xOrg = Random.Range(0, 20);
    yOrg = Random.Range(0, 20);
    // Set up the texture and a Color array to hold pixels during processing.
    noiseTex = new Texture2D(pixWidth, pixHeight);
    pix = new Color[noiseTex.width * noiseTex.height];
    rend.material.mainTexture = noiseTex;
}

void CalcNoise()
{
    // For each pixel in the texture...
    float y = 0.0F;

    while (y < noiseTex.height)
    {
        float x = 0.0F;
        while (x < noiseTex.width)
        {
            float xCoord = xOrg + x / noiseTex.width * scale;
            float yCoord = yOrg + y / noiseTex.height * scale;
            float sample = Mathf.PerlinNoise(xCoord, yCoord);
            pix[(int)y * noiseTex.width + (int)x] = new Color(sample, sample, sample);
            x++;
        }
        y++;
    }

    // Copy the pixel data to the texture and load it into the GPU.
    noiseTex.SetPixels(pix);
    noiseTex.Apply();
}

void Update()
{
    CalcNoise();
}

The only difference between this code and the Unity docs example is that I added some randomness to the size and origin points of the texture.

I attached the script to a plane, and now when I run the game, the plane has a random noise texture on it.

Now I want the texture to give height to the mesh, so it will look like a terrain. How do I do this?

\$\endgroup\$
0
\$\begingroup\$

First, I'd recommend pulling your noise calculation into its own function:

float SampleNoise(Vector2 normalizedPoint) {
    Vector2 samplePoint = origin + normalizedPoint * scale;
    return Mathf.PerlinNoise(samplePoint.x, samplePoint.y);
}

Then you can easily re-use the same function in both your texture generation and vertex displacement, without writing it twice (with the risk of changing one copy while forgetting to change the other). Inside your texture loop it would look like this:

Vector2 normalized = new Vector2(x / noiseTex.width, y / noiseTex.height);
float sample = SampleNoise(normalized);

Then you can do a second pass to update your vertices:

// Do this part once, in Start, so you're not allocating new vertex arrays constantly.
var mesh = ((MeshRenderer)rend).mesh;
var vertices = mesh.vertices;

// Update your vertex heights anytime your noise changes.
for(int i = 0; i < vertices.Length; i++) {
    var vertex = vertices[i];
    // The default plane goes from -5 to +5, so remap it to 0...1:
    var normalized = (new Vector2(vertex.x, vertex.z))/10f + Vector2.one * 0.5f;
    vertex.height = heightScale * SampleNoise(normalized);
}

mesh.vertices = vertices;

You can also apply this displacement in the vertex shader, by reading the texture itself. But modifying the source mesh CPU-side lets you use it for things like raycasts and collisions too.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.