How to add physics in three.js

Three.js is a JavaScript library used for rendering graphics and animations on the web by making use of WebGL. However, it's only responsible for rendering the graphics. We can add physics to objects by using another JavaScript library, cannon.js.

Initializing the physical world

Just like the Scene in three.js, the World in cannon.js needs to be initialized before we can add objects to it. Below is the syntax to initialize CANNON.World.

const world = new CANNON.World()

The World class has many technical properties and methods. The one we are concerned with is gravity. This three-dimensional vector exerts vector force on all bodies presents inside the World. We can set the value of this vector using the syntax below:

world.gravity = new CANNON.Vector3(0, -9.82, 0);

Here, we set the gravity to exert a force in the direction of the vector, which is downwards with a magnitude of 9.82-9.82 units.

Note: Real-world quantities in cannon.js, such as mass, velocity, and so on, make use of SI Units.

Adding objects inside the world

Now that the world has been initialized, we can use the addBody method to add objects inside the world - but they need to be created first. Below is the syntax to create a body with a box shape.

const boxBody = new CANNON.Body({
      mass: 5, // kg
      shape: new CANNON.Box(new CANNON.Vec3(1,1,1)),
})

Here, we have initialized CANNON.Body and given it a mass of 5 units and a shape of a box. CANNON.Box is given a three-dimensional vector that defines the size of the box along each axis.

Once the body has been created, we can add it to the world by using the syntax below:

world.addBody(boxBody);

Starting the simulation

After everything has been set in place, we need to start the simulation. Similar to the renderer.render method that needs to be called at each frame for three.js; we need to call the world.fixedStep for cannon.js. This method computes the physics of the world at each frame.

function animate() {
requestAnimationFrame(animate);
world.fixedStep();
}
// start simulation
animate();

Binding physics with three.js

It is important to note that cannon.js doesn’t render any animations, so the code shown above would be useless if used. We need to bind the three.js elements to the bodies in cannon.js for the physics actually to work.

We do this by mapping the positions and rotations of the bodies in the physical world to the shapes rendered in three.js.

So, if we want a box to mimic boxBody, we will first need to create it in three.js and add it to the Scene.

const boxGeometry = new THREE.BoxGeometry(2,2,2);
const boxMaterial = new THREE.MeshPhongMaterial({color: 0xfafafa,});
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(boxMesh);

Inside animate(), we then use the following statements.

boxMesh.position.copy(boxBody.position);
boxMesh.quaternion.copy(boxBody.quaternion);

Here,

  • Line 1: We'll copy the position of the physical box, boxBody, to the position of the animated box, boxMesh.

  • Line 2: We'll copy the rotation of the physical box, boxBody, to the rotation of the animated box, boxMesh.

Example

Below is the code for a React application that uses three.js and cannon.js to render graphics in a physical world.

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three Physics</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.jsx"></script>
  </body>
</html>

Explanation

In App.jsx, our application renders an HTML canvas and populates the <div> with id root in /public/index.html.

  • Line 17: Here, we initialize the cannon.js World and set the gravity of the world by passing it a three-dimensional vector.

  • Lines 20–23: Here, we initialize a Body with the shape of a Box.

  • Lines 25–27: In these lines of code, we set the orientation of boxBody. We place it at an altitude with some rotation, so when it comes down, it has a linear and rotational motion to it.

  • Lines 30–35: Here, we create another Body to use as a ground. We set its type to CANNON.Body.Static. This is the same as calibrating the mass of Body to 0. Before adding it to the world, we make ground face up by setting its rotation.

  • Lines 38–91: This code creates our scene in three.js with two shapes inside it. One is boxMesh, and the other is planeMesh. These are the shapes we will bind with the bodies created using cannon.js.

  • Lines 71–72: Here, we copy the position and rotation of ground into planeMesh. We only need to do this once (and not every frame) because this Body is Static, and does not need to be updated because it won't be moving.

  • Line 76: Here, we initialize OrbitControls to allow the user to pan the camera around the scene. To read more on OrbitControls, please follow this link.

  • Lines 82–83: Here, we update the position and rotation of boxMesh by setting them to the coordinates and quaternion of boxBody, respectively.

  • Line 86: world.fixedStep(), as discussed earlier, updates the physics of the World.

Copyright ©2024 Educative, Inc. All rights reserved