web-dev-qa-db-fra.com

Créer dynamiquement du texte 2D dans three.js

J'ai un modèle 3D que j'ai créé dans three.js. Sur la base de certaines données, je souhaite créer un ensemble de flèches qui est décoré par une petite étiquette de texte. Ces étiquettes doivent être en 2D.

Il semble que j'ai deux alternatives: soit utiliser un élément canvas distinct pour créer une texture qui est utilisée à son tour dans le modèle 3D, soit utiliser HTML au-dessus de l'élément canvas du modèle 3D. 

Je me demande comment s'y prendre. Quelle est la "bonne" façon de faire cela? Toutes les suggestions et exemples de code sont les bienvenus!

31
navlelo

Si cela ne vous dérange pas que le texte soit toujours au premier plan (par exemple, si l'objet est bloqué par quelque chose d'autre, son libellé reste visible et au-dessus de tout le reste) et que le texte ne sera pas affecté par aucun élément. rendu comme des lumières/ombre, etc, alors HTML est le moyen le plus facile d'aller à mon humble avis.

Voici un exemple de code:

var text2 = document.createElement('div');
text2.style.position = 'absolute';
//text2.style.zIndex = 1;    // if you still don't see the label, try uncommenting this
text2.style.width = 100;
text2.style.height = 100;
text2.style.backgroundColor = "blue";
text2.innerHTML = "hi there!";
text2.style.top = 200 + 'px';
text2.style.left = 200 + 'px';
document.body.appendChild(text2);

Remplacez 200 dans les variables style.top et style.left par les coordonnées y et x (respectivement) pour lesquelles vous souhaitez placer le texte. Ce seront des valeurs positives où (0,0) est le coin supérieur gauche du canevas. Pour obtenir des conseils sur la manière de projeter du point 3D de votre canevas vers un point 2D en pixels dans la fenêtre de votre navigateur, utilisez un extrait de code comme suit:

function toXYCoords (pos) {
        var vector = projector.projectVector(pos.clone(), camera);
        vector.x = (vector.x + 1)/2 * window.innerWidth;
        vector.y = -(vector.y - 1)/2 * window.innerHeight;
        return vector;
}

Assurez-vous d'avoir préalablement appelé camera.updateMatrixWorld (). 

38
kronuus

Si vous souhaitez réellement inclure du texte (ou une image) provenant d'un objet de dessin dans votre scène 3D, consultez l'exemple de code à l'adresse suivante: http://stemkoski.github.com/Three.js/Texture. -De-Canvas.html

19
Lee Stemkoski

Découvrez le demo .

TextGeometry consomme beaucoup de mémoire et vous devez charger une police contenant une géométrie pour chaque lettre que vous utiliserez. Si j'ai plus de 100 maillages de texte dans une scène, mon navigateur se bloque.

Vous pouvez dessiner du texte sur une toile et l’inclure dans votre scène via une variable Sprite. Le problème, c’est que vous devez calculer la taille de la police. Si vous avez une caméra en mouvement, vous devez calculer périodiquement la taille de la police. Sinon, le texte deviendra flou si vous vous approchez trop du texte avec l'appareil photo.

J'ai écrit une classe TextSprite qui calcule automatiquement la meilleure taille de police possible, en fonction de la distance à la caméra et de la taille de l'élément de rendu. L'astuce consiste à utiliser le callback .onBeforeRender de la classe Object3D , qui reçoit la caméra et le rendu.

let Sprite = new THREE.TextSprite({
    textSize: 10,
    texture: {
        text: 'Hello World!',
        fontFamily: 'Arial, Helvetica, sans-serif',
    },
    material: {color: 0xffbbff},
});
scene.add(Sprite);

Vous pouvez également modifier le texte, la taille du texte et la police à la volée.

Sprite.textSize = 25;
Sprite.material.map.text = 'Hello Stack Overflow!';
Sprite.material.map.fontFamily = 'Tahoma, Geneva, sans-serif';
13
SeregPie

Mise à jour : Cette méthode est obsolète maintenant et nécessite beaucoup de temps de processeur, ne l'utilisez pas.

J'ai découvert une autre solution basée uniquement sur trois.js,

var textShapes = THREE.FontUtils.generateShapes( text, options );
var text = new THREE.ShapeGeometry( textShapes );
var textMesh = new THREE.Mesh( text, new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ) ;
scene.add(textMesh);
// Example text options : {'font' : 'helvetiker','weight' : 'normal', 'style' : 'normal','size' : 100,'curveSegments' : 300};

Maintenant, pour éditer ce texte dynamiquement,

var textShapes = THREE.FontUtils.generateShapes( text, options );
var text = new THREE.ShapeGeometry( textShapes );
textMesh.geometry = text;
textMesh.geometry.needsUpdate = true;
8
Ishan Sharma

J'ai publié un module sur NPM qui le fait pour vous: https://github.com/gamestdio/three-text2d

Il vous permet d’avoir une Sprite ou Mesh avec le texte rendu sans traiter manuellement le canevas.

Exemple:

var Text2D = require('three-text2d').Text2D

var text = new Text2D("Hello world!", { font: '30px Arial', fillStyle: '#000000', antialias: true })
scene.add(text) 
7
Endel

Vous pouvez créer une toile 2d et utiliser css pour la positionner en haut de votre toile webgl. Ensuite, vous pouvez dessiner du texte dessus à l'aide des méthodes ctx.fillText(text,x,y) ou ctx.strokeText(text,x,y) fournies par le contexte de la toile 2d. En utilisant cette méthode, vous pouvez dessiner des éléments autres que du texte, tels que des flèches et des mètres.

Vous pouvez utiliser la méthode suggérée par @kronuus pour convertir un point de l'espace 3D en espace 2D.

5
Licson

La réponse de @LeeStemkoski a été très utile. Cependant, un léger changement doit être apporté au code pour que le texte suive vos flèches i.e. au cas où la flèche serait déplacée, tournée, etc. 

Voir le code suivant

                        var canvas1 = document.createElement('canvas');
                        var context1 = canvas1.getContext('2d');
                        context1.font = "Bold 10px Arial";
                        context1.fillStyle = "rgba(255,0,0,1)";
                        context1.fillText('Hello, world!', 0, 60);

                        // canvas contents will be used for a texture
                        var texture1 = new THREE.Texture(canvas1)
                        texture1.needsUpdate = true;

                        var material1 = new THREE.MeshBasicMaterial({ map: texture1, side: THREE.DoubleSide });
                        material1.transparent = true;

                        var mesh1 = new THREE.Mesh(
                            new THREE.PlaneGeometry(50, 10),
                            material1
                          );
                        mesh1.position.set(25, 5, -5);
                        mesh1.rotation.x = -0.9;
                        shape.add(mesh1);
                        // Note that mesh1 gets added to the shape and not to the scene

                       scene.add(shape)

Comme vous pouvez le constater, l’astuce consiste à ajouter le texte (mesh1) à la variable shape(votre "flèche") au lieu de l’ajouter à la scène

J'espère que ça aide.

1
Hugo Nava Kopp