Dynamically create 2D text in three.js

◇◆丶佛笑我妖孽 提交于 2019-12-29 11:02:32

问题


I have 3D model which I have created in three.js. Based on some data, I want to create a set of arrows which is decorated by a small text label. These labels should be in 2D.

It seems like I have two alternatives: either use a separate canvas element to create a texture which in its turn is used in the 3D model, or use HTML on top the 3D model's canvas element.

I'm wondering how to go about this. Which is the "correct" way to do this? Any suggestions and example code is very welcome!


回答1:


If you don't mind that the text will always be on top (e.g. if the object is blocked by something else, its text label will still be visible and on top of everything else), and that the text will not be affected by any rendering like lights/shadow, etc, then HTML is the easiest way to go imho.

Here is some sample 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);

Substitute 200 in the style.top and style.left variables for the y and x coordinates (respectively) you wish to place the text. They will be positive values where (0,0) is the top left of the canvas. For some guidance on how to project from the 3D point in your canvas to a 2D point in pixels in your browser window, use a code snippet like the following:

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

Make sure you have called camera.updateMatrixWorld() beforehand.




回答2:


If you actually want to include text (or any image) from a canvas object as part of your 3D scene, check out the sample code at: http://stemkoski.github.com/Three.js/Texture-From-Canvas.html




回答3:


Check out the demo.

TextGeometry consumes a lot of memory and you need to load a font, which contains a geometry for each letter, you will be using. If I have more than 100 text meshes in a scene, my browser crashes.

You can draw text on a canvas and include it in your scene via a Sprite. The problem with it is that you need to calculate the font size. If you have a moving camera, then you need to calculate the font size periodically. Otherwise the text will get to blurry, if you get too close to the text with the camera.

I wrote a class TextSprite which automatically calculates the best possible font size, depending on the distance to the camera and the size of the renderer element. The trick is to use the callback .onBeforeRender of the class Object3D, which receives the camera and the renderer.

let sprite = new THREE.TextSprite({
  text: 'Hello World!',
  fontFamily: 'Arial, Helvetica, sans-serif',
  fontSize: 12,
  fillColor: '#ffbbff',
});
scene.add(sprite);

You can also change the text and the font on the fly.

sprite.text = 'Hello Stack Overflow!';
sprite.fontFamily = 'Tahoma, Geneva, sans-serif';
sprite.fontSize = 24;



回答4:


Update : This method is Deprecated now and is CPU heavy, don't use it.

I found out another only three.js based solution,

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

Now to edit this text dynamically,

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



回答5:


I've published a module on NPM that does that for you: https://github.com/gamestdio/three-text2d

It allows you to have an Sprite or Mesh with the rendered text without dealing with canvas manually.

Example:

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

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



回答6:


You can create a 2d canvas and use css to position it to the top of your webgl canvas. Then you can draw text on it using the ctx.fillText(text,x,y) or ctx.strokeText(text,x,y) methods provided by the 2d canvas context. By using this method you can draw other things besides text, such as arrows and meters.

You can use the method as suggested by @kronuus to convert a point from 3d to 2d space.




回答7:


The response by @LeeStemkoski was quite useful. Yet there is a slight change that needs to be made to the code in order for the text to follow your arrows around i.e. in case the arrow gets moved around, rotated, etc.

See the next code

                        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)

As you can see, the trick is to add the text (mesh1) to the shape (your "arrow") instead of adding it to the scene.

I hope this helps.



来源:https://stackoverflow.com/questions/15248872/dynamically-create-2d-text-in-three-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!