Premier pas en 3D avec WebGL et Babylon.js

Faire de la 3D directement dans son navigateur sans aucun plugin est possible grâce à WebGL. Je vous propose de faire un peu de JavaScript et par la même occasion de créer une scène en 3D avec le moteur Babylonjs. L’avantage de Babylonjs est de proposer par défaut un support de tous les navigateurs récents, y compris Internet Explorer 11, il prend aussi en charge les écrans tactiles. Nous allons créer ensemble une première scène 3D très simple où on pourra se déplacer avec les touches du clavier et la souris. Les collisions seront aussi de la partie (non ne partez pas 8D on utilise Babylon.js).

Ce que nous aurons fait à la fin de ce tutoriel
Ce que nous aurons fait à la fin de ce tutoriel

1. Création de la structure HTML de l’application

La première chose à faire est de créer la structure HTML/CSS de l’application avec les éléments nécessaires, à savoir une balise canvas et l’inclusion des scripts qui vont bien. Rendez vous sur la page github du projet Babylonjs pour y télécharger les sources (bouton download à droite) ou alors récupérez simplement le contenu du fichier Babylon-{version}.js et copiez son contenu dans un fichier « babylon.js ». Vous n’êtes pas obligé de récupérer hand.js pour ce tutoriel mais si vous voulez utiliser les fonctions tactiles il faudra penser à l’inclure. Enfin on ajoute un fichier JavaScript vide nommé demo.js qui contiendra notre code de démonstration. Vous pouvez de toute façon récupérer les sources à la fin de ce tutoriel.

<!doctype html>
<html lang="fr">
<head>
	<meta charset="utf-8" />
	<title>Demonixis.Net :: Babylon.js - Partie 1</title>
    <style>
		body, html {
			margin: 0; 
			padding: 0; 
			overflow: hidden;
			width: 100%;
			height: 100%;
		}
		canvas {
			width: 100%;
			height: 100%;
		}	
	</style>
</head>
<body>
	<canvas id="renderCanvas"></canvas>
	<script src="js/babylon.1.6.js"></script>
	<script src="js/hand-1.1.3.js"></script>
	<script src="js/demo.js"></script>
	<script>
		runDemo("renderCanvas");
	</script>
</body>
</html>

La structure du projet est la suivante :

  1. Un dossier images où nous mettrons les textures
  2. Un dossier js pour les scripts
  3. Un fichier index.html avec le code de l’application

2. Premiers pas en 3D

Une scène en trois dimensions est composée de quelques éléments indispensables pour s’afficher correctement. Il faut typiquement un Renderer, une Camera et une Scene. Le Renderer permet d’afficher le contenu de la scène à l’écran, c’est le composant qui travail avec WebGL. La caméra est nécessaire pour indiquer au renderer comment afficher la scène  (type de perspective, angle de vision, position, etc…). Enfin la scène est un conteneur d’objets 3D comme des cubes ou des modèles 3D.
Ainsi pour afficher quelque chose à l’écran il faudra créer une scène, lui attacher des objets, créer une caméra et un Renderer.

'use strict';

function runDemo(canvasId) {
     var canvas = document.getElementById(canvasId);
     var engine = new BABYLON.Engine(canvas, true);

     // Création de la scène
     var scene = new BABYLON.Scene(engine);
     scene.gravity = new BABYLON.Vector3(0, -9.81, 0);    	                            scene.collisionsEnabled = true;

    // La suite plus bas
}

On récupère l’élément canvas du document index.html car Babylon en a besoin pour initialiser le moteur interne. La construction de la scène prend en paramètre le moteur créé juste au dessus. Vous remarquerez que j’ai déjà activé le moteur physique en indiquant le sens et la force de gravité et en activant la détection des collisions. Rassurez vous, la detection des collisions est faite automatiquement pour tous les objets enregistrés, vous n’avez rien à faire ! Passons à la caméra et au controlleur.

// Ajout d'une caméra et de son contrôleur
var camera = new BABYLON.FreeCamera("MainCamera", new BABYLON.Vector3(0, 2.5, 5), scene);
camera.applyGravity = true;
camera.checkCollisions = true;

camera.speed = 0.5;
camera.angularSensibility = 1000;

camera.keysUp = [90]; // Touche Z
camera.keysDown = [83]; // Touche S
camera.keysLeft = [81]; // Touche Q
camera.keysRight = [68]; // Touche D;
scene.activeCamera.attachControl(canvas);

Les objets dans Babylon doivent être nommés, le nom MainCamera est totalement arbitraire, nous aurions pu mettre « Hello », « Machin » ou encore « HelloMachin ». Nous créons une caméra de type FreeCamera qui contient déjà tout le code nécessaire au déplacement, celui-ci sera assuré avec les touches du clavier. Ce type de caméra est appelée « First Person » et c’est ce que l’on utilise pour créer un FPS (First Person Shooter) par exemple.

Encore une fois j’ai directement ajouté les paramètres permettant à la scène de savoir que la caméra doit être prise en compte dans les tests de collisions. Par défaut je trouve que la vitesse de déplacement est trop importante et c’est pour ça que j’ai changé les valeurs de vitesse « speed » et de sensibilité « angularSensibility« .

Le mappage par défaut utilise les touches directionnelles, cependant afin de faire « comme » un vrai FPS j’ai changé ce mappage par un ZQSD que nous connaissons tous bien. Vous remarquerez que nous passons des tableaux avec des keyCode (un key code est le code correspondant à une touche). Il est donc possible de passer plusieurs keyCode pour une même touche, ce qui est relativement pratique. Enfin il faut indiquer à la caméra active le canvas utilisé car toutes les détections d’événements souris en dépendent. Passons à l’éclairage si vous voulez bien.

// Ajout d'une lumière
var light = new BABYLON.PointLight("DirLight", new BABYLON.Vector3(0, 10, 0), scene);
light.diffuse = new BABYLON.Color3(1, 1, 1);
light.specular = new BABYLON.Color3(0.6, 0.6, 0.6);
light.intensity = 1.5;

Ici pour des raisons de simplicité et parce que ce sujet prendrais plus d’un article pour être traité correctement (n’ayez pas peur je vous assure, ça va très bien ce passer), j’ai choisis de ne mettre qu’une lumière de type Point. Elle est positionnée au milieu de la scène, sa couleur est blanche et son intensité de 1.5. Je vous laisse changer ses paramètres et observer les changements. En réalité c’est très facile d’ajouter d’autres types d’éclairage, mais il faut bien garder en tête que c’est consommateur et qu’un bon réglage de l’éclairage est très compliqué. Bon pour le moment nous n’avons toujours rien d’affiché à l’écran… Vous voulez un écran bleu ?

function runDemo(canvasId) {

    // -----------------	
    // Le code avant
    // -----------------

    // Lancement de la boucle principale
    engine.runRenderLoop(function() {
        scene.render();
     });
}

Ce code met en place la boucle infinie de rendu, à chaque tour de boucle la scène est rendu à l’écran. Sachez que cette boucle est optimisée pour afficher au maximum 60 images par secondes.

3. Créons une scène

Il nous reste deux concepts à voir pour être opérationnel, les meshes et les materials ! Un mesh (maillage en Français) est un objet contenant toutes les informations sur la position des différents points d’un modèle en trois dimensions. Dans un mesh on retrouve les informations relatives aux vertex, normal, face, uv, etc..
Une material (certains disent « un » material) est l’habillage du mesh. On y trouve tout un tas d’informations pratiques comme la ou les textures utilisées, les comportements de l’objet face à la lumière, etc… Le truc est donc de créer une material (je pense à Materia quand j’écris ça pas vous ?) et de lui attacher une texture. Après on créera un mesh simple et on lui attachera cette material pour qu’il ai un habillage visuel car sans ça ce n’est qu’un tas de points dans l’espace.

function createDemoScene(scene) {
    // Création d'un sol
    var ground = BABYLON.Mesh.CreatePlane("ground", 50, scene);
    ground.rotation.x = Math.PI / 2;
    ground.material = new BABYLON.StandardMaterial("gMaterial", scene);
    ground.material.diffuseTexture = new BABYLON.Texture("images/ground.png", scene);
    ground.checkCollisions = true;

    // La suite dans 5 minutes :)
}

Ici on créé une sol avec un mesh de type Plane. Par défaut la rotation du plane n’est pas bonne pour l’utiliser en tant que sol (il est face à nous), il faut donc changer sa rotation sur l’axe X avec la propriété qui va bien. Vous noterez que TOUTES les rotations sont exprimées en radian (ce qui est normal après tout). On lui attache une material avec une texture et enfin, on indique à la scène que cette objet doit être prit en compte dans les detections de collisions. Là si vous faite un rafraîchissement de la page vous devriez avoir quelque chose, cool non ?

// Et quelques cubes...
var boxMaterial = new BABYLON.StandardMaterial("bMaterial", scene);
boxMaterial.diffuseTexture = new BABYLON.Texture("images/box.png", scene);

var positions = [
	{ x: -15, z: 15 },
	{ x: -15, z: -15 },
	{ x: 15, z: 15 },
	{ x: 15, z: -15 }
];

var cubeSize = 2.5;

for (var i = 0; i < 4; i++) {
	  var box = BABYLON.Mesh.CreateBox("box1", cubeSize, scene);
	  box.position = new BABYLON.Vector3(positions[i].x, cubeSize / 2, positions[i].z);
	  box.material = boxMaterial;
	  box.checkCollisions = true;
}

Un mesh se créé avec la méthode statique BABYLON.Mesh.Create{Primitive} ou {Primitive} peut être Plane, Box, Cylinder, Cercle, Torus, etc… Comme vous en avez prit l’habitude on nomme l’objet avec un nom arbitraire, une spécifie une taille (suivant le type de primitive le nombre de paramètres peut varier) et on passe la scène en dernier. Le fait de passer la scène en paramètre y ajoute automatiquement le mesh. On renseigne la propriété material du mesh avec la material créée juste au dessus. Plusieurs meshes peuvent avoir la même material et c’est d’ailleurs conseillé afin d’économiser des ressources. Avant de testez ce code vous devrez appeler cette fonction juste avant la fonction qui exécute la boucle principale, ensuite après un rafraîchissement de la page vous pourrez admirer un sol et quatre caisses texturées. Le déplacement se fait avec les touches ZQSD et la souris (il faut cliquer pour déplacer la vue).

4. Ajouter une skybox

Une Skybox est un cube avec une texture par face permettant de simuler un ciel réaliste. Vous pouvez trouver quelques Skybox sur ce site.

function createSkybox(scene) {
  // Création d'une material
	 var sMaterial = new BABYLON.StandardMaterial("skyboxMaterial", scene);
	 sMaterial.backFaceCulling = false;
	 sMaterial.reflectionTexture = new BABYLON.CubeTexture("images/skybox/skybox", scene);
	 sMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;

	 // Création d'un cube avec la material adaptée
	 var skybox = BABYLON.Mesh.CreateBox("skybox", 250, scene);
	 skybox.material = sMaterial;
}

La fonction permettant de créer la skybox prend en paramétre la scène principale. Dans un premier temps nous créons une material standard avec une texture, mais pas n’importe quelle texture, une texture Cube (aussi appelée Texture 3D) ! La particularité de cette texture est qu’elle est composée de 6 textures 2D. Le mécanisme permettant de charger une Texture Cube dans Babylon est assez simple, vous devez avoir un dossier avec six images au format jpg et respectant la nomination suivante :

  1. skybox_px
  2. skybox_py
  3. skybox_pz
  4. skybox_nx
  5. skybox_ny
  6. skybox_nz

Sur certains site la nomination est différente, voilà comment renommer vos textures au besoin. Sachez que ça se fait très simplement en utilisant votre main gauche et ses trois doigts.

VecteurNormal

  1. Right => skybox_px
  2. Top => skybox_py
  3. Front => skybox_pz
  4. Left => skybox_nx
  5. Bottom => skybox_ny
  6. Back => skybox_nz

Une fois la material créée et la texture chargée (en asynchrone bien entendu) on créé un cube ayant une grande taille. Sa position par défaut est (0, 0, 0) et ça tombe bien car la scène aussi, ainsi la Skybox sera centrée sur la scène. Appelez la méthode createSkybox() juste avant la méthode createDemoScene() et admirez le résultat, pas mal hein ?

5. Conclusion

C’est le moment de faire un petit bilan, est-ce que nous avons fait des maths ? non ! Est-ce que nous avons fait de la physique ? non ! Babylon.js est un moteur de jeu vraiment complet et croyez moi nous n’avons pas vue grand chose. Le lead développeur du projet travail d’arrache pied pour que le moteur soit toujours plus stable et compétitif. Dernièrement nous avons eu l’ajout du support des bones ce qui permet d’avoir des personnages animés sur la scène. Si vous voulez vous lancer dans la 3D en JavaScript alors Babylon.js est certainement la solution idéale pour vous.

Il est compatible avec Internet Explorer 11 et peut être utiliser pour créer des applications modernes sous Windows 8 et RT ! Vous voulez votre FPS sur le Windows Store ? C’est désormais à votre portée. On se retrouve dans le prochain tutoriel : Un Micro FPS en JavaScript avec Babylonjs.

On termine par les liens qui vont bien :

  1. Le blog de David Catuhe (Lead developer)
  2. Le blog de David Rousset
  3. Le site officiel avec tous les exemples
  4. La page Github
  5. Les sources de ce tutoriel