3D Graphics with WebGL, Three.JS & Babylon.JS

3D-Visualization and gaming has come a long way since just two decades ago, when implementations required a lot of overwork, including third-party plug-ins, and in some cases also required capturing of motion graphics for commercial 3D gaming.

The results we found had always been awe-inspiring, but they often incurred huge costs and required countless hours of work behind the scenes. However, the options that we have today make this process much easier. This post will discuss how JavaScript provides us some great answers with WebGL and two of the APIs – Three.js and Babylon.js –that use WebGL to provide cross- browser solutions for 3D visualization and gaming.

What is WebGL?

WebGL is a JavaScript-based API that provides a medium to render interactive 3D and 2D graphics on any compatible web browser. The advantage here lies in the fact that there is no dependency on plug-ins. WebGL does this with the help of control logic implemented with JavaScript and shader logic that is executed on a Graphics Processing Unit (GPU). Code implementations for 3D graphics can be written directly in WebGL but it comes with some caveats, like a very high learning curve and the absence of graceful fall back in case of incompatibility. This is where two of the most flexible open source libraries, Three.js and Babylon.js, come to the rescue.

What are Three.js and Babylon.js?

These are JavaScript-based frameworks that handle the complexities of WebGL with ease and provide a user-friendly way of implementing 3D graphics. While Three.js was designed as a tool for general purpose web animations, Babylon.js was designed to take a more targeted approach for web-based game development and uses an extensive physics engine for collision detection and other intricacies involved for 3D graphics.

Possibilities with Three.js and Babylon.js

The possibilities are endless–from simulating an actual fluttering cloth to a simulation of an entire universe with plants to implementing a 360-degree view of a room. The rendering of landscapes and terrains are nearly perfect examples of the beauty of these frameworks. Below are a few examples of what can be achieved with these frameworks:

As a brief introduction, let us take a look at the interiors of Three.js. For any 3D visualization, we would require the following objects:

  • Camera
  • Scene–represents a list of objects that is displayed on-screen, like 3D models and lights
  • Geometry–the shape of objects, like cube, sphere, cylinder
  • Material–color, image, or texture that defines how a geometrical shape would be displayed
  • Mesh–composed of the geometry and material

To begin, we would implement an animated cube. The declaration of objects that we would need comes first:

var camera, scene, renderer;
var geometry, material, mesh;

We require two functions–init() and animate().

The init() function creates and sets the position of the camera, creates a cubic geometrical shape, defines the material of the mesh that the shape would be composed of, adds the shape and the mesh onto the scene, and renders the entire scene on a canvas element.

The code for the entire init() function is listed below:

3D Graphics with WebGL

There are multiple types of camera classes provided by Three.js, out of which THREE.PerspectiveCamera is used for a perspective view of the scene. The scene can be added with a mesh (where mesh is composed of a geometrical shape and the colour, or texture, or image that defines how the shape would look), which is implemented by using the THREE.MeshBasicMaterial class. Again, Three.js provides a vast collection of classes of material that can be applied to different geometrical shapes.

The most important aspect is the rendering of the objects that makes WebGL with Three.js or Babylon.js easy to work with. Three.js provides a class THREE.CanvasRenderer that renders the objects automatically on the HTML5 canvas element. This provides a very graceful fallback in case WebGL (which is dependent on the compatibility of the latest browsers) is not compatible with the browser being used at client end.

The animate() function makes use of the requestAnimationFrame() function to perform a call back of the animate() function itself, which updates the animation before the next repaint takes place in the browser. For this example, the rotation vector is updated for the mesh in the scene in any subsequent repaints. The code for the animate() function is listed below:

3D Graphics with WebGL

The animating Cube example can be viewed on the demo link.

This post has discussed a basic level implementation of 3D graphics in WebGL using the Three.js JavaScript library. In subsequent blogs, we will go further, discussing advanced usage of WebGL in tandem with these two libraries in implementing visualizations and 3D gaming.

 

Rahul Upadhyaya

Rahul Upadhyaya

Technical Lead

Rahul Upadhyaya is a Technical Lead in 3Pillar Global’s India office. He has over 9 years of experience in web technologies, with specialties in HTML5, CSS3, and JavaScript. In his current role, he leads a UI team in helping with different client needs. His interests also include game designing and development.

One Response to “3D Graphics with WebGL, Three.JS & Babylon.JS”
  1. Shakeel Ahmed on

    Hello,
    I have made two custom meshes properly using xhtml code, one is garment(T-Shirt) and another is Model(Human Body), but i am trying to simulate Garment with Model means i have added physics engine and collision. I am unable to add imposter and joints beacuse of this simulation between Garment(Cloth) and Model(Human Body).I am too much stuck and waiting your reply.
    I am writing whole code for both custom meshed

    Code:

    // Get the canvas element from our HTML above
    var canvas = document.getElementById(“mesh_id”);

    // Load the BABYLON 3D engine
    var engine = new BABYLON.Engine(canvas);

    // Now create a basic Babylon Scene object
    var scene = new BABYLON.Scene(engine);

    var gravityVector = new BABYLON.Vector3(0,-9.81, 0);
    var physicsPlugin = new BABYLON.CannonJSPlugin();
    scene.enablePhysics();
    scene.enablePhysics(gravityVector, physicsPlugin);
    scene.setGravity(new BABYLON.Vector3(0, -9.81, 0));

    scene.clearColor = new BABYLON.Color3(0.8, 0.8, 0.8);

    var light = new BABYLON.HemisphericLight(“hemi”, new BABYLON.Vector3(0, 1, 0), scene);
    light.groundColor = new BABYLON.Color3(0.2, 0.2, 0.5);
    light.intensity = 0.6;

    var light2 = new BABYLON.PointLight(“light2”, new BABYLON.Vector3(-20, 0, -20), scene);
    light2.diffuse = BABYLON.Color3.White();
    light2.specular = BABYLON.Color3.Black();
    light2.intensity = 0.6;

    var camera = new BABYLON.ArcRotateCamera(“Camera”, 3 * Math.PI / 2, Math.PI / 2, 50, BABYLON.Vector3.Zero(), scene);
    camera.attachControl(canvas, true);
    camera.setPosition(new BABYLON.Vector3(0, 15, 150));
    camera.checkCollisions = true;
    camera.applyGravity = true;

    var subdivisions = 96;
    var groundWidth = 0;

    var garment = new BABYLON.Mesh(“mesh”, scene);
    var myMaterial = new BABYLON.StandardMaterial(“myMaterial”, scene);

    // garment.setPhysicsState({ impostor: BABYLON.PhysicsEngine.BoxImpostor, mass: 1, friction: 0.5, restitution: 0.7 });
    garment.checkCollisions = true;

    myMaterial.diffuseTexture = new BABYLON.Texture(“textures/flowers.jpg”, scene);
    myMaterial.specularTexture = new BABYLON.Texture(“textures/flowers.jpg”, scene);
    myMaterial.emissiveTexture = new BABYLON.Texture(“textures/flowers.jpg”, scene);
    myMaterial.ambientTexture = new BABYLON.Texture(“textures/flowers.jpg”, scene);
    myMaterial.backFaceCulling = true;
    garment.position.y = 60;
    garment.material = myMaterial;
    myMaterial.wireframe = true;

    // Garment(T-Shirt)
    coordinates_list = [-5.000000,0.000000,5.000000,-2.500000,0.000000,…..]
    var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,……]
    var uvs = [0.000000,0.000000,0.250000,0.000000,0.250000,0.250000,…..]
    var normals = [];

    var vertexData = new BABYLON.VertexData();
    BABYLON.VertexData.ComputeNormals(coordinates_list, indices, normals);
    vertexData.positions = coordinates_list;
    vertexData.indices = indices;
    vertexData.normals = normals;
    vertexData.uvs = uvs;
    vertexData.applyToMesh(garment);

    garment.physicsImpostor = new BABYLON.PhysicsImpostor(garment, BABYLON.PhysicsImpostor.MeshImpostor, { mass: 1, restitution: 0.9 }, scene);

    var position_vertices_data = garment.getVerticesData(BABYLON.VertexBuffer.PositionKind);
    var spheres = [];
    for (var i = 0; i = subdivisions) {
    createJoint(point.physicsImpostor, spheres[idx – subdivisions].physicsImpostor);
    if (idx % subdivisions) {
    createJoint(point.physicsImpostor, spheres[idx – 1].physicsImpostor);
    }
    }
    });

    garment.registerBeforeRender(function () {
    var positions = [];
    spheres.forEach(function (s) {
    positions.push(s.position.x, s.position.y, s.position.z);

    });
    garment.updateVerticesData(BABYLON.VertexBuffer.PositionKind, positions);
    garment.refreshBoundingInfo();
    });

    // BASE MODEL STARTS
    var basemodel = new BABYLON.Mesh(“basemodel”, scene);
    var basemodelmaterial = new BABYLON.StandardMaterial(“basemodelmaterial”, scene);
    basemodel.setPhysicsState({ impostor: BABYLON.PhysicsEngine.SphereImpostor, mass: 0,friction: 0.9 }, scene, true);
    basemodel.checkCollisions = false;
    basemodelmaterial.backFaceCulling = false;
    basemodel.material = basemodelmaterial;

    var coordinates_list = [-4.679900,39.907700,1.244800,-4.377600,40.614700,1.138200,…….]
    var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,………]
    var normals = [];

    var vertexData = new BABYLON.VertexData();
    BABYLON.VertexData.ComputeNormals(coordinates_list, indices, normals);
    vertexData.positions = coordinates_list;
    vertexData.indices = indices;
    vertexData.normals = normals;

    vertexData.applyToMesh(basemodel);
    basemodel.physicsImpostor = new BABYLON.PhysicsImpostor(basemodel, BABYLON.PhysicsImpostor.MeshImpostor, { mass: 0 }, scene,true);
    // BASE MODEL ENDS

    // Collision
    scene.collisionsEnabled = true;
    scene.applyGravity = true;
    ground.checkCollisions = true;
    spheres.checkCollisions = true;

    // Register a render loop to repeatedly render the scene
    engine.runRenderLoop(function () {
    scene.render();
    });

    // Watch for browser/canvas resize events
    window.addEventListener(“resize”, function () {
    engine.resize();
    });

    Reply
Leave a Reply

Related Posts

The 3 Keys to Building Products That Drive Retention –... I had the privilege of being invited to speak at the Wearable Technology Show in Santa Clara this week, where I gave a bit of a reprisal of a talk I d...
High Availability and Automatic Failover in Hadoop Hadoop in Brief Hadoop is one of the most popular sets of big data processing technologies/frameworks in use today. From Adobe and eBay to Facebook a...
3Pillar CEO David DeWolf Quoted in Enterprise Mobility Excha... David DeWolf, Founder and CEO of 3Pillar Global, was recently quoted in a report by Enterprise Mobility Exchange on the necessity of understanding and...
How the Right Tech Stack Fuels Innovation – The Innova... On this episode of The Innovation Engine podcast, we take a look at how choosing the right tech stack can fuel innovation in your company. We'll talk ...
The Road to AWS re:Invent 2018 – Weekly Predictions, P... For the last two weeks, I’ve been making predictions of what might be announced at AWS’ upcoming re:Invent conference. In week 1, I made some guesses ...