DevLog #003 – Rendering Chunks

Part of a series of posts in which we write a voxel engine from scratch for Android.

So we’ve talked about why chunks are important in #001, and we made blocks as efficient as possible in #002 using Draw-Call Batching and Texture Atlasing. Now we can start building a chunk, and filling it with blocks. You might remember that we’re storing blocks inside chunks using a Lookup-Table that simply contains 4096 short ints describing each block-type, and Blocklists, which store the x,y,z position of visible blocks so they can be batched into a 3D model.

Let’s assume we already have 1 Chunk in the world, and throw down some random blocks by filling the first 256 (16×16 blocks) elements of our Lookup-Table with random numbers, and then building a 3D model from that data…

Success! We have the first layer of blocks in our chunk, which actually contains 16×16x16 blocks, or 4096 total – so we can add the rest by filling the rest of the Lookup-Table, and rebuilding the model. So the steps for building a chunk are..

  1. Fill the Lookup-Table with values (-1 = empty space, 0=dirt etc..)
  2. Calculate which blocks (and block faces) are visible by checking neighboring blocks
  3. Build a temporary Blocklist of visible blocks with their x,y,z positions
  4. Build a batched mesh and discard the Blocklist from memory (but we keep the Lookup-Table)

This time we’ve used a single block-type for each layer (for illustrative purposes) and filled the entire chunk with blocks. It still doesn’t look very exciting, and nothing like terrain so far, but we’ll be adding proper terrain later.

Something else is missing…

Lighting

Because this is for mobile we’re going to use the simplest lighting model possible, namely directional vertex lighting with some linear fog. We’ll add more complex lighting much later, but for now, just a very basic shader will do the trick..

//Vertex Shader
precision highp float;
uniform mat4 u_MVPMatrix;
uniform mat4 u_MVMatrix;
uniform lowp vec4 u_lightAmbient;       //Ambient Light
uniform vec3 u_lightDir;		//Directional Light Direction
uniform lowp vec4 u_lightDirDiffuse;	//Directional Light Color
uniform lowp float u_fogDist;
attribute vec4 a_vPosition;
attribute mediump vec2 a_texCoord;
attribute vec3 a_vNormal;
varying mediump vec2 v_texCoord;
varying lowp vec4 v_light;
varying lowp float v_fogPower;
void main() {
  vec4 mcPosition = u_MVPMatrix * a_vPosition;
  vec3 mcNormal = a_vNormal;
  vec3 ecNormal = vec3(u_MVMatrix * vec4(mcNormal, 0.0));
  float ecNormalDotLightDirection = max(0.0, dot(ecNormal, u_lightDir));	//Directional Light
  lowp vec4 dirLight = ecNormalDotLightDirection * u_lightDirDiffuse;		//Directional Light
  v_light = u_lightAmbient + dirLight;
  v_texCoord = a_texCoord;
  v_fogPower = clamp((mcPosition.z / u_fogDist),0.0,1.0);
  gl_Position = mcPosition;
}
//Fragment Shader
precision mediump float;
uniform lowp sampler2D u_texture;
uniform lowp vec4 u_fogColor;
uniform lowp vec4 u_vColor;
varying mediump vec2 v_texCoord;
varying lowp vec4 v_light;
varying lowp float v_fogPower;
void main() {  
  lowp vec4 ftex = texture2D(u_texture, v_texCoord) * u_vColor * v_light;
  lowp vec4 fcol = mix(ftex,u_fogColor,v_fogPower);
  gl_FragColor=fcol;
}

Voila! With some empty spaces to show off the lighting better, we now have something that looks a little more solid – but we can do much better than this. We can use the Lookup-Table to calculate some per-vertex (cheap & cheerful) Ambient Occlusion, which will cause neighboring blocks to cast some subtle shadows on each-other, and we can do this virtually for free by reducing the vertex normals toward zero for any occluded edges.

Since the lighting calculation uses the dot-product of the light/normal vectors, as long as we don’t go past zero (which would invert the normal direction), we can reduce the light each vertex receives to produce fake Ambient Occlusion without any cost to the GPU.

The effect is subtle here, but will make more sense with less randomness when we get to making actual terrain. You should notice a gradient in the lighting towards neighboring blocks, which should add some fidelity to our world later on.

Share this