0
$\begingroup$

I'm making a space game and so far what I have is a system to generate spheres with noise on them, making them planets. I make a sphere, create noise data, write that data to an image file, and then bind it to the sphere as a texture, and from there, I use that to make terrain. The problem I'm having is there are too many hills and valleys, and just looks like a spikeball instead of a planet sphere

sphere wireframe

Here's the function I use to generate the noise data

void write_noise_2d(int w, int h, int channels_num, const char* bmp_path)
{
        // create random seed for noise
        srand(time(NULL));
        float seed = (float)rand()/RAND_MAX;
        // initialize noise
        fnl_state noise = fnlCreateState();
        noise.noise_type = FNL_NOISE_OPENSIMPLEX2;
        noise.octaves = 6;
        noise.lacunarity = 2.0f;
        noise.gain = 0.5f;
        noise.frequency = 0.01f;
        noise.seed = (int)seed;
        printf("Seed: %i\n", seed);
        noise.fractal_type = FNL_FRACTAL_FBM;
        // create data array for noise
        float* noise_data = malloc(w * h * channels_num * sizeof(float));
        int index = 0;
        // create noise throughout the entire image
        for(int x=0; x<w; x++)
        {
            for(int y=0; y<h; y++)
            {
                noise_data[index++] = fnlGetNoise2D(&noise, x * 0.3f, y * 0.3f);
            }
        }

        stbi_write_bmp(bmp_path, w, h, channels_num, noise_data);
        free(noise_data);
}

Here's the function where I bind the noise to the sphere

unsigned int load_noise_2d(const char* path, unsigned int shader_program, const char* sampler, 
bool remove_noise)
{
    unsigned int noise;
    glGenTextures(1, &noise);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, noise);
    // from what I heard no need to specify gl_repeat because that is the default
    // filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_MIPMAP);

    // image stats
    int width, height, num_channels;
    // load image
    unsigned char* noise_data = stbi_load(path, &width, &height, &num_channels, 0);
    if(noise_data)
    {
        // image options
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_SHORT, noise_data);
        glGenerateMipmap(GL_TEXTURE_2D);

        // send to the GPU for processing
        set_int(shader_program, sampler, 0);
        printf("Loaded noise map of %i by %i\n", width, height);
    }
    else
    {
        printf("Failed to load noisemap\n");
    }
    // deallocate data that's no longer needed
    stbi_image_free(noise_data);
    if(remove_noise == true)
    {
        remove(path);
        printf("Deleted noise file\n");
    }
    else
    {
        printf("Kept noise file\n");
        return 0;
    }

    return noise;
}

For reference, this is how the function calls look like

    // write noise data from FNL to texture
write_noise_2d(512, 512, 2, "textures/2dnoise.bmp");
// now we load the noise map back into the program to be used later
load_noise_2d("textures/2dnoise.bmp", earthlike, "noisemap", true);

And finally, here's the tessellation shader, where I had a problem before with the intensity being too high, but that's solved

#version 450 core

// determines what type of tessellation to do
layout(triangles, equal_spacing, cw) in;

// input from control shader
in vec3 vertex_coord[];
// output vec
out vec3 vert;
// simplex noise goes here
uniform sampler2D noisemap;
// allows for object transformations
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out float height;

void main()
{
    // gets barycentric coordinates from the triangles
    vec3 x = gl_TessCoord.x * vertex_coord[0];
    vec3 y = gl_TessCoord.y * vertex_coord[1];
    vec3 z = gl_TessCoord.z * vertex_coord[2];
    vec3 v = x + y + z;
    // scale noise between an acceptable value
    float min_noise_value = 0.5f;
    float max_noise_value = 2.5f;
    float intensity_scale = 0.015f;
    float noise_range = max_noise_value - min_noise_value;
    // use texture for offsetting sphere vertices
    float noise = texture(noisemap, vec2(v)).r;
    // sphere offsetting multiplier
    height = min_noise_value + noise_range * noise * intensity_scale;
    // makes every triangle an equal distance from the center through normalization (that's how spheres are formed)
    vec3 pos = normalize(x + y + z);
    // apply noise
    pos *= height;
    // output tessellated shape
    gl_Position = projection * view * model * vec4(pos, 1.0);
}

For reference, this is sort of how I want my planet to look like instead notice how there is not a lot of frequency. It's just some occasional hills and valleys

$\endgroup$
2
  • $\begingroup$ The spiky look is usually the result of a lot of white noise in the noise texture. What does the noise texture look like? $\endgroup$
    – pmw1234
    Commented Jun 5, 2023 at 19:04
  • $\begingroup$ @pmw1234 i.imgur.com/LqOSbH7.png $\endgroup$
    – Chillzy
    Commented Jun 5, 2023 at 20:12

1 Answer 1

0
$\begingroup$

Based on the shader and the way it is using the noise texture there is to much "white noise" / "cell noise" in the noise texture.

Noise is usually built in layers with low frequencies making up the large features and higher frequencies making up the small features. The different frequencies are literally added together. Looking at the noise texture it looks like the high frequency "bumps" are either multiplied into the noise, or they are just overwritten directly. This will give very sharp edges and give the spiky result you are seeing.

Basically anywhere you see a sudden change from black to white in the noise you will get a cliff/spike.

To get the hilly looking terrain like the image you want then the noise texture should look something like this one: Gradual hills

The slow transition from black to gray then white gives the hills. This "noise" could also be described as low frequency, high amplitude noise.

To get the bumps this image would have the regular "square" pattern added in. This could be done in the shader itself by computing the pattern on a regular grid. That should make it easier to experiment with different amplitudes and frequencies for the pattern.

$\endgroup$
1
  • $\begingroup$ I wouldn't say the white noise is being caused in the image, I would say that the noise generator that created the image was generating a lot of white noise. An old trick to smooth out noise is to just blur the texture in an image editor. Noise is one of those subjects that seems simple on the surface, but is actually fairly complex. $\endgroup$
    – pmw1234
    Commented Jun 6, 2023 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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