2016 s’annonce comme une année phare pour la réalité virtuelle. De nombreux produits de consommation autour de la réalité virtuelle sortiront sur le marché et beaucoup d’entreprises de logiciel s’apprêtent à prendre en charge ces nouveaux appareils. Le Web n’est pas en reste avec ce nouveau medium et les fournisseurs de navigateur s’affairent également. La croissance de WebVR s’est concentrée sur la création d’outils incroyables de visualisation et d’outils de création pour le contenu de réalité virtuelle en ligne.

WebVR Showcase

L’équipe de Mozilla qui s’occupe de la réalité virtuelle a travaillé d’arrache-pied afin que Firefox permette de créer du contenu virtuel en ligne et permette de le consulter. Cette semaine (NdT : la semaine du premier mars 2016) est un jalon pour WebVR. Grâce au travail réalisé avec Brandon Jones, qui fait partie de l’équipe de Google Chrome, l’équipe Mozilla est fière d’annoncer la version 1.0 de la proposition de spécification pour l’API WebVR.

Les récentes avancées en réalité virtuelle et les retours de la communauté nous ont permis d’améliorer l’API afin de répondre aux besoins des développeurs.

Certaines de ces améliorations concernent :

  • La gestion de l’affichage pour les appareils de réalité virtuelle
  • La capacité à circuler avec les liens entre différentes pages WebVR
  • Un schéma pour gérer les entrées des manettes à 6 degrés de libertés (aussi appelées 6DoF pour six degrees of freedom)
  • La gestion des situations, qu’on soit assis ou debout
  • La pertinence de l’utilisation dans le cadre mobile ou dans le cadre d’un bureau

Nous sommes ravis de pouvoir partager ces améliorations. La liste précédente ne représente qu’une petite partie de ce qui a été changé. Pour connaître tous les détails, vous pouvez lire le brouillon de la spécification et consulter le billet de Brandon.

Dans cet article, nous nous concentrerons sur une utilisation simple de l’API proposée. Quelques prérequis sont nécessaires comme des connaissances sur les mathématiques appliquées aux matrices. Vous pouvez aussi choisir de consulter A-Frame et le modèle de base (boilerplate) WebVR qui sont tous les deux construits sur cette API.

Avant d’aller plus loin, nous souhaiterions particulièrement remercier Chris Van Wiemeersch (Mozilla), Kearwood “Kip” Gilbert (Mozilla), Brandon Jones (Google) et Justin Rogers (Microsoft) qui ont contribué à la création de cette spécification.

Feuille de route pour l’implémentation

Nous prévoyons de livrer une implémentation stable de cette première version de l’API dans Firefox Nightly au cours du premier semestre. Vous pouvez suivre Bugzilla pour avoir accès à tous les détails. Vous pouvez aussi suivre les nouveautés liées à la prise en charge des plateformes sur iswebvrready.org.

Envie de commencer aujourd’hui ? Les développeurs peuvent faire des tests en utilisant l’implémentation expérimentale de l’API, développée par Brandon Jones, présente sur des versions de Chromium spécifiquement compilées par Brandon.

three.js et WebVR Polyfill (utilisée pour WebVR Boilerplate) sont tous les deux ouverts aux poules requêtes afin de prendre en charge les dernières API.

Les différents composants contribuant à la réalité virtuelle

Voyons les différents composants qui jouent un rôle :

  • L’appareil de réalité virtuelle sur lequel est affiché le contenu
  • La situation de l’utilisateur ; l’orientation et la position du casque dans l’espace
  • Les paramètres liés aux yeux qui définissent le champ de vision et la séparation stéréo.

Voici le processus utilisé pour afficher du contenu dans un casque :

  • navigator.getVRDisplays() récupère un objet d’affichage pour la réalité virtuelle
  • On crée un élément <canvas> qui sera utilisé pour afficher le contenu
  • On utilise VRDisplay.requestPresent() pour fournir le canevas
  • On crée la boucle d’animation spécifique à l’appareil. Celle-ci sera utilisée pour afficher le contenu.
    • VRDisplay.getPose() met à jour les informations liées à la situation de l’utilisateur.
    • On effectue les calculs et l’affichage
    • On utilise VRDisplay.submitFrame() pour indiquer lorsque le contenu du canevas est prêt à être présenté sur l’appareil.

Les sections qui suivent décrivent chacune de ces actions dans de plus amples détails.

Travailler avec des appareils de réalité virtuelle

Les appareils affichant des scènes de réalité virtuelle doivent respecter des conditions particulières pour le taux de rafraichissement, le champ de vision et la présentation du contenu. Ces conditions sont donc gérées différemment des affichages classiques d’ordinateur.

Lister les affichages de réalité virtuelle

Afin de récupérer les affichages du navigateur qui peuvent être utilisés pour la réalité virtuelle, on utilisera la méthode navigator.getVRDisplays() qui renvoie une promesse (Promise) qui se résout en un tableau d’objets VRDisplay.

navigator.getVRDisplays().then(function (displays) {
  if (!displays.length) {
    // WebVR est pris en charge, aucun affichage VRDisplays n'est trouvé
    return;
  }

  // On gère les objets VRDisplay. (on utilise ici une variable globale
  // au cas où on voudrait réutiliser l'objet autre part)
  vrDisplay = displays.length[0];
}).catch(function (err) {
  console.error("Impossible d'obtenir des affichages VRDisplays", err.stack);
});

Quelques éléments à garder en tête :

  • Le casque de réalité virtuelle doit être branché et allumé afin que les appareils puissent être listés
  • Si vous ne disposez pas d’un casque de réalité virtuelle, vous pouvez simuler un appareil en utilisant about:config et en activant dom.vr.cardboard.enabled avec la valeur true
  • L’appareil Cardboard VR (utilisé avec Google Cardboard) pourra être listé pour les utilisateurs de Firefox Nightly pour Android ou de Firefox pour iOS.

Créer une cible pour le rendu

Pour déterminer la taille de la cible pour le rendu (c’est-à-dire la taille du canevas), on créera une cible de rendu suffisamment grande pour contenir les zones d’affichage (viewports) des deux yeux. Pour obtenir la taille (exprimée en pixels) de chaque œil, on pourra utiliser :

// On utilisera 'left' pour l'œil gauche ou 'right' pour l'œil droit.
var eyeParameter = vrDisplay.getEyeParameters('left');

var width = eyeParameter.renderWidth;
var height = eyeParameter.renderHeight;

Afficher le contenu dans le casque

Pour afficher du contenu dans le casque, vous aurez besoin d’utiliser la méthode VRDisplay.requestPresent(). Cette méthode prend comme paramètre un élément WebGL <canvas> qui représente la surface à afficher pour la vision. Afin de vérifier qu’il n’y a pas d’abus, le navigateur requiert un événement de la part de l’utilisateur pour passer une première fois en mode réalité virtuelle. Autrement dit, l’utilisateur doit choisir d’activer le mode réalité virtuelle. Pour cela nous plaçons un bouton « Passez en réalité virtuelle » et utilisons un gestionnaire d’événement pour le clic sur ce bouton.

// Sélectionner l'élement WebGL canvas à partir du document
var webglCanvas = document.querySelector('#webglcanvas');
var enterVRBtn = document.querySelector('#entervr');

enterVRBtn.addEventListener('click', function () {
  // Demande la présence du canevas WebGL sur l'affichage de réalité virtuelle
  vrDisplay.requestPresent({source: webglCanvas});
});

// Pour finir la session et arrêter de présenter du contenu dans le casque.
vrDisplay.exitPresent();

Un requestAnimationFrame pour chaque appareil

Maintenant que le rendu cible est configuré et qu’on connaît les paramètres nécessaires pour le rendu et la présentation de la vue dans le casque, nous pouvons créer une boucle de rendu pour la scène.

Nous allons faire cela avec taux de rafraîchissement optimisé pour l’écran de réalité virtuelle. Nous utiliserons la fonction de rappel (callback) VRDisplay.requestAnimationFrame :

var id = VRDisplay.requestAnimationFrame(onAnimationFrame);

function onAnimationFrame () {
  // Boucle de rendu.
  id = VRDisplay.requestAnimationFrame(onAnimationFrame);
}

// Pour quitter la boucle d'animation
VRDisplay.cancelRequestAnimationFrame(id);

Ce fonctionnement est analogue à celui qu’on a avec la fonction de rappel standard window.requestAnimationFrame que vous connaissez sûrement. Nous utilisons cette fonction pour appliquer des mises à jour de position, d’orientation et également pour rendre le contenu sur l’affichage de réalité virtuelle.

Récupérer les informations de pose pour l’affichage de réalité virtuelle

Nous avons besoins de récupérer l’orientation et la position du casque utilisant la méthode VRDisplay.getPose() :

var pose = vrDisplay.getPose();

// Renvoie un quaternion.
var orientation = pose.orientation;

// Renvoie un vecteur à trois composantes
// qui décrit la position absolue.
var position = pose.position;

Notes :

  • L’orientation et la position renvoyés valent null s’ils ne peuvent être déterminés.
  • Voir la spécification sur VRStageCapabilities et VRPose pour plus d’informations.

Projeter une scène sur un affichage de réalité virtuelle

Pour un rendu stéréoscopique de bonne qualité, nous avons besoin d’informations sur les yeux, tels que l’offset (la distance entre les deux pupilles, aussi appelée IPD) et le champ de vision (FOV pour field of vision).

// On passe 'left' ou 'right' selon l'œil visé.
var eyeParameters = vrDisplay.getEyeParameters('left');

// Après avoir traduit les coordonnées grâce à VRPose,
// on les décale avec le décalage de l'œil
var eyeOffset = eyeParameters.offset;

// Ensuite on projette le contenu grâce à une matrice.
var eyeMatrix = makeProjectionMatrix(vrDisplay, eyeParameters);

// On applique la matrice eyeMatrix à notre vue.
// ...


/**
 * On génère une matrice de projection
 * @param {object} display - VRDisplay
 * @param {number} eye - VREyeParameters
 * @returns {Float32Array} 4×4 la matrice de projection
 */
function makeProjectionMatrix (display, eye) {
  var d2r = Math.PI / 180.0;
  var upTan = Math.tan(eye.fieldOfView.upDegrees * d2r);
  var downTan = Math.tan(eye.fieldOfView.leftDegrees * d2r);
  var rightTan = Math.tan(eye.fieldOfView.rightDegrees * d2r);
  var leftTan = Math.tan(eye.fieldOfView.leftDegrees * d2r);
  var xScale = 2.0 / (leftTan + rightTan);
  var yScale = 2.0 / (upTan + downTan);

  var out = new Float32Array(16);
  out[0] = xScale;
  out[1] = 0.0;
  out[2] = 0.0;
  out[3] = 0.0;

  out[4] = 0.0;
  out[5] = yScale;
  out[6] = 0.0;
  out[7] = 0.0;

  out[8] = -((leftTan - rightTan) * xScale * 0.5);
  out[9] = (upTan - downTan) * yScale * 0.5;
  out[10] = -(display.depthNear + display.depthFar) / (display.depthFar - display.depthNear);

  out[12] = 0.0;
  out[13] = 0.0;
  out[14] = -(2.0 * display.depthFar * display.depthNear) / (display.depthFar - display.depthNear);
  out[15] = 0.0;

  return out;
}

Envoyer les images au casque

La réalité virtuelle est optimisée pour réduire le temps entre le mouvement de l’utilisateur et l’affichage de la réaction dans le casque. Cette réactivité est très importante afin d’obtenir une expérience confortable (et qui ne donnera pas le mal de mer). Pour que cela soit possible, on pourra utiliser les méthodes VRDisplay.getPose() et VRDisplay.submitFrame().

// Ici on traite les opérations de rendu et les calculs qui ne dépendent pas de la pose
// ....

var pose = vrDisplay.getPose();

// Ici les opérations de rendu et de calcul qui dépendent de la position du casque.
// C'est ici qu'il faut appliquer les matrices de l'œil.
// On essaiera de minimiser les opérations effectuées ici.

vrDisplay.submitFrame(pose); 

// Toutes les opérations effectuées sur la frame après l'envoi n'ont aucun impact sur la latence.
// C'est donc un endroit utile pour générer une autre vue (telle qu'un reflet).
// ...

La règle générale consiste à appeler VRDisplay.getPose() aussi tard que possible et VRDisplay.submitFrame() aussi tôt que possible.

Démonstration, retours et ressources

Vous cherchez une façon de commencer ? Voici une liste d’exemples qui utilisent l’API 1.0 de WebVR. N’hésitez pas à regarder les ressources listées ci-dessous.

Surtout, n’hésitez pas à faire vos retours !

Le développement de cette proposition d’API est une réponse directe à l’évolution des technologies liées à la réalité virtuelle, aux retours et aux discussions que nous avons eues avec la communauté. Cette étape représente un bon début, continuez à nous faire vos retours pour poursuivre ces améliorations !

Nous vous invitons à renseigner les issues et à proposer des pull requests sur le dépôt GitHub de la spécification WebVR.

Quelques ressources

À propos de Casey Yee

Casey travaille au sein de l’équipe WebVR de Mozilla et mène des recherches autour des technologies web liées à la réalité virtuelle haute performance.