How should I apply 3D noise to sphere?

Started by
17 comments, last by Chillzy 1 year ago

I'm currently trying to create planets and stars, and for that, I need noise (for planetary surfaces). Now, I know I need 3D noise because I need to sample the x, y, and z coordinates on each vertex, and I know how to use noise to offset the vertices, I just don't know which method I should choose to do it. I created my sphere through octahedron vertices on the CPU, and then uploaded the vertices to the GPU for tessellation and vertex normalization so I was thinking that I should try and just implement the noise functions straight on the GPU but that didn't work. I also tried modifying the vertices on the CPU with the noise functions, but that didn't work either. Lastly I tried creating the noise on the CPU, and through uniform variables, uploading the result of the noise functions onto the GPU but that also didn't work. If it helps the noise library I decided to use is Fast Noise Lite, so it has bindings for multiple languages, including the GLSL and C, the languages I'm using for this project. The library is found here https://github.com/Auburn/FastNoiseLite

This is along the lines of what I want the output to be like

None

Advertisement

You might want to read the blog post I wrote a while ago about this topic. I am generating 2D noise on the CPU iteratively with LOD, where as you zoom in more detail gets added to the previous LOD. I do this in 34x34 tiles (with 1 triangle overlap between 33x33 tiles). Once noise is generated for a current LOD, I build a mesh on the CPU and upload it to a VBO which stays around until the tile goes out of scope. Each tile takes 0.1ms to generate, and there are 600-3000 tiles, depending on LOD quality settings and distance from the surface.

You don't really need true 3D noise if you can ensure that the edges of a cube-sphere line up properly with coherent noise across the boundary. See my blog post for an image which shows how to divide a tile so that it has a shared border overlap with the neighbors. The only places where it gets difficult is at the cube corners, where there is a kind of singularity. The easiest solution for corners is to just not add any noise in those places, which makes them flatter than other places.

The alternative to this approach is to generate 3D noise in a voxel octree, but this seems unattractive because you will end up doing much more work per tile (i.e. if tiles are now 34x34x34, it's 34 times slower). No Man's Sky uses this kind of approach but it's clearly pretty slow (they have to limit ship movement speed to compensate, whereas my 2D approach can fly close to the surface at many km per second).

@Aressera I know I don't need 2D noise, but the reason why I decided to is because I want to render complex terrain like overhangs, caves, ravines, etc. Complex terrain that requires 3D noise.

None

Chillzy said:
I want to render complex terrain like overhangs, caves, ravines, etc. Complex terrain that requires 3D noise.

So do I, but my plan is to switch from 2D to 3D noise only when you get close enough to the surface for it to matter. I don't want to pay that 34x cost per tile unless I have to. Building the noise incrementally as you zoom in is important so that the transition from 2D→3D works smoothly. I'm not sure exactly how this will work because I'm still focused on 2D terrain and erosion simulation in that domain, but I think it should be possible.

You might be able to use two different layers of noise to define complex terrain. Use one 2D noise field such as mine to define the bulk shape of the terrain from aerial perspective, and then add details like caves with a second field of 3D noise only when you zoom in far enough. The 3D noise would represent a perturbation of the 2D heightfield terrain and could produce more complex shapes on a small scale.

@Aressera Yeah that makes sense, and I appreciate the help, but I still don't know how I should apply my noise. Like I said, I create the initial octahedron coordinates on the CPU, then upload the vertices to the GPU and create the sphere through tessellation and normalization. I tried modifying the vertices with noise on the CPU and then uploading that and it didn't work, I also tried directly on the GPU (via shaders), didn't work either. And I thought that I could just generate noise on the CPU, and then through uniforms, upload it to the tessellation evaluation shader for the uniform to modify the vertices from there, but either that didn't work, or I didn't do it right. The examples I've seen are from people who generate their spheres on the CPU, and I don't do that, so their ways aren't gonna work for me.

None

You are going to need to provide more information about what didn't work in order for anyone to help you. Just saying “it didn't work” doesn't tell us anything.

I suppose the reason most people generate planets on CPU is because doing the generation from scratch every single frame on the GPU is a waste of resources for data that is mostly static. Also, GPU will not be able to handle anything outside single precision floats. That effectively limits you to either planets less than about 10km in size, or prevents zooming in to human-scale (i.e. orbital view only). Telling us more about your requirements for world size and precision will determine what approach is feasible.

Aressera said:
You are going to need to provide more information about what didn't work in order for anyone to help you.

First thing I tried was using FNL (fastnoiselite, the noise library I'm using) to generate noise in my main file (C file), and from there, sending the noise values to the vertices defined on the CPU. In my mind, what should've happened was the octahedron would've gotten tessellated and normalized, with the added bonus that the vertices would've gotten offsetted like I wanted it to. The next method I tried was the same, except I defined a uniform variable in my TES and then use that as a sort of scale value that I multiply with the normalized octahedron coordinates. The last thing I tried was copying the GLSL FNL code into my TES directly and using / accessing functions from there. (All of these methods didn't work as you could probably guess.)

Aressera said:
I suppose the reason most people generate planets on CPU is because doing the generation from scratch every single frame on the GPU is a waste of resources for data that is mostly static.

The whole point of me creating my sphere the way I did was that it was the fastest method I found, all others not only were slow, but didn't even render the sphere in the correct way, no matter how much I tweaked it. Either way, like I said, I tried generating noise alongside the CPU vertices.

Aressera said:
Telling us more about your requirements for world size and precision will determine what approach is feasible.

The world sizes are as close to those of real life planets (and stars). As for precision, really anything that has a good balance between performance friendly, and actually accurate. It doesn't have to be perfect, but ideally, it (obviously) has as best of performance as it can, while still being as accurate as the player can detect it. Not necessarily 100% accurate, but the bare minimum essentially.

None

We are still getting the “didn't work” description. Nothing you try will work the first time around. If you dig enough to figure out why it didn't work, you'll have a chance to either fix it or learn enough that you can then find a different solution that might work. Otherwise, you are just trying things in the dark.

alvaro said:
We are still getting the “didn't work” description.

What I mean when I say “didn't work” is either I got no output to my window, or there were no changes to the sphere

alvaro said:
If you dig enough to figure out why it didn't work, you'll have a chance to either fix it or learn enough that you can then find a different solution that might work.

I did, and from what I found, the noise wasn't making it to the part of the code where my sphere gets created, or that it just doesn't have any effect on the sphere vertices. I did my research and I haven't found a solution yet which is why I've come here.

None

Try using a debugger to see what's happening to the noise, or even adding a bunch of print statements. Compare a run with the noise disabled and a run with the noise enabled and see if the difference between them makes sense.

Debugging is hard, but you have the code and you can play around with it. We can't possibly help you with the information we have.

This topic is closed to new replies.

Advertisement