1
$\begingroup$

A few years ago I started creating a procedural planet engine/renderer for a game in Unity, which after a couple of years I had to stop developing due to lack of time. At the time i didn't know too much about shaders so I did everything on the CPU. Now that I have plenty of time and am more aware of what shaders can do I'd like to resume development with the GPU in mind.

For the terrain mesh I'm using a cubed-sphere and chunked LODs. Since terrain never changes, heights for each chunk need to be calculated just once.The way I calculate heights is rather complex since it's based on a noise tree, which is created from an artist/designer-provided XML for each main planet/asteroid. Leaf nodes in the tree would be noise generators like Simplex, Value, Sine, Voronoi, etc. while branch nodes would be filters and modifiers (FBM, abs, neg, sum, ridged, billow, blender, selectors, etc).

To calculate the heights for a mesh you'd call void CalcHeights( Vector3[] meshVertices, out float[] heights ) on the noise's root node, somewhere in the game code.

This approach offers a lot of flexibility but also introduces a lot of load in the CPU. The first obvious thing to do would be (I guess) to move all generators to the GPU via compute shaders, then do the same for the rest of the filters. But, depending on the complexity of the noise tree, a single call to CalcHeights could potentially cause dozens of calls back and forth between the CPU and GPU, which I'm not sure it's a good thing.

How should I go about this?

$\endgroup$
  • 2
    $\begingroup$ "Now that I have plenty of time and am more aware of what shaders can do I'd like to resume development with the GPU in mind." Is this terrain that the physics/AI/collision system needs to know about? Because if it is, the GPU is probably not the best place to do this. $\endgroup$ – Nicol Bolas Sep 18 '18 at 3:23
  • $\begingroup$ @NicolBolas Yeah it is, but since terrain never changes, chunks just need to be calculated once. $\endgroup$ – Trap Sep 18 '18 at 16:54
3
$\begingroup$

r a similar problem (a tree of combined noise functions, evaluated on the GPU), I found a good method is to generate a shader from the expression tree. Each predefined node corresponds to a single shader function, e.g.

float simplexNode(vec3 pos) {
  // ... implementation of simplex noise
}

or

float sumNode(float val1, float val2) {
  return val1 + val2;  // ... possibly clipped or whatever
}

You'd then create a shader like

#version 300 core
// ... include node definitions here
in vec3 vertexPos;
out float vertexHeight;
void main() {
  float f1 = simplexNode(vertexPos);
  float f2 = worleyNode(vertexPos);
  float f3 = sumNode(f1,f2);
  // .. a bunch more
  vertexHeight = f76;
}

Constant parameters could either be uniforms, or just pasted straight into the code.

One limitation of this method is that the noise functions and methods have to be local, i.e. the evaluation can be done pointwise. In my case, some nodes were blends and convolutions, so I had to evaluate pieces of the expression tree into textures, blend or convolve the textures into new textures, then use these textures as leaves for the rest of the expression tree. All of the combinators you mentioned seem to be local, so this may not be a problem for you.

$\endgroup$
  • $\begingroup$ Can't think of a better way to do this, thanks. Maybe I also could get rid of functions and write all the code in a single function "noise(vec3 v)"? or do shader compilers do the inlining work for me? $\endgroup$ – Trap Sep 19 '18 at 13:03
  • 1
    $\begingroup$ @Trap: I don't know how inlining works in shaders. In my case, I wrote the nodes as their own functions because I could just drop the big chunk of prewritten text at the beginning of the shader and go from there. I also didn't have many nodes (I inlined the arithmetic, so only more complex operations had their own functions). I think it may also be possible to use glAttachShader to precompile the node functions and link them to each of the created shaders to provide the functions to them, though I didn't do that. $\endgroup$ – gilgamec Sep 19 '18 at 14:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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