Of all the HTML5 and related APIs we’ve been given, WebGL is possibly the most interesting. With it we can create awesome 3D environments right in our browsers, and in this article I’m going to walk you through exactly how to do that.
In particular, we’re going to be building a model of planet Earth that we can rotate around like a crazy person and with which we can wow our fellow developers… well, at least a little.
Before We Begin
For this tutorial we will be using the fantastic three.js WebGL plugin. It’s a bit like jQuery for WebGL in that it abstracts a lot of the processing ugliness inherent in the native API. Put simply, three.js makes our lives a lot easier.
We can include it, just like any normal script in our HTML document, like so:
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r63/three.min.js"></script>
Here we’ve used a CDN, but you could use a local version. We then need to make sure that WebGL has something to render onto. Here we have some flexibility: we can use an ordinary div
or canvas to render directly into the HTML, otherwise we can create and append the canvas later using JavaScript. We’ll go with the first method, to make things a little easier:
<body>
<div id="container"></div>
<script src="earth.js"></script>
</body>
After adding the link to the JavaScript file, our HTML is more-or-less done for now.
The Setup
Three.js itself tends to set things up much like a real 3D desktop program might. We have a scene in which things live, a camera with which to view them, some lighting, a renderer to actually render things, and of course the 3D objects themselves. The list can seem a little daunting, but all of these can take shape as variables within our earth.js
JavaScript file.
var scene,
camera,
light,
renderer,
earthObject;
var WIDTH = window.innerWidth - 30,
HEIGHT = window.innerHeight - 30;
var angle = 45,
aspect = WIDTH / HEIGHT,
near = 0.1,
far = 3000;
There are some other variables defined here, namely the WIDTH and HEIGHT constants to get the width and height for our renderer, and a group of variables we’ll be using later on to set up our camera position.
All of these are elements that are common to almost every 3D project, regardless of the platform or environment, so get used to these guys hanging around. With three.js we’re able to implement these pretty easily, and we’ll see how they fit into our project in a moment.
The Environment
To start, we need to take our new variables and set them up so that our model Earth will show up looking awesome.
We can start by setting up everything that will handle this environmental stuff:
var container = document.getElementById('container');
camera = new THREE.PerspectiveCamera(angle, aspect, near, far);
camera.position.set(0, 0, 0);
scene = new THREE.Scene();
light = new THREE.SpotLight(0xFFFFFF, 1, 0, Math.PI / 2, 1);
light.position.set(4000, 4000, 1500);
light.target.position.set (1000, 3800, 1000);
Here’s a description of what’s happening in the code above:
- We grab a reference to our container in our HTML
- We set up our camera using the variables we declared before (for more info on how cameras work in 3D, check this out).
- We set the camera’s position using
position.set
, which takes an argument for each dimension (x, y and z). As you may have guessed, we’re going to use this camera to point to our 3D objects, which in this case is our Earth.
- Next, we set up our light. Without this we’d just have a black screen when we render everything, so we need to set it up pretty carefully. Three.js’s SpotLight object takes more-or-less the same arguments as our camera, but adds the colour in a hexadecimal value as its first argument followed by the rest.
- Lastly, we set up our renderer. Another vital piece of the puzzle, the renderer actually renders the scene we’ve made onto the screen; again, without it we’d see nothing but the darkness of space. We give it anti-aliasing (for some smooth-ass surfaces) and append it as a DOM element to our original container.
Now we need to start building Earth itself by sticking in a mesh:
var earthGeo = new THREE.SphereGeometry (30, 40, 400),
earthMat = new THREE.MeshPhongMaterial();
var earthMesh = new THREE.Mesh(earthGeo, earthMat); earthMesh.position.set(-100, 0, 0);
earthMesh.rotation.y=5;
scene.add(earthMesh);
Here we create a mesh, which is kind of like an object that we’ll dress up to look like Earth, and give it some geometry and a material, or texture, to cover it. We also position it properly and add it to our scene like we did with our other objects.
You can see an example of our setup below. There is some extra code here to handle the rendering (which we’ll get to in a moment), but it’s looking good so far!
See the Pen WebGL Earth Tutorial, Demo 1 by SitePoint (@SitePoint) on CodePen.
The Blue Planet
Now the fun part is skinning this guy. First we’ll use a diffuse map, which will give us the look of Earth, and which you can add like this:
// diffuse map
earthMat.map = THREE.ImageUtils.loadTexture('images/earthmap1k.jpg');
If you’re in need of a good texture to use here, you can try this one or search Google for a decent diffuse map of Earth. Anything with a good resolution is fine.
Now this doesn’t look too bad, but we can make it better by introducing a little topography here. The Earth has some pretty high mountains and in order to make sure the rest of the solar system knows that, we’ll need to use a bump map. In 3D, bump maps are black and white images, with the intensity of white indicating the height of the “bump” (or in our case, our mountains).
// bump map
earthMat.bumpMap = THREE.ImageUtils.loadTexture('images/elev_bump_16ka.jpg');
earthMat.bumpScale = 8;
With that, we’re more-or-less there. Again, searching Google for “Earth bump map” will give you plenty of options, but if you don’t feel like hunting you can find a nice one here. If we run the above code (with the rendering code from before), we’ll get this:
See the Pen WebGL Earth Tutorial, Demo 2 by SitePoint (@SitePoint) on CodePen.
Spin It!
All that’s left now is to give our Earth some rotation. We’ll need two new functions for this, which we can call render()
and animate()
.
function animate() {
requestAnimationFrame(animate);
render();
}
Our animate()
function isn’t too complicated. It calls itself continuously using requestAnimationFrame()
and each time it does, it calls our render()
function. Let’s get to that render()
function next:
function render() {
var clock = new THREE.Clock(),
delta = clock.getDelta();
earthMesh.rotation.y += rotationSpeed * delta;
renderer.render(scene, camera);
}
Here’s where things are happening. Every time render()
is called, it spins our Earth a little bit on the y-axis (you can choose any amount to rotate by here; we’re using a built-in clock with a getDelta()
method in this case, but you don’t have to). It then clears our renderer, a necessary step to avoid render ugliness, and renders our scene (with everything in it) and our camera.
To The Bitter End
There are, of course, tweaks we can add to make our Earth even better. OrbitControls.js is a script that will give some mouse-driven rotation ability to our Earth and adding a field of stars to our backdrop or some clouds in our stratosphere isn’t too difficult either. If you’re a real sucker for punishment, you can even add an atmosphere to your planet using WebGL’s shaders.
You can see an example, with code, of some of these additions in the final CodePen demo:
See the Pen WebGL Demo Final by SitePoint (@SitePoint) on CodePen.
Be sure to click around on the page and scroll your mouse wheel to see the special effects (hold down shift to get the mouse wheel working on this page or else use the full page demo).
Conclusion
WebGL and three.js can be challenging because they require us to think a little like a 3D artist at times, using things like scenes, renderers, and cameras to get the job done.
But the result is something that looks pretty impressive and, if you’re dedicated, can lead to some interesting possibilities with 3D in the browser. Stick with it and you’ll be able to pull off some amazing feats in no time.
I just browsed through this article, but from what I noticed it seems really cool to create the planet like this with only HTML, CSS and JavaScript! Really neat.
I thought of what would I use something like this for and my mind set off...
Since I am interested in sustainable communities across the globe I would perhaps map various communities onto a globe creating hundreds of dots to which a user could click each and every dot to learn more about the community in the specific location. It would be a worldwide sustainable community gathering giving the chance for anyone interested learn about any community across the globe. Hundreds of dots would slowly grow into thousands of dots. Creating a broader sustainable world for all of us to learn more about and to take part in.
Have a great day! Thank you for the inspiration!
That's a really great idea @paaljoachim! WebGL has a lot of beautiful applications for this kind of thing. Google has made a number of interesting Chrome experiments with this technology and a lot of it blows my mind.
Have you ever seen Cesium.js? It's a great WebGL 3D globe.
This is pretty neat. I'm pretty sure that in the last example, you don't want to offset the mesh position with
earthMesh.position.set(-100, 0, 0);
. The sphere is currently not at the center of the camera, so when you spin it around with the orbital control, it looks a little weird. (More noticeable if you set the camera close to the earth)I had to build something similar two years ago : https://www.chromeexperiments.com/experiment/cnes-satellites .
Adding another layer with the clouds really improve the general look and feel of the globe. You should try it