Afficher du texte dans WebVR

Dans les détails

Consulter le site

Dans (https://with.in/) se trouve une plate-forme de storytelling en réalité virtuelle. Ainsi, lorsque l'équipe a entendu parler de WebVR en 2015, nous nous sommes immédiatement intéressés à son potentiel. Aujourd'hui, cet intérêt se manifeste dans un sous-domaine unique de notre plate-forme Web, https://vr.with.in/. Toute personne disposant d'un navigateur compatible RV peut accéder au site, cliquer sur un bouton et mettre un casque pour se plonger dans notre gamme de films en réalité virtuelle.

Aujourd'hui, cela inclut, sans s'y limiter, Chrome sur Daydream View. Pour en savoir plus sur votre appareil et votre visiocasque, consultez la page https://webvr.info/.

Comme d'autres environnements de rendu spécifiques à la réalité virtuelle, le Web repose principalement sur une représentation en trois dimensions d'une scène. Cette scène comporte une caméra, votre perspective et un nombre illimité d'objets. Pour faciliter la gestion de cette scène, de la caméra et des objets, nous utilisons une bibliothèque appelée Three.js qui exploite l'élément <canvas> pour lancer le rendu sur le GPU de votre ordinateur. Il existe de nombreux modules complémentaires Three.js utiles pour rendre votre scène visible dans WebVR. Les deux principaux sont THREE.VREffect pour créer une fenêtre d'affichage pour chaque œil et THREE.VRControls pour traduire la perspective (par exemple, la rotation et la position de l'écran visé) de manière convaincante dans votre scène. Il existe de nombreux exemples de mise en œuvre. Pour vous lancer, consultez les exemples WebVR de Three.js.

En explorant WebVR plus en détail, nous avons rencontré un problème. Si nous regardons le contenu du Web, le texte en fait partie intégrante. Bien que la majorité de notre contenu soit basé sur une vidéo, si vous accédez à la section Dans le site, le contenu est placé au centre du contenu. L'interface utilisateur et les informations supplémentaires sur un film ou des films similaires sont tous créés avec du texte. De plus, tout ce texte est créé dans le DOM. Nos explorations WebVR et https://vr.with.in/ sont toutes disponibles dans <canvas>.

Texte utilisé dans WebVR Texte utilisé dans WebVR
Texte utilisé dans WebVR pour vr.with.in

Quelles sont mes options ?

Heureusement, nous mettons tout en œuvre pour rendre cela possible. En fait, nos recherches ont révélé plusieurs méthodes efficaces pour afficher du texte dans un environnement tridimensionnel sur un élément <canvas>. Vous trouverez ci-dessous une matrice de quelques-uns de ces points, avec leurs avantages et leurs inconvénients respectifs:

Indépendant de la résolution Caractéristiques typographiques Performances Facilité d'implémentation
Texte du canevas 2D Oui Oui Oui
Texte vectoriel tronqué Oui Oui
Texte 3D extrudé Oui
Texte bitmap du champ de distance signé Oui Oui Oui

Notre décision: police bitmap SDF

Le canevas 2D avec ctx.fillText() permet de renvoyer le texte à la ligne, d'espacer les lettres et de définir la hauteur des lignes, mais le dépassement est tronqué et le texte sera flou si vous faites un zoom avant trop important. Vous pouvez augmenter la taille de la texture du canevas, mais vous risquez d'atteindre une limite supérieure en termes de taille ou de performances si la texture est trop grande.

Le texte 3D extrudé est essentiellement identique au texte vectoriel triangulé, mais avec de la profondeur et éventuellement un biseau, il présente donc au moins deux fois plus de géométrie. L'une ou l'autre de ces méthodes peut fonctionner à faible dose pour les titres ou les logos, mais ne fonctionnerait pas aussi bien pour de grandes quantités de texte et aucune de ces deux méthodes n'a de caractéristiques typographiques.

Workflow de police en bitmap pour les fichiers SDF
Workflow du bitmap de la police au fichier SDF

Les polices bitmap utilisent un quad (deux triangles) par caractère. Elles utilisent donc moins de géométrie et offrent de meilleures performances que les vecteurs triangulés. Ils sont toujours basés sur une trame, car ils utilisent un lutin de map de texture, mais avec un nuanceur SDF, ils sont essentiellement indépendants de la résolution. Ils sont donc plus beaux qu'une texture de canevas 2D. Le texte three-bmfont de Matt DesLauriers inclut également des fonctionnalités typographiques fiables pour le retour à la ligne, l'espacement des lettres, la hauteur des lignes et l'alignement. Le dépassement n'est pas tronqué. La taille de la police est contrôlée par l'échelle. Nous avons choisi cette approche, car elle nous offre les meilleures options de conception tout en restant performants. Malheureusement, l'implémentation n'a pas été aussi facile à mettre en œuvre. Nous allons donc suivre la procédure dans l'espoir d'aider d'autres développeurs travaillant sur WebVR.

1. Générer une police bitmap (.png + .fnt)

Interface Hiero
Interface Hiero
Sortie Hiero (fichiers bitmap PNG et .fnt) Sortie Hiero (fichiers bitmap PNG et .fnt)
Sortie Hiero (fichier PNG et .fnt bitmap)

Hiero est un outil d'empaquetage de polices bitmap qui s'exécute avec Java. La documentation de Hiero n'explique pas vraiment comment l'exécuter sans passer par un processus de compilation complexe. Tout d'abord, installez Java si ce n'est pas déjà fait. Ensuite, si le double-clic sur runnable-hiero.jar n'ouvre pas Hiero, essayez de l'exécuter à l'aide de la commande suivante dans la console:

java -jar runnable-hiero.jar

Une fois que Hiero est en cours d'exécution, ouvrez une police de bureau .ttf ou .otf, saisissez les caractères supplémentaires que vous souhaitez inclure, définissez le rendu sur Java pour activer les effets, augmentez la taille pour que vos caractères remplissent tout le carré du cache de glyphes, ajoutez un effet de champ de distance, ajustez l'échelle et l'étendue du champ de distance. La valeur d'échelle est semblable à une résolution. Plus cette valeur est élevée, moins l'image est floue, mais plus Hiero met du temps à afficher l'aperçu. Enregistrez ensuite votre police bitmap. Il génère une police bitmap composée d'une image .png et d'un fichier de description de police .fnt AngelCode.

2. Convertir le code AngelCode en JSON

Maintenant que la police bitmap a été générée, nous devons la charger dans notre application JavaScript avec le package npm load-bmfont de Matt DesLauriers.

Nous pourrions utiliser load-bmfont et l'utiliser sur l'interface, mais nous allons exécuter load-bmfont.js avec Node pour convertir le fichier AngelCode de Hiero et l'enregistrer en fichier .json:

npm install
node load-bmfont.js
Exemple de résultat JSON
Exemple de sortie JSON

Nous pouvons à présent contourner load-bmfont et effectuer simplement une requête XHR (XMLHttpRequest) sur le fichier de polices .json.

var r = new XMLHttpRequest();
r.open('GET', 'fonts/roboto/bitmap/roboto-bold.json');

r.onreadystatechange = function() {
    if (r.readyState === 4 && r.status === 200) {
    setup(JSON.parse(r.responseText));
    }
};

r.send();

function setup(font) {
    // pass font into TextBitmap object
}

3. Browserify three-bmfont-text

Une fois la police chargée, le three-bmfont-text de Matt se charge du reste. Comme nous n'utilisons pas Node pour notre propre application, nous allons browserify three-bmfont-text.js en un fichier three-bmfont-text-bundle.js utilisable

npm install -g browserify
browserify three-bmfont-text.js -o three-bmfont-text-bundle.js

4. Nuanceur de fichiers SDF

Ajustez les curseurs afwidth et threshold sur vr.with.in/archive/text-sdf-bitmap/ pour voir l'effet du nuanceur de champ de distance signé.

5. Utilisation

Pour plus de commodité, j'ai créé une classe de wrapper TextBitmap pour le three-bmfont-text de navigateurifié.

Text-sdf-bitmap en action
Text-sdf-bitmap en action
<script src="three-bmfont-text-bundle.js"></script>
<script src="sdf-shader.js"></script>
<script src="text-bitmap.js"></script>

Créez une requête XHR pour le fichier de police .json et créez un objet texte dans le rappel:

var bmtext = new TextBitmap({ options });

Pour modifier le texte:

bmtext.text = 'The quick brown fox jumps over the lazy dog.';

scene.add( bmtext.group );
hitBoxes.push( bmtext.hitBox );

Le fichier .png de la police bitmap est chargé par THREE.TextureLoader dans text-bitmap.js.

TextBitmap inclut également un masque de collision invisible pour les interactions raycast 3.js avec une souris, un appareil photo ou des contrôleurs de mouvement suivis manuellement, comme l'Oculus Touch ou les contrôleurs Vive. La taille du masque de collision est mise à jour automatiquement lorsque vous modifiez les options de texte.

Ajout de Bmtext.group à la scène three.js. Si vous devez accéder aux enfants/d'Object3D, le graphique de scène du texte se présente comme suit:

Schéma du système de fichiers

6. Supprimer la taille de JSON et modifier les xoffsets

GIF dans le texte

Si le crénage est différent, vous devrez peut-être modifier les xoffsets dans le fichier JSON. Collez le fichier JSON dans Jsbeautifier.org pour obtenir une version non réduite du fichier.

Le xoffset est essentiellement un crénage global d'un caractère. Le crénage concerne deux caractères spécifiques qui apparaissent l'un à côté de l'autre. Les valeurs par défaut du tableau de crénage ne font pas de différence et il serait trop fastidieux à modifier. Vous pouvez donc vider ce tableau pour réduire la taille du fichier JSON. Modifiez ensuite les xoffsets pour le crénage.

Vous devez d'abord déterminer quels caractères correspondent à quel ID de caractère dans le fichier JSON. Dans three-bmfont-text-bundle.js, insérez console.log après la ligne 240:

    var id = text.charCodeAt(i)
    // console.log(id);

Saisissez ensuite du texte dans le champ de texte dat.gui sur https://vr.with.in/archive/text-sdf-bitmap/, puis consultez la console pour trouver l'ID correspondant d'un personnage.

Par exemple, dans notre police bitmap, "j" est constamment trop à droite. Son ID de caractère est 106. Recherchez donc "id": 106 dans le fichier JSON et modifiez son xoffset de -1 à -10.

7. Mise en page

Si vous disposez de plusieurs blocs de texte et que vous souhaitez qu'ils s'affichent de haut en bas, comme avec le code HTML, tout doit être positionné manuellement, de la même manière que le positionnement absolu de chaque élément DoM avec CSS. Imaginez-vous faire cela en CSS ?

    * { position: absolute; }

C'est à quoi ressemble la mise en page de texte en 3D. Dans la vue détaillée, le titre, l'auteur, la description et la durée sont chacun un nouvel objet TextBitmap avec leurs propres styles, couleurs, échelles, etc.:

mise en page 3D
author.group.position.y = title.group.position.y - title.height - padding;
description.group.position.y = author.group.position.y - author.height - padding;
duration.group.position.y = description.group.position.y - description.height - padding;

Cela suppose que l'origine locale de chaque groupe TextBitmap est alignée verticalement avec la partie supérieure du maillage TextBitmap (voir la section sur le centrage dans la mise à jour text-bitmap.js). Si, par la suite, vous modifiez le texte de l'un de ces objets et que la hauteur de cet objet change, vous devrez également recalculer ces positions. Ici, seule la position Y du texte est modifiée. Toutefois, pour travailler en 3D, vous pouvez pousser et extraire le texte dans la direction Z, et faire pivoter les axes X, Y et Z.

Conclusion

Dans WebVR, le texte et la mise en page ont encore beaucoup de chemin avant d'être aussi faciles à utiliser et aussi largement utilisés que HTML et CSS. Cependant, des solutions fonctionnelles existent, et vous pouvez faire bien plus dans WebVR qu'avec une page Web HTML traditionnelle. WebVR existe aujourd'hui. Il y aura probablement de meilleurs outils demain. En attendant, essayez-les et faites des tests. Développer sans framework omniprésent donne lieu à des projets plus uniques, et c'est passionnant.