Procedural Infinite 3D Cave Generation


I want to show my latest project ‘Procedural Infinite 3D Cave Generation’. The idea I got from NVidia’s article: Chapter 1. Generating Complex Procedural Terrains Using the GPU.


Cave scalar field is sum of 5 random 3D noises. Each of them is tri-linearly interpolated. Marching tetrahedrons algorithm is used for polygonization. Texture coordinates for tri-planar texture and bump mapping are calculated in shader program. Geometry is calculated in another thread in CPU, not in GPU like in the NVidia’s article.

Here is my first shader program: ‘Triplanar texture and bump mapping shader’. The shader samples two textures: diffuse and normal.



varying vec4 vertex;

varying vec3 normal, vNormal, lightDir, eyeVec;

varying float att;

void main()


normal = normalize(gl_Normal);

vertex = gl_Vertex;

vNormal = gl_NormalMatrix * gl_Normal;

vec3 vVertex = vec3(gl_ModelViewMatrix * gl_Vertex);

lightDir = vec3(gl_LightSource[0] - vVertex);

eyeVec = -vVertex;

float d = length(lightDir);

att = 1.0 / ( gl_LightSource[0].constantAttenuation +

(gl_LightSource[0].linearAttenuation * d) +

(gl_LightSource[0].quadraticAttenuation * d * d));

gl_Position = ftransform();





uniform sampler2D sampler;

uniform sampler2D samplerBump;

varying vec4 vertex;

varying vec3 normal, vNormal, lightDir, eyeVec;

varying float att;

void main(void)


if(att > 0.01) {

// tri-planar texture bending factor

vec3 blending = (abs( normal ) - 0.2) * 7.0;

blending = normalize(max(blending, 0)); // Force weights to sum to 1.0 (very important!)

blending /= (blending.x + blending.y + blending.z ).xxx;

//vec3 blending = abs( normal );

vec3 signedBlending = sign(normal) * blending;

// texture coords

vec4 coords = vertex * 0.05;

vec4 col1 = texture2D( sampler, coords.yz );

vec4 col2 = texture2D( sampler, coords.zx );

vec4 col3 = texture2D( sampler, coords.xy );

vec3 nor1 = texture2D( samplerBump, coords.yz ).zxy - 0.5;

vec3 nor2 = texture2D( samplerBump, coords.zx ).yzx - 0.5;

vec3 nor3 = texture2D( samplerBump, coords.xy ).xyz - 0.5;

// Finally, blend the results of the 3 planar projections.

vec4 colBlended = col1 * blending.x + col2 * blending.y + col3 * blending.z;

vec3 norBlended = nor1 * signedBlending.x + nor2 * signedBlending.y + nor3 * signedBlending.z;

vec3 N = normalize(gl_NormalMatrix * norBlended);

vec3 L = normalize(lightDir);

float lambertTerm = max(0.2, dot(N,L));

colBlended = colBlended * lambertTerm * att;

vec3 E = normalize(eyeVec);

vec3 R = reflect(-L, N);

float specular = 0.25 * pow( max(dot(R, E), 0.0), 32 ) * att;

colBlended += specular * att;

gl_FragColor = colBlended;

} else {

gl_FragColor = vec4(0,0,0,0);





Wow, cool! So your are still working on this? Any chance to see some contribution growing out of this? It really looks great.



P.S.: I removed the “html link” around your youtube video link to it displays in the thread.

Gorgeous! This is like one of our coolest graphics showcases to date :slight_smile: I might be out of time for today but I’m gonna take a snapshot of the HD video and add it to our showcase screenshots. If you could give me a screenshot if your own though, that’d be even better. Ideally you also include a description of the image (SEO stuff basically), though I do have enough to go on by what you’ve explained in this post. Lastly, I suppose I could include a link of your choice.

Great job man.

@erlend_sh, shouldn’t you be sure that @mazander did this in jME3? The GLSL could be for a number of graphics packages :slight_smile:

Either way, it looks fantastic! Can’t wait to see more on this!

This is done with JME2. I haven’t tried JME3 yet. Maybe I should, because I want to test this with SSAO.

I see, sorry I jumped to conclusions. Too bad though, it won’t make the showcase then. If you do end up porting this to jME3 though, I guarantee you we’ll showcase it; it might very well be frontpage material :wink:

I’m at a lost where to start, but how do I use this in JME3? :smiley:

You can’t USE it. Mazender is just demonstrating his work.

It’s made for JME2, and the given shader code is just used to render the material of the cave.

Though it would need some adaptation to work with JME3.

But if you are eager to step into this, you would need to generate a procedural terrain first with JME3, which is far from trivial.

You can start by reading the article on GPU gem about this (if you didn’t yet).

But be aware that this is a very advanced topic.

I did read the article, and it did feel above my level at this point for sure. But definitely within my grasp with time. And I was referring to the article and the terrain generation described there, not Mazander’s. Sorry for confusion.

If someone wants use or just test this, the source code of the project is now hosted on Google.

I added one y-axis scaled noise which produced nice stalactites:


That’s a great contribution to the community! Kudos :slight_smile:

I totally agree to what Skye said! Fantastic, that will make porting it to jme3 easier… Anyone? :smiley:



A few issues to sort out still.

  1. Texture stretching. I need to properly calculate the tex coords from the view projection. I’ve realized it has to do with trigonometry. Simply using pythagoras theorem on the “v” coord produces a radial shape in the texture.
  2. Normal maps. Ran into some problems generating TangentBinormals. It seems no triangles are found/generated for the VertexData. I haven’t worked with that before so i’m not sure how to solve it.

    The reason for this is it’s now using the Lighting material. Previously everything was handled by the shader (and i didn’t get that to work)

    Now i need to get some fresh air. Hope to complete this later :slight_smile:

    Edit: issue clarification. If anyone has any pointers to give regarding the issues, feel free to mention them.

Good work Rickard!

Whoa! Awesome! U rock!

Consider me psyched!

normen said:(...) U rock!

I don't think his pun was intended, so I'll claim it: You rock!

So did you base this on @mazander's Cave3D? If so, then I think ideally this would be just another branch in that project, so that everything is kept in one place, and mazander might catch on the the jME3 branch eventually :)

Yes, it’s mazander’s code, modified and adapted for jme3.

I think i’ve sorted the issues now:

I still have a few things i’d like to look at before submitting the code. Hopefully i’ll be able to document the changes a bit tomorrow.

The reason behind all this is i happened to see this thread the other day ago and thought it was too awesome not to be for jme3, and that a conversion couldn’t be that hard (given how easy it was to convert the Geometry Batch classes).

I had planned for 2-3 hours, and it’s taken about 7-8 hours so far.

Here’s the source plus some documentation about the changes.

There are two issues that i know about:

  1. Texturing. I haven’t been able to find the correct algorithm for calculating the texture coords, But the current one works “ok”, but not always.
  2. The mesh generator freaks out after you’ve moved a while. I don’t know why, but it’s likely it’s either due to the stack used to populate the node (my addition to get around concurrency problems), or something related to the bounds detection.

    Hopefully someone else will be able to solve those, or i will if i have the time soon.

    Then to solve the previous problem with the tangent calculation, i had to modify the TangentBinormalGenerator class slightly (also included) to check if the VertexData has any triangles, or exit otherwise. I think it’s the polygonisator that sometimes generates funky meshes. This wasn’t a problem in jme2 since it didn’t use the TangentBinormalGenerator.
Removal of jme2 material code. Cave now uses the Lighting material.
The generator thread now populates a stack with "cave mesh instances" that will be added to the node in simpleUpdate(). This to get around the concurrency problem that appears when another thread modifies a node.
Modified for jme3.
No modifications.
Changed to inherit from Geometry instead of TriMesh
Added texture coord generation.
Added check to exit generation if VertexData has no triangles.

Source can be downloaded here, for now.