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

Designing the Future & the Future of Work – The I... Martin Wezowski, Chief Designer and Futurist at SAP, shares his thoughts on designing the future and the future of work on this episode of The Innovat...
The 4 Characteristics of a Healthy Digital Product Team Several weeks ago, I found myself engaged in two separate, yet eerily similar, conversations with CEOs struggling to gain the confidence they needed t...
Recapping Fortune Brainstorm Tech – The Innovation Eng... On this episode of The Innovation Engine, David DeWolf and Jonathan Rivers join us to share an overview of all the news that was fit to print at this ...
4 Reasons Everyone is Wrong About Blockchain: Your Guide to ... You know a technology has officially jumped the shark when iced tea companies decide they want in on the action. In case you missed that one, Long Isl...
The Connection Between Innovation & Story On this episode of The Innovation Engine, we'll be looking at the connection between story and innovation. Among the topics we'll cover are why story ...