Voxel Terrain System

Any progress on this project?

Should be a demo up in a day or two. Last two weeks was the void of final week :facepalm: .

I feel your pain… :smiley:

Did you manage to make basic CMS work? From that point on if you don’t mind sharing the code I could step in and start working on octrees, physics, procedural generation, mesh voxelizer, etc.

@Neomex this week I sorta got side tracked into rendering which is something I intended to work on a bit later in the summer.

This weekend I will make a post on isosurface extraction as well a technical blog post about them. By next week I’ll hopefully have some serious work done on CMS or some other technique ^.^

The review of this week:

  • Added bump and specular mapping to the MC demo.
  • Added parameterization of voxel materials

The latter is where a lot of complications arose.

But before I discuss lets drop an image:

The blending of the shininess makes for a really harsh boundary between the lava and the other materials.

So there’s a big issue of how to cram all these material parameters into the shader and still have them run at a decent rate. @Momoko_Fan suggests batching the same materials together but this has a lot of tricky implications. Right now when you edit a chunk (16x16x16 area) the whole chunk gets reconstructed (by marching cubes) and a new mesh gets generated. To group by material would require a more complex system as far as I can see.
Here are just a few properties that each material in the demo has:

My current method for getting material parameters to the shaders as @Momoko_Fan and @nehon warn isn’t very scalable. So I’ll have to try something new.

Here is a low quality video:

Here is the image at the end of the video:

@pspeed I also added support for the whole multi-scale thing or at least i implmented the version talked about in http://udn.epicgames.com/Three/TerrainAdvancedTextures.html

Its definitely not a good example of when to use, would have actually looked good on grass or something of that nature. I think the scalers 4x and 16x

So that’s going to be it for marching cubes. Still have a bit of research left on the whole isosurface extraction.

I’ll post the demo in a little while…

1 Like
@FuzzyMonkey said: Its definitely not a good example of when to use, would have actually looked good on grass or something of that nature. I think the scalers 4x and 16x

For repeating textures like that, try adding a simple grey scale noise texture and play around with the scale. It doesn’t even have to be very high res.

I’m looking forward to your demo.
For your mipmapping/atlas problem a look into array textures might be worth it. As far as I understand this was designed to solve the problem you are facing. (Unfortunately there is a bug with array textures in the current Version but a fix can be found here: http://hub.jmonkeyengine.org/forum/topic/fix-mipmapped-texture-arrays-and-3d-textures/)

@Perjin Yeah i switched to texture arrays sometime last week, yeah make life so much easier ^.^
However this also means there wont be support for any form of mobile or other non-opengl 3.0 capable devices.
I’ll pack the demo later today, sorry for the delay.

Fuzzy, checkout this if you haven’t already:


I was planning to stick with this one untill you have showed up. :wink:

Alright here’s the demo:

just unpack the zip and double click on the jar

wasd - standard movement
Up arrow - changes selected material
Left Mouse - Add Terrain
Right Mouse - Remove Terrain
Shift - Increase movespeed

I’m assuming the shaders won’t compile on nvidia graphics cards so just post the compile errors and I can try to fix them. At the moment I have the shaders set to require OpenGL 3.2

@NeoMex Thanks I’ll be sure to take a look ^.^

@FuzzyMonkey said: I'm assuming the shaders won't compile on nvidia graphics cards so just post the compile errors and I can try to fix them.

…this made me chuckle a little bit. nVidia being the most lenient and robust of all shader compilers in the known universe. :slight_smile:

@pspeed Oh, I thought it was the other way around :facepalm:
I know it compiled on both my laptop and desktop and they both run ati.

My error when I try and run the project (yeah wow I’m running nVidia XD)

Well, the texture array thing is the only error… so the others were probably hidden on the ATI running systems.

Still strange, though.

Here’s the Frag Shader code. But I feel like that error doesn’t actually indicate a problem with the shader its self
uniform sampler2DArray m_materialColors;
uniform sampler2DArray m_materialSpecular;
uniform sampler2DArray m_materialNormal;
uniform sampler2D m_noise;

varying vec4 vertex;
varying vec2 texCoord;
varying vec3 vertexWieght;
varying vec3 vType;

//Rendering Effects:
varying vec3 baseScalers;
varying float shiny;
varying vec3 rotSpeed;

varying vec3 otherS;
varying vec3 desV;
varying vec3 hlV;

varying float mP; //minnaert parameter
varying float mL; //minnaert limit

varying vec3 AmbientSum;
varying vec4 DiffuseSum;
varying vec3 SpecularSum;

//Pixel Lighting:
uniform vec4 g_LightDirection;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec3 lightVec;

float tangDot(in vec3 v1, in vec3 v2){
float d = dot(v1,v2);
//#ifdef V_TANGENT
// d = 1.0 - d*d;
// return step(0.0, d) * sqrt(d);
return d;

float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
float ld = max(0.0, dot(norm, lightdir));
if(mP > 0.0){
float NdotL = max(0.0, dot(norm, lightdir));
float NdotV = max(0.0, dot(norm, viewdir));
ld = NdotL * min(pow(max(NdotL * NdotV*mP,.1), -1.0),mL);
return ld;


float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shine){
// Standard Phong
vec3 R = reflect(-lightdir, norm);
return pow(max(tangDot(R, viewdir), 0.0), shine);


vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir,float s){
float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, s);

// float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0);
// #else
float att = vLightDir.w;
// #endif

if (shiny <= 1.0) {
specularFactor = 0.0; // should be one instruction on most cards …

specularFactor *= diffuseFactor;

return vec2(diffuseFactor, specularFactor) ;

vec3 desaturate(in vec3 color,in float amount)
vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), color));
//vec3 gray = vec3(dot(vec3(0.3, 0.59, 0.11), color));
return vec3(mix(color, gray, amount));

vec2 rotate(in vec2 coords,in float rotation){
float c = cos(rotation);
float s = sin(rotation);
mat2 r = mat2(c,-s,s,c);
vec2 rotated = coords - .5f;
rotated = r*rotated;
return rotated;

vec2 rotateCoords(in vec2 coords,in float rotSpeed){
float r = int((coords.x+coords.y)/rotSpeed)%2;
float angle = 1.570795f * r;
return rotate(coords,angle);

//vec4 getRotatedSample(vec2 coord, float index){
// vec4 sample = texture2DArray(m_materialColors,vec3(coord,index));
// vec4 rotatedSample = texture2DArray(m_materialColors,vec3(rotate(coord,1.570795f),index));
// float f = texture2D(m_noise,coord*(1.0/64.0)).g;
// return mix(sample,rotatedSample,f);

//Multi-Scale Sampling
vec4 getColor(vec2 coord, float sc1, float sc2, float hl, float desat,float index){
vec3 scaler1 = texture2DArray(m_materialColors, vec3(texCoordsc1,index)).xyz;
if(sc2 > 0 ){
vec3 scaler1Desat = desaturate(scaler1
vec3 scaler2 = texture2DArray(m_materialColors, vec3(texCoordsc2,index)).xyzhl;
scaler1 = scaler1Desat*scaler2;
return vec4(scaler1,1.0);

void main()
//Now that we’re in frag shader our type wieghts are “averaged”
//Now we normalize them:
vec3 weights =abs( vertexWieght / (vertexWieght.x + vertexWieght.y + vertexWieght.z));

vec3 indexes = vType;
//vec2 rot = rotate(texCoord,1.570795f);

///////////////////////////Type 1////////////////////////////////////////////
//Texture 1:
vec2 coord1 = texCoord;
if(rotSpeed.x &gt; 0){
	coord1 = rotateCoords(texCoord,rotSpeed.x);
vec4 tex1 = getColor(coord1,baseScalers.x,otherS.x,hlV.x,desV.x,vType.x);
vec3 coord1T = vec3(coord1*baseScalers.x,vType.x);
vec4 n1 = texture2DArray(m_materialNormal, coord1T);
vec4 s1 = texture2DArray(m_materialSpecular,coord1T);

///////////////////////////Type 2////////////////////////////////////////////
//Texture 2:
 vec2 coord2 = texCoord;
if(rotSpeed.y &gt; 0){
	coord2 = rotateCoords(texCoord,rotSpeed.y);
vec4 tex2 = getColor(coord2,baseScalers.y,otherS.y,hlV.y,desV.y,vType.y);
vec3 coord2T = vec3(coord2*baseScalers.y,vType.y);
vec4 n2 = texture2DArray(m_materialNormal, coord2T);
vec4 s2 = texture2DArray(m_materialSpecular,coord2T);

///////////////////////////Type 3////////////////////////////////////////////
//Texture 3:
vec2 coord3 = texCoord;
if(rotSpeed.z &gt; 0){
	coord3 = rotateCoords(texCoord,rotSpeed.z);
vec4 tex3 = getColor(coord3,baseScalers.z,otherS.z,hlV.z,desV.z,vType.z);
vec3 coord3T = vec3(coord3*baseScalers.z,vType.z);
vec4 n3 = texture2DArray(m_materialNormal, coord3T);
vec4 s3 = texture2DArray(m_materialSpecular,coord3T);

vec4 finalColor = (tex1 * weights.x) + (tex2 * weights.y) + (tex3 * weights.z);
vec4 finalNormal = (n1 * weights.x) + (n2 * weights.y) + (n3 * weights.z);
vec4 finalSpecular = (s1 * weights.x) + (s2 * weights.y) + (s3 * weights.z);

//Spot Fall Off 
float spotFallOff = 1.0;

if(g_LightDirection.w != 0.0){
vec3 L = normalize(lightVec.xyz);
vec3 spotdir = normalize(g_LightDirection.xyz);
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = floor(g_LightDirection.w) * 0.001;
float outerAngleCos = fract(g_LightDirection.w);
float innerMinusOuter = innerAngleCos - outerAngleCos;
spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
if(spotFallOff <= 0.0){
gl_FragColor.rgb = AmbientSum * finalColor.rgb;
gl_FragColor.a = 1.0;
spotFallOff = clamp(spotFallOff, 0.0, 1.0);

//From lighting.frag:
vec3 normal = normalize((finalNormal.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0)));

//Final Approach ^.^
vec4 lightDir = vLightDir;
lightDir.xyz = normalize(lightDir.xyz);
vec3 viewDir = normalize(vViewDir);

vec2 light;
if(shiny &gt;=255){
	light = computeLighting(normal, viewDir, lightDir.xyz,finalSpecular.r * shiny) * spotFallOff;
	gl_FragColor.rgb =  AmbientSum * finalColor.rgb  +
                    DiffuseSum.rgb   * finalColor.rgb  * vec3(light.x) +
	light = computeLighting(normal, viewDir, lightDir.xyz,shiny) * spotFallOff;
	gl_FragColor.rgb =  AmbientSum * finalColor.rgb  +
                    DiffuseSum.rgb   * finalColor.rgb  * vec3(light.x) +


// finalColor.rgb*NdotL +
//gl_FragColor.rgb =  finalColor.rgb*NdotL;//vec4(offsets[0].y,0,0,1);
gl_FragColor.a = 1.0;

maybe @pspeed or @nehon has an idea?

well the compiler is not kidding
You need to put #extension GL_EXT_texture_array : enabled at the beginning of your frag shader to use texture2Darray.
If your version is 1.3 or higher you have to use the texture() function instead and you won’t have to declare the extension.

How do you declare your shader in the j3md?

I tried to run the demo in a Intel GPU (I have switchable graphics [Intel and ATI]):

Uncaught exception thrown in Thread[LW]GL Renderer Thread,5,main]

UnsupportedOperationException: No default technique on material ‘Simple’ is supported by the video hardware. The caps [GLSL150] are required.


1 Like
@nehon said: How do you declare your shader in the j3md?

Here’s the j3md:
MaterialDef Simple {
//This is the complete list of user defined uniforms to be used in the
MaterialParameters {
//Int voxelTypes
//IntArray vTD;
//IntArray vTN;
//IntArray vTS;
FloatArray bS //base Scale
FloatArray shine

    //Multi-Scale Sampling
    FloatArray oS //Other scalar
    FloatArray hl //Highlight value
    FloatArray des //Desaturation value
    //Rotation Technique
    FloatArray rot // Rotation speed
    FloatArray minnaertP //minnaertParameter
    FloatArray minnaertL //hasMinnaert
    //Float voxelTypeToColorMixing[voxelTypes]
    // FloatArray typeShininess
    //IntArray useMinnaert
    //Color materialColors[voxelTypes]
    TextureArray materialColors
    TextureArray materialNormal
    TextureArray materialSpecular
    Texture2D noise
    //Boolean useNormalMap

Technique {
	LightMode MultiPass
    VertexShader GLSL150:   Shaders/multimaterial.vert
    FragmentShader GLSL150: Shaders/multimaterial.frag
    WorldParameters {

Thanks for the debugging help ^.^

ok so you set the version to 1.5 with those lines, so a #version 150 will be added to your shaders before they are sent to be compiled (that’s why you have the warnings about varyings and thats why you have the error about the texture2DArray since you should use the texture() function)

You have 2 solutions

  1. keep this technique as is and implement a proper 1.5 shader (“in” instead of “attribute”, “varying” declaring out variables and use texture()). then make another technique but for version 110 and implement a 1.1 shader (pretty much the same as the one you have now but with the declared extension to be able to use texture2DArray)

  2. just implement the 1.1 version.

Also read this http://www.opengl.org/wiki/Array_Texture

same error here as nomnom’s