on webgl, Writings, Tutorials, ThreeJS, Code

Coding an infinite 3D world with three.js [Part 1]

Intro

This tutorial will show you how to create an infinite terrain that can be used in combination with a Chase Camera. If you try the Chase Camera demo, the concept created in this tutorial is the creation of new terrain when the object reaches the edge of the existing terrain.

Outcome

The entire tutorial will creatie a 9 x 9 grid, keeping the camera in the middle. In this part, we'll just be creating the first column made up of three tiles.

  • Step 1 will create those three tiles
  • Step 2 will allow us to move forward, adding ground in front of us
  • Step 3 will allow us to move backwards, adding ground in the opposite direction.

This will put is in a good position for Part 2, where we'll replicate this column twice more to create the full grid, ending in the final infinite world.


Step 1:  Start off with three tiles

First, create three tiles with the camera positioned in the middle:

As seen in the video, we're creating three tiles of terrain in a row (to create the initial terrain, look here), using tiles of 100 x 100 for simplicity. This is all done inside the createTerrainMatrix function. Look at the for loop from within the main block of code:

for ( var z= 100; z > -200; z-=100 )

Consider that our camera is intially positioned at z=5, and our 3 tiles are 100 x 100. With this in mind, we'll place the first tile behind the camera at z=100, then the second one will be 100 in front (the height of the tile), and the third further away from the camera at z = -200. That's all created with the for loop. Apart from that, our code is pretty much the same as the original addGround() function from the previous tutorial.

We can create an initial for loop starting at 100, moving to -200.  

    /**  
     * createTerrainMatrix
     * @TODO: create the matrix of terrains - need to add 9 bits of terrain
     */
    createTerrainMatrix:function(scene, perlinNoise){

        //every 100px on the z axis, add a bit of ground
        for ( var z= 100; z > -200; z-=100 ) {

          //Create the perlin noise for the surface of the ground
            var perlinSurface = new PerlinSurface(perlinNoise, 100, 100);
          var ground = perlinSurface.surface;
          //rotate 90 degrees around the xaxis so we can see the terrain
          ground.rotation.x = -Math.PI/-2;
          // Then set the z position to where it is in the loop (distance of camera)
          ground.position.z = z;
          ground.position.y -=4;

          //add the ground to the scene
          scene.add(ground);
          //finally push it to the floor array
          this.floor.push(ground);
        }

    }

Overall, this will create three pieces of terrain, and in the final line, push them to a floor array, which will be used in the next step.


Step 2: Moving forwards

When the camera moves forwards, move the tile from the back to the very front, keeping the camera on the centre tile.

We can create this effect in a new function, moveWithCamera():

    /**  
     * moveWithCamera
     * when the camera gets past the first terrain, put the other in front of it
     */
     moveWithCamera(camera){
        // loop through each of the 3 floors
        for(var i=0; i<this.floor.length; i++) {

          //if the camera has moved past the entire square, move the square
          if((this.floor[i].position.z - 100)>camera.position.z){

            this.floor[i].position.z-=200;
          }

        }

This is fairly simple - it's a modification of Step 3 in the previous animating terrain tutorial, although we've put it into it's own function. The key part is again another for loop.

This time we're looping through the array of tiles we created at the end of step 1, and checking if the camera has moved past the square by comparing z positions. If the camera's z position has a greater negative value than the tile, we move that tile forward (into the screen, and away from the camera) two times it's height so that it goes from the very back to the front. 


Step 3: Moving backwards

The same way we move forward can be applied in reverse to allow us to move backwards. To do this, update moveWithCamera() to include the following code, just underneath the first z position comparison where we were moving the tiles forward:

//if the camera has moved past the entire square in the opposite direction, move the square the opposite way  
          else if((this.floor[i].position.z + this.tileHeight)<camera.position.z){

            this.floor[i].position.z+=(this.tileHeight*2);
          }

Well done

At this point you should have three tiles of terrain that you can move forwards and backwards along, but not left and right. Part 2 will look at adding two more columns, and implementing the ability to move across the x axis infinitely, the same way we have done here with the z axis.