Build a 3D Earth Globe Model in Three.js (PS: It's easier than you think) 🌏😱
Tags: webdev, javascript, beginners, tutorial
Published: 2024-01-10 05:04:48

## Introduction & Demo
Hop on a fascinating tutorial as I guide you through the surprisingly simple process of building a stunning 3D Earth Globe Model using Three.js.
To begin with, I will explain what WebGL & Three.js is and then we will proceed to the build.
[This](https://earth-globe.vercel.app/) is what you will build. Find the source code [here](https://github.com/ArjunCodess/earth-globe-threejs).

Let’s get started!

## What is WebGL?
WebGL is a JavaScript Graphics API that renders high-quality interactive 3D and 2D graphics. It can be simply used in HTML <canvas> elements.

## What is Three.js?
Three.js is a JavaScript library that is used for creating and displaying 3D graphics in a "compatible" web browser. It uses WebGL, which is a low-level graphics API.

## File Structure
```txt
---- textures
   |---- earthCloud.png
   |---- earthbump.jpeg
   |---- earthclouds_8k.jpeg
   |---- earthmap.jpeg
   |---- earthmap_clouds.jpeg
   |---- earthmap_night.jpeg
   |---- galaxy.png
---- index.html
---- style.css
---- three.js
---- main.js
```

Download the /texture files through [this](https://github.com/ArjunCodess/earth-globe-threejs/tree/main/texture) link.

## Code
### /index.html
This HTML code is used to set up the environment for our Three.js Earth Globe Model.
It defines the document structure and links external resources such as style sheets and JavaScript files.
The canvas element with the id 'globe' is where our 3D model will be drawn. The order of script inclusion is important, making sure that ‘three.js’ is loaded before our main logic in 'main.js'.

```HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Earth Globe Model With Three.js</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <canvas id="globe"></canvas>

    <script src="three.js"></script>
    <script src="main.js"></script>
</body>
</html> 
```

### /style.css
Styling for the element with id 'globe' which is our canvas: fixed position, aligned to the top-left corner of the screen.
```css
#globe {
    position: fixed;
    top: 0;
    left: 0;
}
```

### /three.js
Just head over to [build/three.js](https://threejs.org/build/three.js) and copy/paste the code in this file. No observations here.

### /main.js
The main coding starts here. Let's go!

#### Step 1
Create a main function.

``` js
function main() {}
```

#### Step 2
We now establish the core components: scene, renderer, and camera.

Firstly, we create a new THREE.Scene() to serve as the container for all our 3D elements.

Next, the THREE.WebGLRenderer() is configured to render our scene onto our HTML canvas which we defined earlier with the ID "globe".

The renderer's size is then set to match the window dimensions using renderer.setSize(window.innerWidth, window.innerHeight).

Lastly, a THREE.PerspectiveCamera() is initiated. This camera is pivotal for defining our view of the scene. The parameters include a 45-degree vertical field of view, the aspect ratio based on the window dimensions, and the near and far clipping planes (0.1 and 1000, respectively). This determines what is visible within the camera's view. Set the camera's position along the z-axis to 1.7. The scene is now set.

```js
const scene = new THREE.Scene();

const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#globe') });

renderer.setSize(window.innerWidth, window.innerHeight);

const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 1.7;
```

#### Step 3
Let's get into the Earth itself. We start by defining the Earth's geometry using THREE.SphereGeometry(), setting the radius to 0.5 and specifying the number of horizontal and vertical segments (32, 32 for a better appearance).

Next, Initialise the Earth's material with THREE.MeshPhongMaterial(). This material includes textures. The map property loads the Earth's surface texture, while bumpMap integrates a bump map for added depth. Adjust the bumpScale parameter to control the intensity of the bumps.

Finally, we combine the geometry and material to create a mesh, which forms our 3D Earth model. This mesh is added to the previously made scene.

```js
const earthGeometry = new THREE.SphereGeometry(0.5, 32, 32);

const earthMaterial = new THREE.MeshPhongMaterial({
    map: new THREE.TextureLoader().load('texture/earthmap.jpeg'),
    bumpMap: new THREE.TextureLoader().load('texture/earthbump.jpeg'),
    bumpScale: 1,
});

const earthMesh = new THREE.Mesh(earthGeometry, earthMaterial);

scene.add(earthMesh);
```

#### Step 4
Now, let’s light up our globe by adding some lighting effects.

We create a variable pointLight with new THREE.PointLight(). This light source emits white light (0xffffff) with an intensity of 5 and a range of 4 units. Adjust these parameters based on your desired lighting effects.

The light’s position is set using pointLight.position.set(1, 0.3, 1), specifying the x, y, and z coordinates. This determines where the light originates.

Finally, the pointLight is added to the scene.

```js
const pointLight = new THREE.PointLight(0xffffff, 5, 4);
pointLight.position.set(1,0.3,1);
scene.add(pointLight);
```

#### Step 5
Let’s make our globe more realistic by adding clouds.

We start by defining the geometry of the cloud using THREE.SphereGeometry() with a slightly larger radius (0.52) than that of the Earth itself to enhance the visual effect of the cloud layer. The number of horizontal and vertical segments remains at 32 for smoothness.

The cloud material is created with new THREE.MeshPhongMaterial(). The map property loads the cloud texture, and we set transparent: true to ensure that clouds do not obstruct the view of Earth below.

Now, we combine the geometry and material to form a cloud mesh. This mesh is then added to the scene which gives our Earth a realistic view.

```js
const cloudGeometry = new THREE.SphereGeometry(0.52, 32, 32);
const cloudMaterial = new THREE.MeshPhongMaterial({
    map: new THREE.TextureLoader().load('texture/earthCloud.png'),
    transparent: true
});
const cloudMesh = new THREE.Mesh(cloudGeometry, cloudMaterial);
scene.add(cloudMesh);
```

#### Step 6
To create a celestial atmosphere, we will introduce a star-ry background texture.

We define the star geometry using THREE.SphereGeometry() and give it a large radius (5) to cover the entire scene. The increased number of horizontal and vertical segments (64, 64) ensures that the starry sky is well detailed.

The star material is created with THREE.MeshBasicMaterial(). The map property loads a texture representing a galaxy. 

THREE.BackSide() setting ensures that the material is applied to the inner side of the sphere, creating an environment that surrounds our scene.

In the end, we combine the geometry and material to form the star mesh. This mesh is then added to the scene.

```js
const starGeometry = new THREE.SphereGeometry(5, 64, 64);
const starMaterial = new THREE.MeshBasicMaterial({
    map: new THREE.TextureLoader().load('texture/galaxy.png'),
    side: THREE.BackSide
});

const starMesh = new THREE.Mesh(starGeometry, starMaterial);
scene.add(starMesh);
```

#### Step 7
We now declare and initialize variables outside of the main function that will help us in handling interaction with the user and controlling the rotation of the globe.

targetRotationX and targetRotationY: These variables store the initial rotation values around the X and Y axes [control the rotation of the Earth Globe Model in the render function].

mouseX and mouseY: These variables store the current mouse coordinates on the screen.

mouseXOnMouseDown and mouseYOnMouseDown: These variables store the mouse coordinates at the moment when the user starts dragging

windowHalfX and windowHalfY: These represent half of the window's width and height.

dragFactor: This determines how sensitive is rotation to mouse dragging. A smaller value makes it more sensitive while a larger value reduces sensitivity.

```js
let targetRotationX = 0.005;
let targetRotationY = 0.002;
let mouseX = 0, mouseXOnMouseDown = 0, mouseY = 0, mouseYOnMouseDown = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
const dragFactor = 0.0002;
```

#### Step 8
Define functions to render and animate our Globe.

The render function updates the rotation of both the Earth mesh and the cloud mesh based on targetRotationX and targetRotationY values. 

The animate function uses requestAnimationFrame() to create a continuous animation loop. It calls the render function within each frame, updating the display and creating a smooth animation effect.

Finally, we call the animate function to start the animation loop.

```js
const render = () => {
    earthMesh.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), targetRotationX);
    earthMesh.rotateOnWorldAxis(new THREE.Vector3(1, 0, 0), targetRotationY);
    cloudMesh.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), targetRotationX);
    cloudMesh.rotateOnWorldAxis(new THREE.Vector3(1, 0, 0), targetRotationY);
    renderer.render(scene, camera);
}

const animate = () => {
    requestAnimationFrame(animate);
    render();
}

animate();
```

#### Step 9
Let's now define a set of functions that handle mouse events for interaction with the Globe (basically rotation).

onDocumentMouseDown: This function is called when the mouse button is pressed. It prevents the default behavior of the event, adds event listeners for mouse move and mouse up, and records the initial mouse position.

onDocumentMouseMove: This function is called when the mouse is moved. It updates the current mouse position and calculates the rotation based on (currentPosition - initialPosition).

onDocumentMouseUp: This function is called when the mouse button is released. It removes the event listeners for mouse move and mouse up, which indicates that the interaction has ended.

```js
function onDocumentMouseDown(event) {
    event.preventDefault();
    document.addEventListener('mousemove', onDocumentMouseMove, false);
    document.addEventListener('mouseup', onDocumentMouseUp, false);
    mouseXOnMouseDown = event.clientX - windowHalfX;
    mouseYOnMouseDown = event.clientY - windowHalfY;
}

function onDocumentMouseMove(event) {
    mouseX = event.clientX - windowHalfX;
    targetRotationX = (mouseX - mouseXOnMouseDown) * dragFactor;
    mouseY = event.clientY - windowHalfY;
    targetRotationY = (mouseY - mouseYOnMouseDown) * dragFactor;
}

function onDocumentMouseUp(event) {
    document.removeEventListener('mousemove', onDocumentMouseMove, false);
    document.removeEventListener('mouseup', onDocumentMouseUp, false);
}
```

#### Step 10
Add an Event listener to the document for the mousedown event, which calls the onDocumentMouseDown function when the user presses the mouse button and starts the Globe rotation.

When the window finishes loading, execute the 'main' function.

```js
// last line of the main function
document.addEventListener('mousedown', onDocumentMouseDown, false);

// outside of the main function
window.onload = main;
```

## Conclusion
It is a thrilling experience to make an Earth Globe Model with Three.js. Be creative and keep experimenting with new things! 🌐✨

Happy Coding! 🚀
Thanks for 11175!