A journey towards Web3D
Getting started with WebGL for Web 3D using threejs library.
Let's dive deep into WebGL, the engine running on browser Javascript VM exuding all these beautiful works of art.
The Setup
It's painfully simple to setup threejs. Just import the library and you will have the global THREE object available for you to play with.
Add the CDN link of threejs to your project, at the <head> section of your document.
After working on this project for a week, I understand how close design and engineering are.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.min.js"></script>
</head>
<body>
<script>
// Our Javascript will go here or link to your local .js file!
</script>
</body>
</html>
Abstraction before creation
Before getting on with creating an object, abstract the code into easy to test blocks till you learn. As you get better at this, organize your project in any way you see fit.
Remember, abstraction and organization is one of the most important aspect of a project; makes it easy to debug and scale.
/* Our JS section or file containing the animation */
// Define variables that we need globally. Not a good practice for large scale projects. Will become clear as we go into our functions.
var scene, renderer, camera, render, d_light, mouse_light, mouse;
// The main functions that contains everything we need
init();
animate();
function init() {
/* Contains the scene, meshes, materials and light setup section */
}
function animate() {
/* Contains the animation loop where the scene is rendered and stuff animated */
}
Creating the world stage
In order to see something, we need to render a scene. Let's create these two using the THREEjs toolkit.
/* Let's create our scene and renderer within init() */
function init() {
// Create a renderer which will contain all scenes. The renderer is a place which will be added to your HTML. Usually lives within <canvas> HTML element. There are many kinds of renderers, we use WebGLRenderer.
renderer = new THREE.WebGLRenderer();
renderer.physicallyCorrectLights = true; // This will make things difficult, but beautiful nonetheless. Lights need high power in this mode.
renderer.setSize(window.innerWidth, window.innerHeight); // Set size if you like it to take full window
renderer.setPixelRatio( window.devicePixelRatio ); // Helps with rendering on mobile and desktop
document.body.appendChild(renderer.domElement); // Append this to HTML. You can .appendChild to any parent div or canvas! In our case, it's full screen.
// Create a scene in our renderer.
scene = new THREE.Scene();
}
At this stage you should be able to see a pitch black canvas. If not, follow the instructions from the official documentation.
Lights, Camera, Action!
It's pitch black. Several things to be done before we see anything.
Because, we need a camera to observe our scene. We still wouldn't see anything.
Because, we need lights to light up the scene and things in it.
We need a cube so the light reflects it, and we can see it. We still wouldn't see anything unless we add "renderer.render(scene, camera);" in our init() function. this will show one rendered static frame.
Creating a light
Read more about Spotlights here. THREE provides many kinds of lights.
// Create a function for your light. The actual method is pretty simple but keeping our common elements in a functions keeps the code clean.
function createSpotLight(color, x, y, z, power) {
_light = new THREE.SpotLight(color);
_light.intensity = 2;
_light.power = power;
_light.penumbra = 0.8;
_light.decay = 2;
_light.position.set(x, y, z);
_light.castShadow = true;
return _light;
}
// Within your init() function, just after your scene add a light.
function init() {
// ... after other code ...
// Add your spotlight using the function we just created.
var s_light = createSpotLight(0xffffff, -15, 15, -15, 800);
scene.add( s_light );
// Why not add an ambient light. Just to make sure we can see things.
var light = new THREE.AmbientLight( 0x404040, 2 ); // soft white light
scene.add( light );
}
Creating your cube
Read more about creating your first mesh here.
// Within your init() function, just after your scene add a light.
function init() {
// ... after lights
var geometry = new THREE.BoxGeometry( 5, 5, 5 );
var material = new THREE.MeshStandardMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
}
Creating the camera
To see things, we need a camera. Let's add it and place it just slightly away from the center point.
// Within your init() function, just after your scene add a light.
function init() {
// ... after the cube
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(0, 5, 20); // Place it slightly away on the y and z axis.
camera.lookAt(scene.position); // Make it look at the center of the scene :)
}
This is what it will look like
The final code, along with the completed animate() function. All it does is, call the renderer function to render the scene frame, and then repeats it on every frame because of requestAnimationFrame();
var scene, renderer, camera, render, d_light, material, mouse_light, mouse, raycaster;
init();
animate();
function init() {
// Create a renderer which will contain all scenes. The renderer is a place which will be added to your HTML. Usually lives within <canvas> HTML element. There are many kinds of renderers, we use WebGLRenderer.
renderer = new THREE.WebGLRenderer();
renderer.physicallyCorrectLights = true; // This will make things difficult, but beautiful nonetheless. Lights need high power in this mode.
renderer.setSize(window.innerWidth, window.innerHeight); // Set size if you like it to take full window
renderer.setPixelRatio( window.devicePixelRatio ); // Helps with rendering on mobile and desktop
document.body.appendChild(renderer.domElement); // Append this to HTML. You can .appendChild to any parent div or canvas! In our case, it's full screen.
// Create a scene in our renderer.
scene = new THREE.Scene();
// Add your spotlight using the function we just created.
var s_light = createSpotLight(0xffffff, -15, 15, -15, 800);
scene.add( s_light );
// Why not add an ambient light. Just to make sure we can see things.
var light = new THREE.AmbientLight( 0x404040, 2 ); // soft white light
scene.add( light );
var geometry = new THREE.BoxGeometry( 5, 5, 5 );
var material = new THREE.MeshStandardMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(0, 5, 20); // Place it slightly away on the y and z axis.
camera.lookAt(scene.position); // Make it look at the center of the scene :)
}
function animate() {
// Reccursive function to request a new frame, the magic actually happens within the render function!
requestAnimationFrame(animate);
// Render the scene again, as each frame is animated.
renderer.render(scene, camera);
};
function createSpotLight(color, x, y, z, power) {
_light = new THREE.SpotLight(color);
_light.intensity = 2;
_light.power = power;
_light.penumbra = 0.8;
_light.decay = 2;
_light.position.set(x, y, z);
_light.castShadow = true;
return _light;
}
In the below sample, I have added one more light just to light the scene up and enlarged the cube.
See the Pen ThreeJS Simple View by Varun D (@varun_acn) on CodePen.
Feel free to copy the code and experiment!