Pedro Henrique
Pedro Henrique

Reputation: 782

Animate an UV sphere with (4D?) noise

I am using a C# port of libnoise with XNA (I know it's dead) to generate planets.

There is a function in libnoise that receives the coordinates of a vertex in a sphere surface (latitude and longitude) and returns a random value (from -1 to 1).

So with that value, I can change the height of each vertex on the surface of the sphere (the altitude), creating some elevation, simulating the surface of a planet (I'm not simply wrapping a texture around the sphere, I'm actually creating each vertex from scratch).

An example of what I have:

enter image description here

Now I want to animate the sphere, like this

But the thing is, libnoise only works with 3D noise. The "planet" function maps the latitude and longitude to XYZ coordinates of a cube.

And I believe that, to animate a sphere like I want to, I need an extra coordinate there, to be the "time" dimension. Am I right? Or is it possible to do this with what libnoise offers?

OBS: As I mentioned, I'm using an UV sphere, not an icosphere or a spherical cube.

EDIT: Here is the algorithm used by libnoise to map lat/long to XYZ:

public double GetValue(double latitude, double longitude) {           

    double x=0, y=0, z=0;

    double PI = 3.1415926535897932385;
    double DEG_TO_RAD = PI / 180.0;

    double r = System.Math.Cos(DEG_TO_RAD * lat);

    x = r * System.Math.Cos(DEG_TO_RAD * lon);
    y = System.Math.Sin(DEG_TO_RAD * lat);
    z = r * System.Math.Sin(DEG_TO_RAD * lon);

    return GetNoiseValueAt(x, y, z);

}

Upvotes: 0

Views: 1067

Answers (3)

Lennart
Lennart

Reputation: 504

Sorry for the late answer, but I couldn't find a satisfactory answer elsewhere online, so I'm writing this up for anyone who has this problem in the future.

What worked for me was using multiple 3d perlin noise sources, and combining them into 1 single noise source. Adding time to the xyz coordinates just creates a very noticeable effect of terrain moving in the (-1,-1,-1) direction. Averaging over 4 uncorrelated noise sources does change the noise characteristics a bit, so you might have to adapt some factors to your use case. This solution still isn't perfect, but I haven't seen any visual artifacts.

Code is C++ libnoise, but it should translate equally well to other languages.

noise::module::Perlin perlin_noise[4];

float get_height(ofVec3f p, float time) {
    p*=2;
    time /= 10 ;
    return (perlin_noise[0].GetValue(p.x, p.y, p.z) +
            perlin_noise[1].GetValue(p.x, p.y, time) +
            perlin_noise[2].GetValue(p.x, time, p.z) +
            perlin_noise[3].GetValue(time, p.y, p.z))/2;
}

Ideally, for a single 3d noise source, you want to multiply you x,y,z coords with a monotonic function of t, such that it explores a constantly expanding sphere surface of the noise source, but I haven't figured out the math yet..

Edit: the framework I use (openframeworks) has a 4d perlin noise function built in ofSignedNoise(glm::vec4)

Upvotes: 0

Pedro Henrique
Pedro Henrique

Reputation: 782

Ok I think I made it.

I just added the time parameter to the mapped XYZ coordinates.

Using the same latitude and longitude but incrementing time by 0.01d gave me a nice result.

Here is my code:

public double GetValue(double latitude, double longitude, double time) {           

    double x=0, y=0, z=0;

    double PI = 3.1415926535897932385;
    double DEG_TO_RAD = PI / 180.0;

    double r = System.Math.Cos(DEG_TO_RAD * lat);

    x = r * System.Math.Cos(DEG_TO_RAD * lon);
    y = System.Math.Sin(DEG_TO_RAD * lat);
    z = r * System.Math.Sin(DEG_TO_RAD * lon);

    return GetNoiseValueAt(x + time, y + time, z + time);

}

If someone has a better solution please share it!

Upvotes: 1

Pikalek
Pikalek

Reputation: 946

An n dimensional noise function takes n independent inputs (i0, i1, ..., in-1, in) & returns a value v, thus 3D noise is sufficient to generate a height map that varies over time. In your case the inputs would be longitude, latitude & time and the output would be the height offset.

The simple general algorithm would be:

at each time step (t){
  for each vertex (v) on a sphere centered on some point (c){
    calculate the longitude & latitude
    get the scalar noise value (n) for the longitude, latitude & time
    calculate the new vertex position (p) as follows p = ((v-c)n)+c
  }
}

Note: this assumes you are not replacing/modifiying the original vertex values. You could either save a copy of them (uses less computation, but more memory) or recalculate them them based on a distance from c (uses less memory, but more computation). Also, you might get a smoother animation by calculating 2 (or more) larger time steps & interpolating to get the intermediate frames.

To the best of my knowledge, this solution should work for a UV sphere, an icosphere or a spherical cube.

Upvotes: 1

Related Questions