Blog tech

De la 3D dans le navigateur avec BabylonJS

Rédigé par Arnaud Morisset | 3 janvier 2017

Avec l’avènement du HTML5 et la montée en puissance de WebGL, les applications 3D ont désormais leur place sur le web. Ubisoft, Microsoft, Google et d’autres grands noms de l’informatique placent beaucoup d’espoir dans WebGL et chaque jour voit la naissance de nouvelles expérimentations, voire d’un nouveau projet dans le domaine de la 3D web. Si vous avez déjà tenté l’expérience WebGL, vous vous êtes sûrement cassé les dents sur la complexité de cette technologie, c’est là qu’intervient BabylonJS.

Présentation de BabylonJS

BabylonJS est une bibliothèque JavaScript qui agit comme une surcouche à WebGL. Les programmes 3D sont souvent très verbeux, à l’image des programmes OpenGL ou Direct3D, car l’on discute directement avec le GPU. BabylonJS va nous faciliter la vie en encapsulant cette partie verbeuse dans un ensemble de classes et de méthodes. La bibliothèque a été créée par David Catuhe et David Rousset (deux ingénieurs spécialisés dans le web chez Microsoft) et présente des avantages indéniables pour le développement d’applications 3D dans le navigateur. Maintenant que j’ai piqué votre curiosité, je vous invite à créer votre première scène 3D.

Pour profiter de certaines fonctionnalités de BabylonJS, il faudra placer votre code sur un serveur web. Un serveur Apache en local fera parfaitement l’affaire.

Une première scène 3D

Nous allons faire simple pour cette première scène : un sol avec quelques cubes, quelques textures et la possibilité de s’y déplacer. Pour commencer, préparez l’architecture suivante :

.
+-- index.html
+-- js/
|   +-- app.js
+-- css/
|   +-- global.css
+-- img/

Le fichier index.html sera initialisé de la manière suivante (ce fichier ne changera pas lors de la suite du développement) :

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html" charset="utf-8" />
  <title>Babylon - My First 3D Scene</title>
  <!-- Pour simplifier, nous obtenons BabylonJS via un CDN -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babylonjs/2.4.1/babylon.js"></script>
  <script src="js/app.js"></script>
  <link href="css/global.css" rel="stylesheet"/>
</head>

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

</html>

Vous noterez la présence de la balise canvas, c’est grâce à cette dernière, entre autres, que ce genre de programme est possible. Nous passons donc par elle pour effectuer le rendu de l’application.

Le fichier global.css est là pour faire en sorte que notre application 3D occupe toute la place allouée au navigateur.

html, body {
  overflow: hidden;
  width   : 100%;
  height  : 100%;
  margin  : 0;
  padding : 0;
}

#renderCanvas {
  width   : 100%;
  height  : 100%;
}

C’est maintenant dans notre fichier app.js que tout va se passer. Premièrement, nous devons attendre que le DOM soit parfaitement chargé avant de faire quoi que ce soit :

window.addEventListener('DOMContentLoaded', function() {
  // ...
});

Maintenant, nous pouvons initialiser le moteur 3D. Ceci se fait en deux temps : on récupère le canvas puis on crée une instance du moteur en lui passant le canvas en paramètre.

const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true); // true est là pour activer/désactiver l'anti-aliasing

Le moteur est prêt, passons à la construction de la scène qui sera implémentée dans une fonction dédiée :

const createScene = function() {
  // ...
  return scene;
};

Une application 3D est composée de trois éléments fondamentaux :

  • Une caméra, qui offre un point de vue sur l’environnement 3D.
  • Une lumière, permettant de définir l’éclairage et la gestion des couleurs de la scène.
  • Une scène, elle joue le rôle de “conteneur” et va répertorier tous les objets 3D.

Commençons par la création du conteneur : la scène.

let scene = new BABYLON.Scene(engine);
scene.gravity = new BABYLON.Vector3(0, -9.81, 0);
scene.collisionsEnabled = true;

Cela vous donne déjà un aperçu de la simplicité de BabylonJS, la création de la scène est un simple appel au constructeur BABYLON.Scene() qui prend le moteur en paramètre. Pour appliquer une gravité semblable à la gravité terrestre, on déclare une direction négative sur l’axe Y via un Vector3. La dernière ligne va activer le moteur de collisions pour notre scène. Cette dernière prête, nous pouvons lui ajouter une caméra.

let 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]; // Z
camera.keysDown = [83]; // S
camera.keysLeft = [81]; // Q
camera.keysRight = [68]; // D
scene.activeCamera.attachControl(canvas);

Ce code représente également un bon exemple de simplicité, si vous êtes familier avec JavaScript ou la programmation orientée objet en général, tout ceci devrait être limpide. Après avoir instancié une caméra, nous lui indiquons qu’elle est sensible à la gravité ainsi qu’aux collisions. Nous définissons sa vitesse de déplacement, la sensibilité de la souris puis nous mappons les touches de contrôle sur les classiques du jeu vidéo : ZQSD. La dernière ligne permet d’appliquer tous ces contrôles afin que l’utilisateur puisse en profiter.

Si vous avez été attentif, vous avez dû vous rendre compte qu’il ne nous manque qu’un seul élément : la lumière.

let 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;

On distinguera plusieurs types de lumière, celle qui nous intéresse ici est la PointLight qui va simuler une source lumineuse similaire à celle du soleil. On définit une couleur pour les propriétés diffuse et specular afin d’avoir quelques reflets. On termine avec un ajustement de l’intensité de la lumière.

Nos trois éléments sont donc en place. Avant de pouvoir lancer l’application, ajoutez les instructions suivantes après la méthode createScene :

const scene = createScene();

engine.runRenderLoop(function() {
  scene.render();
});

window.addEventListener('resize', function() {
  engine.resize();
});

Nous avons donc une renderLoop chargée d’exécuter notre code le plus de fois possible par seconde, le Graal étant les 16ms entre chaque frame (le fameux 60 FPS). Vous pouvez maintenant lancer l’application dans votre navigateur. Je vous recommande Chrome et Edge qui sont les plus adaptés pour cette bibliothèque javascript (BabylonJS étant principalement testé avec Chackra et V8). Normalement, vous devriez voir une scène… vide. En effet pour l’instant vous n’avez placé aucun objet 3D dans votre scène, uniquement les fondations. Continuons en ajoutant un sol sur lequel nous pourrons nous déplacer.

let ground = BABYLON.Mesh.CreatePlane("ground", 50, scene);
ground.rotation.x = Math.PI / 2;
ground.checkCollisions = true;

On instancie un objet Plane que l’on va orienter de manière à simuler un sol. N’oublions pas de le rendre sensible aux collisions, le cas échéant notre caméra passera à travers. Si vous relancez votre application maintenant, vous pourrez désormais vous balader dans votre premier monde en 3D.

Ce niveau semble bien vide pour l’instant, ajoutons quelques caisses :

const cubeSize = 2;
for (var i = 0; i < 4; i++) {
  let box = BABYLON.Mesh.CreateBox("box", cubeSize, scene);
  const randomX = Math.floor(Math.random() * 31) - 15;
  const randomZ = Math.floor(Math.random() * 31) - 15;
  box.position = new BABYLON.Vector3(randomX, cubeSize / 2, randomZ);
  box.checkCollisions = true;
}

Et voici les premiers éléments de votre nouveau monde créé en 3D. Pour finaliser, je vous invite à ajouter quelques textures. Pour cela, il vous faut d’abord sélectionner des images. Un tour rapide sur votre navigateur favori et vous devriez trouver une image pour le sol et une pour les caisses.

En manque d’inspiration ? Faites donc un tour sur OpenGameArt.

Commençons par appliquer une texture au sol :

// La création de notre sol
let ground = BABYLON.Mesh.CreatePlane("ground", 50, scene);
ground.rotation.x = Math.PI / 2;
ground.checkCollisions = true
ground.material = new BABYLON.StandardMaterial("gMaterial", scene);

// Ajout d'une texture (on adapte sa taille pour un effet mosaïque).
ground.material.diffuseTexture = new BABYLON.Texture("img/ground.jpg", scene);
ground.material.diffuseTexture.uScale = 30;
ground.material.diffuseTexture.vScale = 30;

Pour les caisses, nous allons créer un objet Material commun à chaque box :

let boxMaterial = new BABYLON.StandardMaterial("bMaterial", scene);
boxMaterial.diffuseTexture = new BABYLON.Texture("img/box.jpg", scene);

Puis, nous retournons dans la boucle de création de boxes pour appliquer la texture :

for (var i = 0; i < 4; i++) {
  // ...
  box.material = boxMaterial;
}

Aperçu

Conclusion

Vous venez de faire vos premiers pas dans le monde de la 3D web et je vous en félicite ! Si vous poursuivez votre apprentissage, vous découvrirez rapidement que BabylonJS a beaucoup à offrir et est capable de réduire des notions complexes à de simples appels de méthodes. Si vous souhaitez avoir un aperçu des cas d’utilisation du moteur, vous pouvez consulter un showcase sur le site officiel.

L’équipe Synbioz. Libres d’être ensemble.