Creating 3D Worlds with Babylon.js

Introduction

In our voyage through JavaScript 3D engines, this time we want to show you a great 3D WebGL rendering engine – Babylon.js for building 3D applications and games for the web. Babylon is exceptional with its ease of use, fast prototyping and a polite learning curve. With a good documentation and many examples. Babylon.js seems to be the perfect 3D engine to learn for beginners in the 3D web field.

This article will cover the setup and configuration of the library and the description of basic concepts of building a 3D world using Babylon.js for Tizen Web Applications. Together with this article we are shipping the BABYLONJS example application (fig.1). It was created using the Tizen SDK 2.3 (rev2). We recommend importing the sample application to your Tizen IDE while reading this tutorial, so you can learn the basics of Babylon.js swiftly.

 

Figure 1 – The BABYLONJS example application in action

 

As seen above we have prepared for you an example of an underwater, 3D world. In our application we show you how you can utilize the traditional sprite sheet animation, combined together with 3D objects to achieve great effects with Babylon.js.   

In order to build our 3D world we have used:

  1. A textured skybox for the surrounding underwater world.
  2. A mesh and a texture, for the sea floor, with an applied height map to shape it.
  3. The diver was created from a spritesheet (fig.2) and is rendered as a sprite in our 3D world.
  4. The seaweeds on the sea bed are also spritesheets same as the diver.
  5. Both, bubbles floating from the diver and bubbles around him are created with the particle emitters of Babylon.js. Every single particle is textured with a single bubble.png texture.
  6. We have also laying on the sea floor, the stone statue of the Lumber Jack, probably known to you from the earlier Tizen articles, like this one. The Lubmer Jack was converted using a plugin in Blender to the .babylon file format and imported into Babylon.js.
  7. Finally, we have three different fish – also animated sprites, with applied a sine wobble on their Y axis to make them look more realistic in their endless journey through the vast waters of our 3D world.

 

Figure 2 – The diver animation sprite sheet

 

Also please note that we have a camera in the scene. To be precise, we have a Follow type camera. What it technically does is following the desired target. Why we have used this type of camera? Babylon.js has a pretty nice feature for the Follow camera. It lets you set the delay of the camera until it levels up with the target it is looking at. This feature, combined together with the TweenMax.js library, responsible for moving our diver, gives us the cool effect you can see on figure 1. While the movement of the diver takes place only in 4 directions (left, right, up and down) and the cameras’ movement is delayed, the user gets the feeling of being in a 3D dimensional space.

 

Figure 3 – Tap diver steering areas in the BABYLONJS sample application

 

Babylon.js basic setup

First, you need to setup the Babylon.js rendering engine. But before doing that you need to create a canvas object in your HTML document.

But what about steering with our diver? We have implemented a simple tap steering system, described on figure 3. You can steer by tapping various parts of the screen edges to move the diver.

 

[…]

<canvas id="renderCanvas"></canvas>

[…]

 

Then you can just import the Babylon.js library utilizing the <script> tag in the <head> section of your document.

 

[…]

<script src="js/babylon.2.0.js"></script>

[…]

 

The last thing to do in order to complete the basic setup of Babylon is to fetch the canvas element and assign it to a new instance of the Babylon 3D engine. Then create a createScene function which creates a scene object, assigns the scene to the Babylon engine and at the end it returns the scene object.

[…]

// Fetch the canvas element from the HTML
   var canvas = document.querySelector("#renderCanvas");

// Load BABYLON
   var engine = new BABYLON.Engine(canvas, true);

// Change the scenes' background to black
   scene.clearColor = new BABYLON.Color3(0, 0, 0);
	    
// Set the scenes' ambient color to a bit of blue
   scene.ambientColor = new BABYLON.Color3(0, 0, 0.03);
	
// This function will create our world
   var createScene = function () {

// Creating the BABYLON scene
   var scene = new BABYLON.Scene(engine);

   return scene

}

[…]

 

Please note, that in the code above we have also showed you the clearColor and ambientColor parameters of the scene object. The first one sets actually the 3D scenes’ background color, while the other one sets the overall ambient color for the 3D scene.

After this initial setup you probably want to run everything, but unfortunately you will see nothing at this point. You need two more things to get things running. First of all a rendering loop is needed along with a render() method invoked on the scene object.  Secondly we want our scene to change its size on various screen sizes, so we can attach a resize event to the window object and inside the callback function we invoke the resize() method on the engine object. And finally you have to invoke the earlier created createScene() function. The code below illustrates what we have written about.

 

[…]

    // we need a render loop for our engine
      engine.runRenderLoop(function () {

		scene.render(); // render the scene

        });

    // Hear for the browser and canvas resize events
	  window.addEventListener("resize", function () {
		  
	    engine.resize();
	    
	  });

	// now we create the scene actually invoking the createScnee function
	  var scene = createScene();

[…]

 

The skybox

Having prepared the engine for rendering, we can finally render something in it. First of all, we would like to have a skybox, which will be a background of our Babylon 3D world. A skybox is quite simple to create in Babylon.js. Let’s look at the code below.

 

[…]

// Skybox - we need to create a skybox for our world
   var skybox = BABYLON.Mesh.CreateBox("skyBox", 100.0, scene);                                                   
     //skybox.infiniteDistance = true; // <-- with this you can make the skybox of infinite size
	    
// create material for the skybox
   var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
       skyboxMaterial.backFaceCulling = false;
       skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("images/seabox/sea", scene);
       skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
       skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
       skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
		    
       skybox.material = skyboxMaterial;
		    
       skybox.x = 1000;
[…]

 

We create a box named skybox inside the rendering engine. Set its size and assign it to the scene object. You can also set if the skybox should be infinite or not. This defines if you will be able to zoom in the skybox or not. The next step is to set a material for the skybox. We have used the BABYLON.StandardMaterial. We have turned off backFaceCulling. We don’t want the outer sides of the skybox to be invisible.

After that comes a tricky part with assigning textures to the cube sides. In order to use the reflectionTexture you need to create a folder for the cube maps. In our example the path to the cube maps looks like this images/seabox/sea. This could be misleading for the first time. Why? Because actually the folder this path is pointing to is images/seabox. The sea in the path doesn’t mean a subdirectory. It points to the set of files, starting with the sea prefix. Because the cube maps in Babylon.js consist of a prefix (in our case sea) and the maps position, which are (px,nx,py,ny,pz,nz). Making a long story short, these shortcuts mean – positive x, negative x, positive y, negative y, positive z and negative z. So our cube maps in the images/seabox folder should be named sea_px.jpg, sea_nx.jpg,  sea_py.jpg, sea_ny.jpg, sea_pz.jpg and sea_nz.jpg. Yes, only JPEG files can be used as textures of the cube maps in Babylon.js. Also an important thing is to set the coordinatesMode for the reflection texture by setting it to BABYLON.Texture.SKYBOX_MODE. The last things to set are the diffuseColor and the specularColor. And of course assign the skyboxMaterial to the skybox. You can also change the skybox position with the x, y, z parameters, relative to the scenes’ center. Also to create the skybox cube maps, please consider using any graphic creation suite and then the CubeTheSphere program, which was mentioned in one of our earlier articles about SceneJS.

The sea floor

In our example application we have created the sea floor using a mesh, generated at run time from a height map (ground.jpg) – a grayscale image. Like with the skybox, it is very easy to create an image based height mesh. You just need to implement the following code.

 

[…]

// Create the ground for our sea floor.
   var groundMaterial = new BABYLON.StandardMaterial("groundTex ", scene);                                 
       groundMaterial.diffuseTexture = new BABYLON.Texture("images/floor.png", scene);

// generate a mesh using a loaded from JPG file height map
   var ground = BABYLON.Mesh.CreateGroundFromHeightMap("groundHeight ", 
                                                       "images/ground.jpg", 
                                                        100, 100, 250, 0, 10, 
                                                        scene, false);
       ground.material = groundMaterial;
	    	
       ground.position.y = -48;

[…]

 

Of course first we create a groundMaterial, which is a BABYLON.StandardMaterial, so our sea floor will have a texture. To generate height on a mesh using an image, you need to use the BABYLON.Mesh.CreateGroundFromHeightMap() function and provide it with appropriate parameters. The first one is the name of the mesh inside the rendering engine. The second one is the path leading to the JPG file from which the height data of the generated mesh will be taken. The third and fourth parameters are the width and height of the mesh in Babylon.js units. The fifth parameter is the number of the subdivisions the mesh will have (how much vertices the mesh will have). The sixth and seventh parameters are the minimum and maximum height of the generated mesh height, measured in Babylon.js units. The eighth is the scene to which it should be added. The ninth parameter, which we have actually omitted, is a Boolean value, saying if the mesh can be updated dynamically.

 

The animated sprite sheets (the diver, seaweeds and fish)

Our example application contains several sprites. Sprites are important in creating 3D worlds. They are animations placed on meshes and always turned towards the camera. In 3D engines they are mainly used for generating objects which, if substituted by 3D models would be burdening the processor of the device. By using sprites, we can just like in our example application create lots of seaweeds. If we would try to make the seaweeds with animated 3D meshes then we probably would have a major performance issue on a mobile device. As the process of creating the diver, fish and the seaweeds in the sample application is quite similar, we will show you how to create only the diver animated sprite in the example below.

 

[…]

// Create a sprite manager for our diver
   var spriteManagerDiver = new BABYLON.SpriteManager("diverMgr", 
                                                      "images/diverSprite.png", 
                                                       1, 512, scene);

       mainCharacter = new BABYLON.Sprite("diver", spriteManagerDiver);
       mainCharacter.position.y = -2;
       mainCharacter.position.z = -0.5;
       mainCharacter.size = 6;
       mainCharacter.renderingGroupId = 0;
       mainCharacter.playAnimation(0, 13, true, 100);    	

[…]

 

Before you can create any sprite in Babylon.js, you must create a 2D animation, which should be exported to a sprite sheet. The sprite sheets’ single frame width and height must be the power of 2. For example the single animation frame of our diver has 256 pixels of width and 256 pixels of height. To create a sprite sheet from an animation you can use for example the Adobe Flash CC program or Texture Packer or one of many on-line tools like the CSS Sprite Sheet Generator.

Having done that you can copy the sprite sheet to your Tizen Project images folder and create an animated sprite in your Tizen application. First you need to create a BABYLON.SpriteManager(). This object will manage all the settings of our sprite. In the parameters you need to provide a string with the sprite managers’ name. For the next variable you must provide the path to the sprite sheet of your animation, then the number of instances of this sprite that will be rendered on the scene. Next we have the single frame animation size. In our diver animation it has the size of 512x512. The last parameter needs to get the scene object, on which it is going to be rendered.

After the creation of the BABYLON.SpriteManager(), we need to create the diver sprite itself. As seen in the code above, this is easily done by creating a BABYLON.Sprite() object. It takes two parameters – the name of the sprite (string) and a second parameter, which is the earlier created sprite manager.

You can also easily position the sprite in the 3D world using the position.x, position.y and position.z variables. Also changing the size of the sprite is made with ease by changing the size parameter (1 is default). There is also the possibility to change the rendering order of the sprite by changing the renderingGroupId parameter. Finally you can play the animation of the sprite by invoking on it the playAnimation() method. It takes four parameters.  First one is the first frame we want start playing from and the second is the last frame of the played animation. The third one is a Boolean flag, which indicates if the animation should loop or not. The last parameter is responsible for setting up the time between frames in milliseconds (speed of the animation playback).

 

The particle system

As mentioned in the beginning of this article, the bubbles in the sample application attached to this article are made with the Babylon.js internal particle emitters. They look pretty neat, so we are going to explain how to create a particle emitter for your 3D world in Babylon.js.

The code from the snippet below, at first sight maybe looks a bit complicated, but it is quite easy to understand and implement your own particle emitter in Babylon.js. First, you create the BABYLON.ParticleSystem() object. You pass in the parameters, starting with the particle emitter name, the total number of particles this emitter can have and ending with passing the scene object.

The next step is to get the texture for the particles. You can accomplish that by passing to the particleTexture variable a BABYLON.Texture() object.

 You need to provide the path to the texture and the scene object. It is very important to set up the emitter object for the particles. In our case it is the sphere1 object.

What also matters is the size of the particle emitter box. You can set its size easily with the minEmitBox and maxEmitBox parameters. Also with the minLifeTime and maxLifeTime parameters you can toggle the life time of the particles.

The emitRate tells the emitter how many particles you want to emit in one time unit.

You can also change the blending mode of the particles. BLENDMODE_ONEONE will blend the particles without using the alpha channel in the final result of blending. This is the default blend mode in Babylon.js for particles. If you want the alpha channel of the particles to affect the final visual outcome of the particles, then use BLENDMODE_STANDARD instead.

You can also provide a gravity vector for the emitter.

What is worth mentioning - the direction1 and direction2 parameters are two vectors between which the particles will be randomly flowing. They will create a kind of angle of the particle flow.

The minAngularSpeed and maxAngularSpeed are values that need radian values for the z axis rotation of the particles.

Lastly minEmitPower and maxEmitPower tell the emitter about the variance of power in randomly emitting the particles, while the updateSpeed parameter tells the emitter how fast the particles should flow.

At the and you must start the particle system by invoking on the particle system object the start() function.

As you can see, configuring the particle system in Babylon.js is very easy. It needs a bit of input, but all in all it is really friendly for the developers trying to build something in a 3D engine for the first time.

 

[…]

        var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);

	    // we need to set the texture of each particle
	    particleSystem.particleTexture = new BABYLON.Texture("images/bubble.png", scene);

	    // now is the time to set the bounds for the particles
	    particleSystem.emitter = sphere1; // specify the emitter
	    particleSystem.minEmitBox = new BABYLON.Vector3(-1, 1, 0); // Start
	    particleSystem.maxEmitBox = new BABYLON.Vector3(1, 2, 0); // End

	    // Colors of the emitted particles
	    particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0);
	    particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0);
	    particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0);

	    // the size of each particle
	    particleSystem.minSize = 0.3;
	    particleSystem.maxSize = 0.8;

	    // the life time of each particle
	    particleSystem.minLifeTime = 0.3;
	    particleSystem.maxLifeTime = 1.5;

	    // the emission rate of the particles
	    particleSystem.emitRate = 50;

	    // Blend mode : BLENDMODE_ONEONE, or BLENDMODE_STANDARD
	    particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;

	    // we need to set the gravity of all the particles
	    particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);

	    // the direction of each particle after it has been emitted
	    particleSystem.direction1 = new BABYLON.Vector3(0, 15, 0);
	    particleSystem.direction2 = new BABYLON.Vector3(0, 15, 0);
	    
	    // Angular speed set in radians
	    particleSystem.minAngularSpeed = 0;
	    particleSystem.maxAngularSpeed = Math.PI;

	    // Speed of the particles
	    particleSystem.minEmitPower = 1;
	    particleSystem.maxEmitPower = 3;
	    particleSystem.updateSpeed = 0.005;

	    // Initialize the particle system
	    particleSystem.start();

[…]

 

Importing external 3D objects

The last topic we want to cover in this article is importing external 3D meshes into the Babylon.js engine. The best way to do it is to use the Blender 3D suite to create your own meshes. Then using the Babylon exporter plugin export the mesh or the whole scene to the *.babylon file format.

To import a 3D mesh to Babylon.js (like we did with our statue of the Lumber Jack) you just need to write code like the one in the snippet below.

 

[…]

var lumberMan;

BABYLON.SceneLoader.ImportMesh("", "3d/", "drwal.babylon", scene, function (newMeshes) {

    lumberMan = newMeshes[0];

});

[…]

 

The BABYLON.SceneLoader.ImportMesh() is very easy to use. You need to leave the first parameter as an empty string or the model won't load! Next, the folder in which the 3D model resides. Then the model name itself and finally you need to create a callback function in which you will get the callback functions’ argument. The argument holds an array with the loaded meshes from the *.babylon file. In the callback functions’ body you can change the parameters of the loaded meshes, by for example changing their position, rotation, scaling etc. It is all up to you what you will do with your loaded 3D meshes at that stage.

 

Summary

In this article we have showed you how to create the basic elements of a 3D world with the Babylon.js engine in Tizen. You got the knowledge how to create a skybox. How to make a mesh based on a height map. We have introduced to you the sprite concept in the 3D space. You could also read about creating and configuring particle emitters in Babylon.js. And finally we have showed how to load an external 3D mesh into Babylon.js.

We hope that this article will broaden your knowledge about usage of the 3D engines. Or if you’re a newcomer to the 3D field, then it will encourage you to look for more articles and tutorials regarding this fascinating topic. Please also look into the BABYLONJS sample application attached to this article and play around with its code to get an in depth knowledge about this 3D engine.

If you want to read more about Babylon.js please visit its official site and read the official documentation.

文件附件: 
List
SDK Version Since: 
2.3.0