Create Hills (make noise ) of curved surface - How To?

Hello, everyone

I am a beginner of unity development and also working on my project about planetary terrain…May i explain your my question first.

I am also creating my planet from 6 planes and the resolution of each plane is the same, they could be given by variable. The vertices of each plane mesh have been calculated with resolution and set to each plane again.

Image 1.


The following image shows what i want to create, would your like to explain to me / give me some suggestions, what should i do next to make the planet below? :wink:

Image 2: enter link description here

Thank your very much!!!

Carvin.

Have a look at this thread…

Procedural Terrain Rendering How-To

Hi, cybercritic

thank you for your answer!

It looks like you have already done your own planet generator with XNA or OpenGL ? Do you think i should learn that first…but it is possible to have a simple way to adjuste vertices randomlly? Sorry for my cardboard question, but i really dont have idea about how to do next…

Thanks, Carvin

It’s a lot of work and things get crazy if you want to do real scales, so crazy that it will eat up all your available time.

1 Like

Thank you for your answer, cybercritic

I want to take timw to do the real planet in Unity, but i need to enter the door for the development, now i dont know if i can do it, but if i know the steps, i will try it…would you like to teach me that?

Thank you very much!!!

Carvin

It looks like you have various topics you’ll have to investigate (how to use noise how to set and displace vertices etc). I think Unity can be a good starter, a lot of things you need are already there and you can setup a scene and place your first objects and experiment easily. Sure it has its limitations, but right now you want to start to understand things, not create a 100% perfect engine from scratch - no, be prepared to start and throw away your work and restart again a few times.

My advice, but thats just my personal opinion, you could start with Unity but you should try to create everything on CPU first! - Results are achiveable more easy, and the very very very basic process is the same. IMHO, if you are not able to create a procedural planet on the CPU and dont understand what to do, you’ll have an even more tough time to do that on the GPU. Decide for a an object type that represents a plane (an quadsphere or an icosphere for example), apply noise , etc etc. And: dont start in 1:1 scales initially. Its more easier to inspect the meshes in Unity’s scene viewer when they are at scales Unity typically handles - and an earth is something Unity is not used to handle :wink:

Typically start with a quadsphere. So yo create six planes that represent a cube within [-1,+1] value range. When I say “create six planes” I mean create them from scratch. So you completely (!) create your own mesh by setting each vertice position, UVs, normals. Nothing out of the box. Then, normalise the vertice positions and you have a sphere. Multiply with a radius value (nothing too big) and you have a bigger sphere. Finally you apply a noise value to each vertice position by multiplying it with a noise value (input values for the noise are your vertice positions).
Then you have your first sphere with “hills” and can start to experiment further (apply textures etc).

Things you should google and read
"procedural planet" or “procedural terrain” - obvious, but there a are lots of articles around that cover the above.

"perlin noise" or “simplex noise” - typical noise representation, and you should understand how the different parameters (amplitute, frequency, etc.) influence your noise result. e.g.: http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

"fBm" or “fractional Brownian motion” which orchestrates noise in multiple octaves. Using this you can create very natural looking surfaces already.

And if you feel settled with noise and have created a quadsphere with noise applied yourself, you should google for “quadtree” which helps you to organise your data when you start splitting and merging planes to add more detail.

Again, my best advice I can give: Start on the CPU, Unity is a good engine for that, and really understand what you do, and try yourself first here by checking some tutorials or artices. Then you’ll be able to ask detailed questions instead of “whats next” :wink: of course I can only guess how far you are already by these few lines here. But the above would be my 2 cents.

Yes you should, lots of things covered there. Althoug I think you should first be able to create a CPU terrain on your own by using various other articles before jumping into the above one.

cybercritic is 100% right. A lot of work, you’ll get crazy, and loose a lot of time.

2 Likes

Hi, Joerg

Thank you for your answer and suggestions, i am grad to read it.

You are right, first i must understand how to use noise and what do i need to create noise.

I have already created a sphere from 6 planes by using the GameObject.CreatePrimitive(plane)…it looks like the image show above. there are a lot of introductions of the same projects like this one (http://www.nullpointer.co.uk/content/procedural-planets/). Do you think i should try to use heighmap to create the noise like that?

Or you meant i should start with CPU or GPU ( c++ programming), so i need also libraries, what kind of libraries have you used for your prohects`?

Like you discribed (“Typically start …and you have a sphere … you have a bigger sphere.” ) i have already done the steps, but i just need a method to create the noise / hills of these curved surface (Box -> Sphere already done.)

I have also tried to create waves of the planes after i have visited some links about simplex noise. It looks like following image: http://i65.tinypic.com/a7053.png

I think i know what should i do next, but i realy not sure, maybe i should only limit all side line of 6 planes to keep their position not to be changed? Or have you another idea?

Thank you very much!

Carvin

No, you shouldn’t use GameObject.CreatePrimitive(), but instead create the mesh completely yourself. Once you do this it will be pretty obvious for you how you can displace the vertices like you want to do to create the hill-like surface you’ve linked to - because you’ve created every single vertice position coordinate (as well as UV or normals) yourself and thus have direct acess to it.

I need to look for a simple example how to do that. Its basically about creating a 2D matrix (= a plane) of vector coordinates and storing them in an array. There are different ways how to do that, the example might give you a start…
Going to add the example tomorrow as its already late that evening.

Not initially. While you create the plane completely itself (so not using a Unity primitive) it will be only a few lines of code added to use noise to displace your vertices. There are tons of other techniques how to create a visual noised surface, but you should start with the simplest one.

I’d say depends on if you already have skills on a certain language or graphics engine. I dont want to push you into a certain direction, it depends on your likes, but as you started and got used with Unity I think you could go forward with Unity and use it’s C# and create everything on CPU, as its very userfriendly.

Well I am unsure, you wrote you’ve used Unity’s prebuilt primitives (GameObject.CreatePrimitive() ), I am unsure if you’ve already finished that step and created everything from scratch yourself to be honest. Its a little tough to understand where you are at currently, maybe you can add some lines of code where you created the plane, sphere and pushed everything larger by radius.

But my impression is you should get a step back and create everything completely yourself, as mentioned. You likely have to do this when you do this with another engine / language too, and it helps you to understand whats going on when you move to GPU later. Going to add the example of the manually created mesh tomorrow, maybe it is of some help.

Its a little tough to read between your lines if you just want to know the optimal noise algorithms and parameters to create a natural surface (in this case cybercritics linked thread is a good read) and your have already full control and flexibility with your planes and access to every parameter. Or if you have problems to understand how to apply any kind of noise to your planes - then your current code and the fact that you use prebuilt prefabs might be your problem.

1 Like

Hi, Joerg

Thank you very much for your detailed answer. :slightly_smiling:

Did you mean, i should create single mesh with vertices first via script? Single mesh for box from 6 planes? Then transform it to a sphere, so that i have control of all vertices of them?

Or is my understanding wrong?

Thank you!

Carvin

That’s how I do it, yes. I have a script that runs when the game starts, and which via code creates a mesh, its vertices, calculates its triangles, UVs, normals, etc. Granted, I apply noise to these values on the GPU at a later time, but completely agree with Joerg’s advice of sticking with the CPU to start. Much easier!

Yes, a single mesh with vertices from script. But no, not a single mesh for the whole box. But instead one mesh per side of the box. So you should end up with 6 meshes created by script.

Find below an example how you could do that (there are many ways, the below might not be most efficient one as it has redundant code, but it is very “readable”). I havent tested it lately but it worked if I remember right, and quickly added the loops to transform the plane to a spherical representation, and added example loops to apply a radius and noise. You would have to replace the radius with your desired one, and also implement the function to get a real noise value (google simplex noise or perlin noise). The funtion used the number of vertices (e.g. 32) and the desired Up-Vector of your plane (to define the orientation of the plane) as input. Please note that the vertices are shared between the triangles, which is OK for terrain.

Later on when you move on, it will be a good idea to store the parameters of your mesh (edge coordinates, vertices_pos, etc etc etc) in a new quadtree node class. But for the moment, have a look and play around with it, it might you give a first idea how to manually create a mesh.

using UnityEngine;
/// <summary>
/// Author: Joerg Zdarsky
/// Date: 2014-11-14
/// Service provider that offers service functions for updates on QuadtreeTerrain. It implements functions to create the vertices in [-1,1] range.
/// </summary>
public static class MeshServiceProvider
{
    public static Mesh setupSharedVerticesPlaneDummyMesh(int nVertsPerEdge, Vector3 UpVector)
    {

        // Complete Array Size: Width * Width
        int VERTEXARRAY_SIZE = nVertsPerEdge  * nVertsPerEdge ;

        // Prepare Arrays and objects
        Vector3[] vertices_pos = new Vector3[VERTEXARRAY_SIZE];
        Vector2[] vertices_uv = new Vector2[VERTEXARRAY_SIZE];
        Vector3[] vertices_normals = new Vector3[VERTEXARRAY_SIZE];
        int[] triangles = new int[(nVertsPerEdge - 1) * (nVertsPerEdge - 1) * 2 * 3];

        // Indices
        int index_vertices_pos = 0;
        int index_vertices_uv = 0;
        int index_vertices_normals = 0;
        int index_triangles = 0;

        // Steps
        float vertices_pos_step = 2.0f / (nVertsPerEdge  - 1.0f);
        float vertices_uv_step = 1.0f / nVertsPerEdge ;
        float uvX = 0;
        float uvY = 0;

        int VERTICES_WIDTH = nVertsPerEdge ;
        int TRIANGLES_WIDTH = nVertsPerEdge  - 1;

        // Creation of vertices
        // Vector3.up
        if (UpVector == Vector3.up)
        {
            float posX = 0; float posY = 1; float posZ = 0;
            // Creation-Loop of vertices
            for (int z = 0; z < VERTICES_WIDTH; z++)
            {
                for (int x = 0; x < VERTICES_WIDTH; x++)
                {
                    // vertices_pos
                    posX = -1 + (x * vertices_pos_step);
                    posZ = +1 - (z * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }
        // Vector3.down
        if (UpVector == Vector3.down)
        {
            float posX = 0; float posY = -1; float posZ = 0;
            // Creation-Loop of vertices
            for (int z = 0; z < VERTICES_WIDTH; z++)
            {
                for (int x = 0; x < VERTICES_WIDTH; x++)
                {
                    // vertices_pos
                    posX = -1 + (x * vertices_pos_step);
                    posZ = -1 + (z * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }
        // Vector3.right
        if (UpVector == Vector3.right)
        {
            float posX = 1; float posY = 0; float posZ = 0;
            // Creation-Loop of vertices
            for (int y = 0; y < VERTICES_WIDTH; y++)
            {
                for (int z = 0; z < VERTICES_WIDTH; z++)
                {
                    // vertices_pos
                    posY = +1 - (y * vertices_pos_step);
                    posZ = -1 + (z * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }
        // Vector3.left
        if (UpVector == Vector3.left)
        {
            float posX = -1; float posY = 0; float posZ = 0;
            // Creation-Loop of vertices
            for (int y = 0; y < VERTICES_WIDTH; y++)
            {
                for (int z = 0; z < VERTICES_WIDTH; z++)
                {
                    // vertices_pos
                    posY = +1 - (y * vertices_pos_step);
                    posZ = +1 - (z * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }
        // Vector3.back
        if (UpVector == Vector3.back)
        {
            float posX = 0; float posY = 0; float posZ = -1;
            // Creation-Loop of vertices
            for (int y = 0; y < VERTICES_WIDTH; y++)
            {
                for (int x = 0; x < VERTICES_WIDTH; x++)
                {
                    // vertices_pos
                    posX = -1 + (x * vertices_pos_step);
                    posY = +1 - (y * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }

        // Vector3.forward
        if (UpVector == Vector3.forward)
        {
            float posX = 0; float posY = 0; float posZ = +1;
            // Creation-Loop of vertices
            for (int y = 0; y < VERTICES_WIDTH; y++)
            {
                for (int x = 0; x < VERTICES_WIDTH; x++)
                {
                    // vertices_pos
                    posX = +1 - (x * vertices_pos_step);
                    posY = +1 - (y * vertices_pos_step);
                    vertices_pos[index_vertices_pos] = new Vector3(posX, posY, posZ);
                    index_vertices_pos++;
                    // vertices_normals
                    vertices_normals[index_vertices_normals] = UpVector;
                    index_vertices_normals++;
                }
            }
        }

        Debug.Log("PLANE index_vertices_pos:" + index_vertices_pos);

        // Creation of UV Coordinates
        for (int y = 0; y < VERTICES_WIDTH; y++)
        {
            for (int x = 0; x < VERTICES_WIDTH; x++)
            {
                // vertices_uv
                uvX = 0 + (x * vertices_uv_step);
                uvY = 1 - (y * vertices_uv_step);
                vertices_uv[index_vertices_uv] = new Vector2(uvX, uvY);
                index_vertices_uv++;
            }
        }

        // Creation-Loop of triangles
        for (int y = 0; y < TRIANGLES_WIDTH - 1; y++)
        {
            for (int x = 0; x < TRIANGLES_WIDTH - 1; x++)
            {
                // triangles
                // 1st triangle
                triangles[index_triangles] = y * nVertsPerEdge  + x;
                index_triangles++;
                triangles[index_triangles] = y * nVertsPerEdge  + x + 1;
                index_triangles++;
                triangles[index_triangles] = y * nVertsPerEdge  + x + 1 + nVertsPerEdge ;
                index_triangles++;
                // 2nd triangle
                triangles[index_triangles] = y * nVertsPerEdge  + x;
                index_triangles++;
                triangles[index_triangles] = y * nVertsPerEdge  + x + 1 + nVertsPerEdge ;
                index_triangles++;
                triangles[index_triangles] = y * nVertsPerEdge  + x + nVertsPerEdge ;
                index_triangles++;
            }
        }

        // To transform to sphere
        for (int i = 0; i < vertices_pos.Length; i++)
        {
            vertices_pos[i] = vertices_pos[i].normalized;
            vertices_normals[i] = vertices_pos[i].normalized; // We can do this as we create a plane sphere. If we start to add noise, we would have to take greater care of normals.
        }

        // To apply radius
        for (int i = 0; i < vertices_pos.Length; i++)
        {
            int radius = 6371; // < replace with your desired radius
            vertices_pos[i] *= radius;
        }

        // To apply noise
        for (int i=0;i< vertices_pos.Length;i++)
        {
            int noise = 1; // < replace with noise call, which uses vertices_pos[i] as input. make sure is has a reasonable size, relative to radius
            vertices_pos[i] *= noise;
        }

        Mesh mesh = new Mesh();
        mesh.vertices = vertices_pos;
        mesh.uv = vertices_uv;
        mesh.normals = vertices_normals;
        mesh.triangles = triangles;
        return mesh;
    }
}