简体   繁体   English

在three.js中动态创建二维文本

[英]Dynamically create 2D text in three.js

I have 3D model which I have created in three.js.我有我在three.js中创建的3D模型。 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.似乎我有两种选择:要么使用单独的画布元素来创建纹理,然后在 3D 模型中使用该纹理,要么在 3D 模型的画布元素顶部使用 HTML。

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 (eg 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.如果您不介意文本始终位于顶部(例如,如果对象被其他物体挡住,其文本标签仍将可见并位于其他所有物体的顶部),并且文本不会受到任何影响渲染像灯光/阴影等,那么 HTML 是最简单的方法。

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.将 style.top 和 style.left 变量中的 200 替换为您希望放置文本的 y 和 x 坐标(分别)。 They will be positive values where (0,0) is the top left of the canvas.它们将是正值,其中 (0,0) 是画布的左上角。 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:有关如何从画布中的 3D 点投影到浏览器窗口中以像素为单位的 2D 点的一些指导,请使用如下代码片段:

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.确保您事先调用了 camera.updateMatrixWorld()。

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. TextGeometry占用大量内存,您需要加载一种字体,其中包含您将使用的每个字母的几何图形。 If I have more than 100 text meshes in a scene, my browser crashes.如果场景中有超过 100 个文本网格,我的浏览器就会崩溃。

You can draw text on a canvas and include it in your scene via a Sprite .您可以在画布上绘制文本并通过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.我编写了一个TextSprite类,它根据到相机的距离和渲染器元素的大小自动计算可能的最佳字体大小。 The trick is to use the callback .onBeforeRender of the class Object3D , which receives the camera and the renderer.诀窍是使用Object3D类的回调.onBeforeRender ,它接收相机和渲染器。

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;

如果您确实想将画布对象中的文本(或任何图像)作为 3D 场景的一部分,请查看示例代码: http : //stemkoski.github.com/Three.js/Texture-From-Canvas。 html

Update : This method is Deprecated now and is CPU heavy, don't use it.更新:此方法现在已弃用,并且占用大量 CPU,请勿使用。

I found out another only three.js based solution,我发现了另一个仅基于三个.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};

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;

I've published a module on NPM that does that for you: https://github.com/gamestdio/three-text2d我已经在 NPM 上发布了一个模块,可以为您做到这一点: https : //github.com/gamestdio/three-text2d

It allows you to have an Sprite or Mesh with the rendered text without dealing with canvas manually.它允许您拥有带有渲染文本的SpriteMesh ,而无需手动处理画布。

Example:例子:

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

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

You can create a 2d canvas and use css to position it to the top of your webgl canvas.您可以创建一个 2d 画布并使用 css 将其定位到 webgl 画布的顶部。 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.然后,您可以使用 2d 画布上下文提供的ctx.fillText(text,x,y)ctx.strokeText(text,x,y)方法在其上绘制文本。 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.您可以使用@kronuus 建议的方法将点从 3d 空间转换为 2d 空间。

The response by @LeeStemkoski was quite useful. @LeeStemkoski 的回复非常有用。 Yet there is a slight change that needs to be done to the code in order for the text to follow your arrows around ie 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 .如您所见,诀窍是将文本 ( mesh1 ) 添加shape (您的“箭头”)而不是将其添加到场景中

I hope this helps.我希望这有帮助。

From the "geometry / text / shape" of Three js :https://threejs.org/examples/?q=text#webgl_geometry_text_shapes来自三个js的“几何/文本/形状”:https ://threejs.org/examples/? q= text#webgl_geometry_text_shapes

I simplified the code for just have the strict necessary to add a 2D dynamic text :我简化了代码,只需要添加 2D 动态文本:

var loader = new THREE.FontLoader();
loader.load( 'assets/fonts/helvetiker_regular.typeface.json', function ( font ) {   
    var textPositions = [[1, 1, 1],
                         [2, 2, 2]];

    var textMessages = ['Text 1', 'Text 2'];

    var textSizes = [0.1, 0.2];

    var textName = ['text1', 'text2'];

    var textsNumber = textPositions.length;     

    for (var i = 0; i < textsNumber; i++) {

the method 'generateShapes' permit to create the text in shape 'generateShapes' 方法允许在形状中创建文本

        var textsShapes = font.generateShapes( textMessages[i], textSizes[i] );
        var textsGeometry = new THREE.ShapeBufferGeometry( textsShapes );    
        var textsMaterial = new THREE.MeshBasicMaterial({color: 0xeeeeee});

        var text = new THREE.Mesh(textsGeometry, textsMaterial);
        text.position.set(textPositions[i][0], textPositions[i][1], textPositions[i][2]);
        text.name = textName[i]; 

        scene.add(text);
    }

}); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM