Three.js is an awesome library. It makes complicated things such as 3D graphics and shaders easy for the average frontend developer, which opens up lot of previously inaccessible avenues for web development. You can check out this repository of examples to see what’s possible (basically, the sky is the limit).
Even though Three.js provides all of this funcitonality, it is one of the lesser-documented libraries, and hence can be a little bit overwhelming for a newcomer. This tutorial will take you through the steps required to render a nice little scene with the Three.js library.
We will be using npm and webpack to make our application, along with ES6 syntax.
Initialize a new project in a new folder:
npm init
After that, install Three.js:
npm install --save three
And we are all set!
A cubemap, if you haven’t heard of it before, is precisely what its name suggests. Think of six enormous square pictures, all joined together to form a cube, with you being inside of the cube. The six pictures then form a cubemap. It is used to make 3D background sceneries and fillers.
Every rendering in 3D graphics has two elements: the scene and the camera. The renderer then renders the scene relative to the camera. In this way, you can move through a scene by adjusting its camera, and at the same time stay still with respect to another scene because of its camera. This is the basic principle used while making movement-based 3D graphics. You (or in this case, your camera) are standing still with respect to the background (or in some cases, moving really slowly), considering it to be at a near-infinite distance from you. At the same time, you will be moving with respect to the objects around you, since they are considered to be within your immediate distance.
import THREE from 'three';
let sceneCube = new THREE.Scene();
let cameraCube = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 100000 );
let path = '/cubemap/cm';
let format = '.jpg';
let urls = [
path + '_x_p' + format, path + '_x_n' + format,
path + '_y_p' + format, path + '_y_n' + format,
path + '_z_p' + format, path + '_z_n' + format
];
But where do I find cubemap images from?
Normally, you would have to use Google for cubemap images, but those are not the best quality. You can make your own cubemap from normal images and some respectable photoshop skills, or you can take images from some of the examples that already exist.
let textureCube = THREE.ImageUtils.loadTextureCube(urls, THREE.CubeRefractionMapping);
let shader = THREE.ShaderLib.cube;
shader.uniforms.tCube.value = textureCube;
let material = new THREE.ShaderMaterial({
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: shader.uniforms,
depthWrite: false,
side: THREE.BackSide
}),
mesh = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material);
sceneCube.add(mesh);
Of course, we want to keep our code modular, so all of the above code for making a cubemap should ideally be wrapped in its own function and used in our main program as and when it is needed. The final cubemap “module” would look something like this:
'use strict';
import THREE from 'three';
let Cubemap = function () {
let sceneCube = new THREE.Scene();
let cameraCube = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 100000 );
let path = '/cubemap/cm';
let format = '.jpg';
let urls = [
path + '_x_p' + format, path + '_x_n' + format,
path + '_y_p' + format, path + '_y_n' + format,
path + '_z_p' + format, path + '_z_n' + format
];
let textureCube = THREE.ImageUtils.loadTextureCube(urls, THREE.CubeRefractionMapping);
let shader = THREE.ShaderLib.cube;
shader.uniforms.tCube.value = textureCube;
let material = new THREE.ShaderMaterial({
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: shader.uniforms,
depthWrite: false,
side: THREE.BackSide
}),
mesh = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), material);
sceneCube.add(mesh);
return {
scene : sceneCube,
camera : cameraCube
};
};
module.exports = Cubemap;
Now, we will have to write the core of our little app to actually render the cubemap onto an element in the web browser:
'use strict';
import THREE from 'three';
import Cubemap from './Cubemap';
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
let renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
The renderer returns a canvas element, which you can fix to your DOM. This canvas element is where all the magic happens. As usual, we are creating another scene and camera, different from the ones in our cubemap. All objects that are not the cubemap will be included in this scene.
let lightAmbient = new THREE.AmbientLight(0x202020); // soft white light
scene.add(lightAmbient);
let cubemap = Cubemap();
let render = function () {
requestAnimationFrame(render);
renderer.render(cubemap.scene, cubemap.camera);
renderer.render(scene, camera);
cubemap.camera.rotation.copy(camera.rotation);
};
render();
In case you are using movement (which is most often the case with WebGL), you will want to render your scene a number of times a second. The requestAnimationFrame function is a native browser function that calls the function you pass to it after a set time.
Three.js may seem overwhelming at first, but it’s a huge improvement over the otherwise steep learning curve for GLSL. If you want to see slightly more complex example of using cubemaps and objects in Three.js, you can go here.
Soham Kamai is a Fullstack web developer and electronics hobbyist. He is especially interested in JavaScript, Python, and IOT. He can be found on Twitter @sohamkamani and GitHub at https://github.com/sohamkamani.
I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…
Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…
Once we learn how to deploy an Ubuntu server, how to manage users, and how…
Key-takeaways: Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…
While developing a web application, or setting dynamic pages and meta tags we need to deal with…
Software architecture is one of the most discussed topics in the software industry today, and…