Search Unity

(LibNoise) How to offset a Spherical Generated Noise?

Discussion in 'Scripting' started by TheGameNewBie, Jun 24, 2020.

  1. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    I'm using LibNoise to generate noisemap that can be projected onto a Sphere and it works. But I don't know how to offset the noise.
    If I give the same values of Octave Count, Lacunarity, Frequency every time. it generates the exact same noise. So, If I have two spheres with same values of the above Variables, those two spheres look exactly the same.
    The 'Generate Spherical' function takes lat-long values for generation. I tried adding an offset value to the co-ordinates but it breaks the seamless-ness of the noise.
    Is there a way to offset the noise?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Offsets are an addition.

    Scaling is a multiplication.

    Both are linear operations so if they are applied across a continuous dataset, they will not introduce any nonlinearities (seams).
     
  3. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    The thing is, There doesn't seem to be a way to add the Offset to the noise. I've done this with noise mapped on a plane. But this time it is a sphere.
    For Example,
    https://answers.unity.com/questions/647396/libnoise-how-to-use-it.html

    You can see, in the first script, There are offset variables for this purpose. But in the last script (For the Spherical Terrain), There are no offset variables, It directly uses the lat-long coordinates (-90, 90, -180, 180). These co-ordinates work perfectly fine. But If I try to add something to these co-ordinates, the noisemap gets a visible seam.

    Here's the original documentation,
    http://libnoise.sourceforge.net/tutorials/tutorial8.html
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Hm, I haven't used that API. If there is a forum for libnoise, you might want to ask there. If it's open source, see how you can modify it to add an offset.
     
  5. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    I agree with Kurt that should not be an issue. Maybe your understanding or execution is wrong so I'll try to explain how it should work.
    Imagine your noise as a 3 dimensional field of values. You take noise values from a certain coordinate in that 3d space. To map them on the sphere the "shape" you take the values from must also be a sphere so it is seamless. Frequency is manipulated by the radius of the sphere so choosing a higher radius makes the noise more granular. Then you just sample different spheres with different radii for your octaves and multiply them together. You could (and probably should) also sample all octave values at once for each point on the sphere.
    I had stored a list of points on the unit sphere (normalized) where I wanted to sample values for (either the "positions" of the texels or the vertices, what you need). Then I just took that point multiplied it with the desired radius (length of the vector) and queried the noise value for it. I think this is the point where you should add another vector which shifts the whole sphere (which you sample from) towards a random direction. This way you get another planet for a different position in "noise space" and thus it looks different.

    So for a certain point of your list the "formula" should be (from top of my head):
    noisesampleposition = randomShiftVector + (normalizedVector * radius)

    I hope this describes how it should work for you. I'm not native english speaker so sorry when something is unclear. Maybe it helps to show us your code?

    note that lat long spheres have singularities at the poles which look bad (especially Unity's built in sphere). So maybe take a cube or octahedron sphere as a base to have a more even distribution of points. Also using a rectangular texture has issues at the poles so maybe investigate a more sophisticated texturing (fe cubemap).

    In general is to say that procecdural planet generation is a complicated (but IMO interesting) topic. If you don't want to "waste" time maybe use a premade solution from the AssetStore and adjust it to your needs.
     
  6. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    It works now, Just had to modify the source to consider the offset as @Kurt-Dekker said.
    Here's the code I use to generate the noisemap,
    Code (CSharp):
    1. void GenerateNoise ()
    2.     {
    3.         colorGradient = GetRandomGradient();
    4.         Frequency = Random.Range(0.1f, 7f);
    5.         lacunarity = Random.Range(0f, 1f);
    6.         persistance = Random.Range(0f, 1f);
    7.  
    8.         OffsetX = Random.Range(0f, 999f);
    9.         OffsetY = Random.Range(0f, 999f);
    10.         OffsetZ = Random.Range(0f, 999f);
    11.  
    12.         Perlin perlin = new Perlin();
    13.         perlin.OctaveCount = Octave;
    14.         perlin.Frequency = Frequency;
    15.         perlin.Lacunarity = lacunarity;
    16.         perlin.Persistence = persistance;
    17.         perlin.Quality = QualityMode.High;
    18.  
    19.         ModuleBase perlinModule;
    20.         perlinModule = perlin;
    21.  
    22.         Noise2D noise = new Noise2D(resolution.x, resolution.y, perlinModule);
    23.         noise.GenerateSpherical(south, north, west, east, OffsetX, OffsetY, OffsetZ);
    24.  
    25.  
    26.         textureMap = noise.GetTexture(colorGradient);
    27.         normalMap = noise.GetNormalMap(10f);
    28.         grayscaleMap = noise.GetTexture(grayscaleGradient);
    29.  
    30.         ApplyMaterialsToPlanet();
    31.     }

    Get spherical value function (I changed this, just added the offset variables)
    Code (CSharp):
    1. private double GenerateSphericalValue(double lat, double lon, float offsetx, float offsety, float offsetZ)
    2.         {
    3.             var r = Math.Cos(Mathf.Deg2Rad * lat);
    4.             return _generator.GetValue((r * Math.Cos(Mathf.Deg2Rad * lon)) + offsetx, (Math.Sin(Mathf.Deg2Rad * lat)) + offsety,
    5.                 (r * Math.Sin(Mathf.Deg2Rad * lon)) + offsetZ);
    6.         }


    and this is the GenerateSpherical function,
    Code (CSharp):
    1. public void GenerateSpherical(double south, double north, double west, double east, float offsetX, float offsetY, float offsetZ)
    2.         {
    3.             if (east <= west || south <= north)
    4.             {
    5.                 throw new ArgumentException("Invalid east/west or north/south combination");
    6.             }
    7.             if (_generator == null)
    8.             {
    9.                 throw new ArgumentNullException("Generator is null");
    10.             }
    11.             var loe = east - west;
    12.             var lae = north - south;
    13.             var xd = loe / ((double) _width - _ucBorder);
    14.             var yd = lae / ((double) _height - _ucBorder);
    15.             var clo = west;
    16.             for (var x = 0; x < _ucWidth; x++)
    17.             {
    18.                 var cla = south;
    19.                 for (var y = 0; y < _ucHeight; y++)
    20.                 {
    21.                     _ucData[x, y] = (float) GenerateSphericalValue(cla, clo, offsetX, offsetY, offsetZ);
    22.                     if (x >= _ucBorder && y >= _ucBorder && x < _width + _ucBorder &&
    23.                         y < _height + _ucBorder)
    24.                     {
    25.                         _data[x - _ucBorder, y - _ucBorder] = (float) GenerateSphericalValue(cla, clo, offsetX, offsetY, offsetZ);
    26.                             // Cropped data
    27.                     }
    28.                     cla += yd;
    29.                 }
    30.                 clo += xd;
    31.             }
    32.         }

    Yep, I'm using a cube sphere, But I still get some distortions at the poles, I guess it's not possible to completely remove them.
    Planet.png

    Here's the generated Texture,
    Texture.png

    I thought Cubemaps were used for reflections or skyboxes? I tried using them on a Sphere, but the texture is 'inside-out'. Can you tell me how to use them?

    Thanks for the help btw.
     
    Last edited: Jun 26, 2020
  7. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    You have to differentiate distortion coming from your vertices and coming from your texture. In a cube sphere the vertices are more or less evenly spread. But when you look at your rectangular texture you will still see the topmost and bottomost line apply to a single vertex. This is the distortion coming form the texture I was talking about.
    If you imagine a lat/long spheres vertices you see the singularity at the poles. So you use a cube sphere to prevent it and spread the vertices more evenly. But the texels of your texture are still mapped in the lat long manner and thus still create the singularity.

    Maybe the term cubemaps is not appropriate in this context. And I did not mean to use the built in one. I apologize if this leads to misunderstanding. So I'll try to explain what I mean.
    When you look at your rectangular texture you see the top and bottom (poles) look different than the middle (equator). The pole region shows more detail on a smaller area than the equator region.
    So what I meant with "cubemaps" is, that you use textures which texels spread mor evenly (like the vertices of a cube sphere). So instead of having a rectangular texture for the whole sphere which shows a whole line of the texture in one point, you could fe map the texure in a more even way on your initial cube. Imagine a rectangular texture divided into 6 square regions like the horizontal or vertical layout shown here. Each of this squares represents a single side of your cube. So you don't use lat/long uv-mapping for your vertices but a more "sophisticaed" (and complicated) one. So when you create your initial cube you apply uv coordinates to the vertices which match the position of the cube face in the texture. Then subdivision should take care of the vertices inbetween. The tricky part is to get a "mapping" function to map your lat/long coordinates to the uv coordinates and vice versa.
    You could also have each side of the cube be a separate mesh and apply a single square texture to each side of the cube (with it's own material). This probably makes mapping a bit easier but inreases draw calls. But you could also deactivate the 3 faces which are not seen by the player and this also allows for higher resolution of the underlying mesh per cube face (if you aim for higher resolution meshes). And this would also work well if you later want to create dynamic LOD and such advanced stuff.

    I know this is complicated but this is really advanced stuff (and my experiments with it were many years ago). If you do it for learning purpose kudos. If you want to quickly apply it to your game there are a few tutorials, assets or projects out there (not necessarily for unity). Unfortunately most of the advanced assets (Etherea, Planetary Terrain) available are deprecated (and thus not sold any more).

    Hope this helps a bit.
     
  8. TheGameNewBie

    TheGameNewBie

    Joined:
    Jul 27, 2017
    Posts:
    92
    I tried this. The mapping works, there is no more distortion, But I get some weird lighting issues after applying a normal map.
    weirdLighting.PNG
    I guess its because of the normals not aligned properly?
     
  9. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    Could also be the tangents. Difficult to say. How do you calculate the normals? I guess with this custom setup mesh class's method won't work here and you will have to do it yourself. Also you should somehow access the vertices on the other mesh (or recalculate the values there on the fly) so the first visible vertex is not a border one. And the "dummy" vertices should also not be indexed in the mesh. IIRC this was needed for the tangents to work properly. But it is several years that I tried this stuff so memory does not serve me good here. As I said, it's a complicated topic ;).
    Have you tried switching the lights off (too see wether it's caused by that)?
    Maybe post the code of the mesh generation.