Procedural Voxel Terrain

1 April, 2014

Main Image

This application was written in DirectX 11 using the deferred renderer created previously. The terrain is generated using 3D Simplex noise, rendered using marching cubes and textured using trilinear texturing.

Simplex noise

//returns the density at a certain point in 3D space
float Terrain::GetDensityAt(float x, float y, float z)
{
    const auto density = 0.0f;
 
    //use simplex noise with the octave settings and //return density
    const auto octaves = 11;

    //y / 8 is subtracted to avoid sharp curves
    density = octave_noise_3d(octaves, persistance, frequency, x, y, z) - (y / 8);

    return density;
}

Marching Cubes

The marching cubes algorithm is used to render the geometry using the density calculated via simplex noise. The following is code to calculate the vertices of a single voxel

//process current voxel
void Terrain::CalculateVoxel(float x, float y,float z)
{
 const auto voxelSize = SIZE_BLOCK / NUM_VOXELS;
 float density[8]; //density at 8 corners of the voxel

 density[0] = GetDensityAt(x, y, z);
 density[1] = GetDensityAt(x, y + voxelSize, z);
 density[2] = GetDensityAt(x + voxelSize, y + voxelSize, z);
 density[3] = GetDensityAt(x + voxelSize, y, z);
 density[4] = GetDensityAt(x, y, z + voxelSize);
 density[5] = GetDensityAt(x, y + voxelSize, z + voxelSize);
 density[6] = GetDensityAt(x + voxelSize, y + voxelSize, z + voxelSize);
 density[7] = GetDensityAt(x + voxelSize, y, z + voxelSize);

 //density value converted to 0 or 1 then set to //correct bit
 int densityIndex[8];

 for(int i = 0; i < 8; ++i)
 {
   if(density[i] < 0)
   {
     densityIndex[i] = 1 << i;
   }
   else
   {
     densityIndex[i] = 0;
   }
 }

 //combine all the density indices to get index for  
 //the voxel
 int value = densityIndex[7] | densityIndex[6] | densityIndex[5] |
    densityIndex[4] | densityIndex[3] | densityIndex[2] | densityIndex[1]
    | densityIndex[0];

 //if the voxel is invalid
 if (value == 0 || value == 255)
   return;

 if (value < 0 || value > 255)
   return;

 //get the number of polygons in the voxel from the
 //lookUp table
 int num = numPoly[value];
 
 CreateVertices(x, y, z, density, value, num);

}

Trilinear Texturing

Lastly, the terrain is textured using a single texture.The following is code used in the pixel shader:

//DiffuseMap & PositionBuffer are Texture2D
//ScreenPos = SV_Position
//HLSL
 int3 pos = int3(input.ScreenPos.xyz);

 float4 sampleX = DiffuseMap.Sample(AnisoSampler,      
    PositionBuffer.Load(pos).yz);

 float4 sampleY = DiffuseMap.Sample(AnisoSampler, 
    PositionBuffer.Load(pos).xz);

 float4 sampleZ = DiffuseMap.Sample(AnisoSampler, 
    PositionBuffer.Load(pos).xy);

float4 finalColour = sampleX * lighting.x + sampleY * lighting.y + sampleZ * lighting.z;