Visualisation de points 3D

Comment visualiser en JavaScript des points 3D avec three.js et babylon.js. Une comparaison de ces 2 librairies 3D.

Vincent Roth

Architecte

Vincent Roth
Visualisation de points 3D

23 oct. 2020

Admettons que nous avons à disposition un ensemble de points avec des coordonnées 3D en X, Y et Z. Cet ensemble de points pourrait avoir été généré aléatoirement. Comment visualiser un tel ensemble ?

En JavaScript, il est possible de réaliser des visualisations 3D avec l'élément <canvas> et le contexte WebGL. Problème, il s'agit d'une API bas niveau. Solution, des librairies nous permettent de manipuler un contexte 3D assez simplement, parmi les plus connues three.js et babylon.js.

Pour ces 2 librairies, les concepts utilisés sont assez proches. Pour toutes deux, nous aurons besoin :

  • d'un moteur de rendu 3D ;
  • d'une scène dans laquelle sera rendu les points ;
  • d'une lumière pour que les points soient visibles ;
  • d'une caméra pour définir le point de vue de la scène ;
  • de contrôles de la caméra pour se déplacer dans la scène ;
  • d'un objet représentant en 3D l'ensemble de point ;
  • et la boucle de rendu 3D, notamment pour actualiser la caméra suite aux déplacement.

Nous allons voir maintenant ce que cela donne pour chaque librairie.

La donnée d'origine de l'ensemble de points est sous la forme d'un tableau de coordonnées structurées. Dans les exemples de code, la variable data fera référence à cette donnée.

// Type de l'ensemble de points
{x: number; y: number; z: number}[]

En fonction de la taille de notre ensemble de points, en terme de coordonnées maximales, il faudra éventuellement modifier la position et l'échelle de l'objet 3D rendu par la librairie. Ceci n'est pas inclus dans les exemples de code.

three.js

Three.js propose un objet Points pour représenter un ensemble de points. Cependant cet objet attend une géométrie composée de sommets formant des triangles. Cette structure de géométrie attendue nous oblige à transformer notre donnée d'origine. Aussi, la taille et la couleur des points sont les mêmes pour tous les points.

Autre remarque sur three.js, les contrôles de la caméra ne sont pas gérés par la librairie elle-même, il faut inclure du code pour ce faire. Heureusement des exemples de contrôles existent, tel que OrbitControl qui est utilisé ici.

// Obtention du canvas existant dans le DOM
const canvas = document.getElementById("points-visualisation");

// Initialisation du moteur de rendu 3D
const renderer = new THREE.WebGLRenderer({ canvas });

// Définition de la scène
const scene = new THREE.Scene();
// Définition d'une lumière ambiante
scene.add(new THREE.AmbientLight(0x404040));

// Définition de la caméra
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = -5;
camera.rotation.x = Math.PI;

// Ajout de contrôle de la caméra, à définir soi même
// De l'exemple https://threejs.org/docs/#examples/en/controls/OrbitControls
const controls = new THREE.OrbitControls(camera);

// Admettons que la variable 'data' existe
// Extraction des coordonnées sous forme de tableau représentant un sommet
const vertices = data.map((point) => [point.x, point.y, point.z])

// Typage du tableau de sommets, nécessaire pour three.js
const vertices = new Float32Array(xyz.length * 3);
xyz.forEach((point, index) => {
  vertices[index * 3] = point[0];
  vertices[index * 3 + 1] = point[1];
  vertices[index * 3 + 2] = point[2];
});

// Construction de la géométrie
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

// Définition de la couleur et de la taille des points
const material = new THREE.PointsMaterial({ color: 0xffffff, size: 0.25 });
// Construction de l'ensemble de points
const mesh = new THREE.Points(geometry, material);
scene.add(mesh);

// Boucle de rendu
// Nécessaire pour les mouvements de la caméra
function animationLoop() {
  renderer.render(scene, camera);
  requestAnimationFrame(animationLoop);
}
animationLoop();

Voir la démo three.js.

babylon.js

Babylon.js, de son coté, propose un objet PointsCloudSystem pour représenter un ensemble de points. On peut lui ajouter un nombre de point défini, et pour chaque point préciser sa position et sa couleur. À contrario de three.js, les points ne sont pas considérés comme une géométrie, mais comme des particules.

// Obtention du canvas existant dans le DOM
const canvas = document.getElementById("points-visualisation");

// Initialisation du moteur de rendu 3D
const engine = new BABYLON.Engine(canvas, true, {
  preserveDrawingBuffer: true,
  stencil: true,
});

// Définition de la scène
const scene = new BABYLON.Scene(engine);

// Définition de la caméra
const camera = new BABYLON.FreeCamera(
  "camera",
  new BABYLON.Vector3(0, 0, -5),
  scene
);
camera.setTarget(BABYLON.Vector3.Zero());
// La caméra écoute les évènements
camera.attachControl(canvas, false);

// Light
const light = new BABYLON.HemisphericLight(
  "light",
  new BABYLON.Vector3(0, 0, 1),
  scene
);

// Objet contenant l'ensemble de points
// Le 2nd paramètre précise la taille des points
const pcs = new BABYLON.PointsCloudSystem("pcs", 1, scene);

// Admettons que la variable 'data' existe sous la forme d'un tableau
pcs.addPoints(data.length, (particle, index) => {
  const point = data[index];
  // Définition de la position de chaque point
  particle.position = new BABYLON.Vector3(point.x, point.y, point.z);
  // Définition de la couleur de chaque point, ici blanche
  particle.color = new BABYLON.Color4(1, 1, 1, 1);
});

// Construction du modèle 3D de l'ensemble de point
pcs.buildMeshAsync().then(() => {
  pcs.mesh.position = BABYLON.Vector3.Zero();
});

// Boucle de rendu
// Nécessaire pour les mouvements de la caméra
engine.runRenderLoop(function () {
  scene.render();
});

Voir la démo babylon.js.

Conclusion

Three.js et babylon.js permettent tout deux de visualiser des points. Nous les avons poussé jusqu'à 1.6 million (36 à la puissance 4) de points et la page répond rapidement.

Ensuite, d'un avis personnel, j'ai préféré la documentation de babylon.js à celle de three.js, car je la trouve plus accessible et riche. Sur le long terme, au niveau de la gestion des versions, babylon.js semble plus fiable que three.js, qui lui est très populaire.