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!
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.
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,
color: '#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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With