In recent days, I drew a 3D house with three. js, and put a bed in it. The movement can be controlled by mouse and keyboard, giving it an instant visual feeling of 3D game.

This article will talk about the implementation principle.

Code address: github.com/QuarkGluonP…

Thought analysis

Let’s not write the code, but let’s analyze the idea.

Such a house is actually made up of several geometries:

Here are some geometers:

The floor is a plane, which can be drawn using PlaneGeometry and labeled with a texture map.

The two sides of the wall are an irregular shape, which can be ExtrudeGeometry, which supports drawing a 2D path with a brush and then thickening it into 3D.

Similarly, the back wall is easy to draw either BoxGeometry or ExtrudeGeometry in a shape and then 3D.

The front wall is a little more complicated, it is also irregular, which can be drawn using The ExtrudeGeometry to shape, and then become 3D, but it has two more holes, which need to be drawn and added to the shape.

Door frame, window frame is also the shape of a hole, with ExtrudeGeometry into 3D.

What about the roof? The roof is nothing special, but the cube rotates at a certain Angle, using BoxGeometry (cube) can be drawn.

Next, put different pictures on the walls, roof and floor, and set up different positions to assemble a house.

What about the bed?

Three.js provides a lot of geometry, which can draw some simple objects, but it is difficult to draw complex objects. Such objects are generally drawn with professional 3D modeling software, and exported files in FPX or OBJ format are loaded and rendered by Three.js.

We looked for a 3D model of the bed on the Internet. I found one in FBX format and loaded it with the FBXLoader of Three.js.

There is still a meadow left, which is also a plane, which is drawn by PlaneGeometry, but with a larger length and width, and no end in sight.

Looks like fog?

“Fog” is set. Three. Js sets the Fog effect in the scene, specifying the color and the distance of the Fog. To create a sense of ambiguity, I added fog to the scene.

Once all objects are drawn, you can then roam the 3D scene, using the mouse and keyboard to change direction and move backwards, forwards, left or right. This interaction is implemented using FirstPersonControls.

The one we usually use is OrbitsControls, which allows you to rotate the camera around an object like a satellite. But we don’t want to go around, we want to go back and forth and left and right with the keyboard and mouse.

Let’s briefly summarize:

Three. Js is to add various objects in the three-dimensional coordinate system to assemble different 3D scenes. Simple objects can be drawn, while complex objects can be drawn with modeling software and then loaded into the scene. We can use different controllers to control camera movement and achieve different interactive effects, such as track controller, first-person controller, etc.

BoxGeometry and ExtrudeGeometry can be used to plot the walls, floors, and roofs of houses, but not beds, which are complicated and load model files directly.

The interaction is controlled through FistPersonControls (first person Controller) to achieve the feel of a 3D game.

With that in mind, let’s write down the code:

Code implementation

Start by drawing the grass, i.e. a large flat surface, with the grass map.

The three-dimensional Mesh is composed of Geometry and Material. We create the PlaneGeometry, set the length and width to a large value, such as 10000, and then load the image of the grass as the Texture to form the Texture. Then you can create the grass.


function createGrass() {
    const geometry = new THREE.PlaneGeometry( 10000.10000);

    const texture = new THREE.TextureLoader().load('img/grass.jpg');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 100.100 );

    const grassMaterial = new THREE.MeshBasicMaterial({map: texture});

    const grass = new THREE.Mesh( geometry, grassMaterial );

    grass.rotation.x = -0.5 * Math.PI;

    scene.add( grass );
}
Copy the code

The texture map should be set to repeat in both directions 100 times.

Then the plane of the grass has to be rotated.

Add a little fog to blur the sky:

scene.fog = new THREE.Fog(0xffffff.10.1500);
Copy the code

The color is white, and the fog range is 10 to 1500.

The next step is to create the house. The house consists of the floor, the walls on both sides, the front wall, the back wall, the door frame and the window frame, the roof, and the bed. To create each part separately, we put them into separate groups.


const house = new THREE.Group();

function createHouse() {
    createFloor();

    const sideWall = createSideWall();
    const sideWall2 = createSideWall();
    sideWall2.position.z = 300;

    createFrontWall();
    createBackWall();

    const roof = createRoof();
    const roof2 = createRoof();
    roof2.rotation.x = Math.PI / 2;
    roof2.rotation.y = Math.PI / 4 * 0.6;
    roof2.position.y = 130;
    roof2.position.x = -50;
    roof2.position.z = 155;

    createWindow();
    createDoor();

    createBed();
}
Copy the code

Create the floor is also PlaneGeometry. Just attach the wood diagram and set the position:

function createFloor() {
    const geometry = new THREE.PlaneGeometry( 200.300);

    const texture = new THREE.TextureLoader().load('img/wood.jpg');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 2.2 );

    const material = new THREE.MeshBasicMaterial({map: texture});
    
    const floor = new THREE.Mesh( geometry, material );

    floor.rotation.x = -0.5 * Math.PI;
    floor.position.y = 1;
    floor.position.z = 150;

    house.add(floor);
}
Copy the code

To create the side walls, use ExtrudeGeometry, that is, first draw a 2D shape and then extrude it into 3D. Also paste the wall texture map.

function createSideWall() {
    const shape = new THREE.Shape();
    shape.moveTo(-100.0);
    shape.lineTo(100.0);
    shape.lineTo(100.100);
    shape.lineTo(0.150);
    shape.lineTo(-100.100);
    shape.lineTo(-100.0);

    const extrudeGeometry = new THREE.ExtrudeGeometry( shape );

    const texture = new THREE.TextureLoader().load('./img/wall.jpg');
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 0.01.0.005 );

    var material = new THREE.MeshBasicMaterial( {map: texture} );

    const sideWall = new THREE.Mesh( extrudeGeometry, material ) ;

    house.add(sideWall);

    return sideWall;
}
Copy the code

The two side walls are only in different positions. Just modify the z-axis position:

const sideWall = createSideWall();
const sideWall2 = createSideWall();
sideWall2.position.z = 300;
Copy the code

Oh, and if in doubt, you can add a coordinate assist (AxisHelper) to your scene.

const axisHelper = new THREE.AxisHelper(2000);
scene.add(axisHelper);
Copy the code

Then the back wall, which is a simpler shape, is a rectangle:

function createBackWall() { const shape = new THREE.Shape(); LineTo (150, 0) shape.lineTo(150,100) shape.lineTo(-150,100) shape.lineTo(-150,100); const extrudeGeometry = new THREE.ExtrudeGeometry( shape ) const texture = new THREE.TextureLoader().load('./img/wall.jpg'); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; Texture. Repeat. Set (0.01, 0.005); const material = new THREE.MeshBasicMaterial({map: texture}); const backWall = new THREE.Mesh( extrudeGeometry, material) ; backWall.position.z = 150; backWall.position.x = -100; Rotation. Y = math.pi * 0.5; house.add(backWall); }Copy the code

Next comes the front wall, which, in addition to drawing shapes, also has to cut out two holes:

function createFrontWall() {
    const shape = new THREE.Shape();
    shape.moveTo(-150.0);
    shape.lineTo(150.0);
    shape.lineTo(150.100);
    shape.lineTo(-150.100);
    shape.lineTo(-150.0);

    const window = new THREE.Path();
    window.moveTo(30.30)
    window.lineTo(80.30)
    window.lineTo(80.80)
    window.lineTo(30.80);
    window.lineTo(30.30);
    shape.holes.push(window);

    const door = new THREE.Path();
    door.moveTo(-30.0)
    door.lineTo(-30.80)
    door.lineTo(-80.80)
    door.lineTo(-80.0);
    door.lineTo(-30.0);
    shape.holes.push(door);

    const extrudeGeometry = new THREE.ExtrudeGeometry( shape ) 

    const texture = new THREE.TextureLoader().load('./img/wall.jpg');
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 0.01.0.005 );

    const material = new THREE.MeshBasicMaterial({map: texture} );

    const frontWall = new THREE.Mesh( extrudeGeometry, material ) ;

    frontWall.position.z = 150;
    frontWall.position.x = 100;
    frontWall.rotation.y = Math.PI * 0.5;

    house.add(frontWall);
}
Copy the code

There are only two more holes in the shape, making it more complicated to draw, and the rest of the textures, materials, and positions are set in the same way.

Draw a shape for the door and window, cut a hole, and then add some thickness to make it 3D:

function createWindow() {
    const shape = new THREE.Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, 50)
    shape.lineTo(50,50)
    shape.lineTo(50,0);
    shape.lineTo(0, 0);

    const hole = new THREE.Path();
    hole.moveTo(5,5)
    hole.lineTo(5, 45)
    hole.lineTo(45, 45)
    hole.lineTo(45, 5);
    hole.lineTo(5, 5);
    shape.holes.push(hole);

    const extrudeGeometry = new THREE.ExtrudeGeometry(shape);

    var extrudeMaterial = new THREE.MeshBasicMaterial({ color: 'silver' });

    var window = new THREE.Mesh( extrudeGeometry, extrudeMaterial ) ;
    window.rotation.y = Math.PI / 2;
    window.position.y = 30;
    window.position.x = 100;
    window.position.z = 120;

    house.add(window);

    return window;
}
Copy the code

Color set to silver white.

The door frame is the same:

function createDoor() { const shape = new THREE.Shape(); shape.moveTo(0, 0); shape.lineTo(0, 80); Shape. The lineTo (50 reached); Shape. The lineTo (50, 0); shape.lineTo(0, 0); const hole = new THREE.Path(); Hole. MoveTo (5, 5); hole.lineTo(5, 75); hole.lineTo(45, 75); hole.lineTo(45, 5); hole.lineTo(5, 5); shape.holes.push(hole); const extrudeGeometry = new THREE.ExtrudeGeometry( shape ); const material = new THREE.MeshBasicMaterial( { color: 'silver' } ); const door = new THREE.Mesh( extrudeGeometry, material ) ; door.rotation.y = Math.PI / 2; door.position.y = 0; door.position.x = 100; door.position.z = 230; house.add(door); }Copy the code

Next comes the roof, which is the BoxGeometry of two cubes, for a rotation:

const roof = createRoof();

const roof2 = createRoof();
roof2.rotation.x = Math.PI / 2;
roof2.rotation.y = Math.PI / 4 * 0.6;
roof2.position.y = 130;
roof2.position.x = -50;
roof2.position.z = 155;
Copy the code

The six sides of the roof are made of different materials. One side is tile map, and the other side is gray to simulate the effect of cement. The tile texture should be rotated to set the number of repetitions in the next two directions.

function createRoof() {
    const geometry = new THREE.BoxGeometry( 120.320.10 );

    const texture = new THREE.TextureLoader().load('./img/tile.jpg');
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set( 5.1);
    texture.rotation = Math.PI / 2;
    const textureMaterial = new THREE.MeshBasicMaterial({ map: texture});

    const colorMaterial = new THREE.MeshBasicMaterial({ color: 'grey' });

    const materials = [
        colorMaterial,
        colorMaterial,
        colorMaterial,
        colorMaterial,
        colorMaterial,
        textureMaterial
    ];

    const roof = new THREE.Mesh( geometry, materials );

    house.add(roof);

    roof.rotation.x = Math.PI / 2;
    roof.rotation.y = - Math.PI / 4 * 0.6;
    roof.position.y = 130;
    roof.position.x = 50;
    roof.position.z = 155;

    return roof;
}
Copy the code

Then the bed is simple, because you don’t have to draw your own, directly load an existing model on the line, this complex model is usually drawn by professional modeling software.

function createBed() {
    var loader = new THREE.FBXLoader();
    loader.load('./obj/bed.fbx'.function ( object ) {
        object.position.x = 40;
        object.position.z = 80;
        object.position.y = 20; house.add( object ); }); }Copy the code

And the lighting is set to ambient light, which means the light intensity is the same in every direction.

const light = new THREE.AmbientLight(0xCCCCCC);
scene.add(light);
Copy the code

To create a camera, use a perspective camera, that is, a perspective that is near, large and far, small:

const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1.1000);
Copy the code

Specify viewing Angle of 60 degrees, aspect ratio, range of 0.1 to 1000.

Create a renderer and render it frame by frame in requestAnimationFrame:

const renderer = new THREE.WebGLRenderer();
function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render)
}
Copy the code

The next step is to support roaming in 3D scenes, which you don’t have to do by yourself. Three. Js provides many controllers with different interaction effects, including the FirstPersonControls, which is the interaction of playing the game. Through THE W, S, A, D keys to control the left and right, through the mouse control direction.

const controls = new THREE.FirstPersonControls(camera);
controls.lookSpeed = 0.05;
controls.movementSpeed = 100;
controls.lookVertical = false;
Copy the code

We specify transition direction speed lookSpeed, movementSpeed movementSpeed, and forbid vertical rotation.

Then each frame should be updated to see the screen, through the Clock to get how long has passed, and then update the controller.

const clock = new THREE.Clock();

function render() {
    const delta = clock.getDelta();
    controls.update(delta);

    renderer.render(scene, camera);
    requestAnimationFrame(render)
}
Copy the code

Take a look at the final result:

The entire code is uploaded to Github:

Code address: github.com/QuarkGluonP…

conclusion

This paper writes the realization principle of Three. Js drawing 3D house.

Three.js manages various objects through Scene, and objects can be grouped. Objects are made up of two parts: Geometry and Material. The house is made up of various Geometry such as BoxGeometry and extruded Geometry, with different textures, positions and rotation angles.

Special among them is ExtrudeGeometry, which works by drawing a shape on a two-dimensional surface and then “squeezing” it into three dimensions, with a hole in the shape.

There is a bed in the house, and this complex object is more difficult to draw by hand with Three.js, which is usually drawn by professional modeling software such as Blender and then loaded and rendered with Three.js.

Three. Js provides a variety of controls such as OrbitsControls, FirstPersonControls, and so on.

We want to use FirstPersonControls for the interaction of front, back, left, and right via the keyboard and steering via the mouse.

Three.js is still pretty fun and can be used mainly for visualization and games in business, but it can also be used for fun things outside of work.