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.
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 units.
Note: Real-world quantities in cannon.js, such as mass, velocity, and so on, make use of SI Units.
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);
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 simulationanimate();
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
.
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>
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
.
Free Resources