在three.js中动态创建2D文本

时间:2013-03-06 13:38:38

标签: javascript html three.js

我有三个模型,我在three.js中创建。基于一些数据,我想创建一组由小文本标签装饰的箭头。这些标签应为2D。

似乎我有两种选择:使用单独的canvas元素创建一个纹理,然后在3D模型中使用它,或者在3D模型的canvas元素上使用HTML。

我想知道如何解决这个问题。这是“正确”的方法吗?任何建议和示例代码都是非常受欢迎的!

8 个答案:

答案 0 :(得分:39)

如果您不介意文本将始终位于顶部(例如,如果对象被其他内容阻止,则其文本标签仍然可见并且在其他所有内容之上),并且文本将不会是受到任何渲染(如灯光/阴影等)的影响,那么HTML是最简单的方式。

以下是一些示例代码:

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

在style.top和style.left变量中替换为您希望放置文本的y和x坐标(分别)。它们将是正值,其中(0,0)是画布的左上角。有关如何从画布中的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;
}

请务必事先调用camera.updateMatrixWorld()。

答案 1 :(得分:19)

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

答案 2 :(得分:15)

查看demo

TextGeometry消耗大量内存,您需要加载一个字体,其中包含您将使用的每个字母的几何图形。如果场景中有超过100个文本网格,我的浏览器会崩溃。

您可以在画布上绘制文字,并通过Sprite将其包含在场景中。它的问题是你需要计算字体大小。如果您有移动相机,则需要定期计算字体大小。否则,如果您使用相机太靠近文本,文本将变得模糊。

我写了一个类TextSprite,它会自动计算出最佳的字体大小,具体取决于相机的距离和渲染器元素的大小。诀窍是使用类Object3D的回调.onBeforeRender,它接收相机和渲染器。

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

您还可以动态更改文字,文字大小和字体。

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

答案 3 :(得分:8)

更新:此方法现已弃用,且CPU很重,请勿使用它。

我发现了另一个只有三个基于解决方案的解决方案,

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

现在动态编辑此文本

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

答案 4 :(得分:7)

我已经在NPM上发布了一个模块,可以帮助您:https://github.com/gamestdio/three-text2d

它允许您使用渲染文本获得SpriteMesh,而无需手动处理画布。

示例:

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

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

答案 5 :(得分:5)

您可以创建2D画布并使用css将其放置在webgl画布的顶部。然后,您可以使用2d画布上下文提供的ctx.fillText(text,x,y)ctx.strokeText(text,x,y)方法在其上绘制文本。通过使用此方法,您可以绘制除文本之外的其他内容,例如箭头和米。

您可以使用@kronuus建议的方法将点从3d转换为2d空间。

答案 6 :(得分:1)

@LeeStemkoski的回应非常有用。然而,需要对代码进行细微更改,以便文本跟随箭头,即箭头移动,旋转等等。

参见下一个代码

                        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)

正如您所看到的,诀窍是将文字(mesh1添加到shape (您的"箭头")而不是将其添加到场景

我希望这会有所帮助。

答案 7 :(得分:1)

来自三个js的“几何/文本/形状”: https://threejs.org/examples/?q=text#webgl_geometry_text_shapes

我简化了代码,只是为了严格添加2D动态文本:

    activeUsersList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
        historyTextArea.appendText("ListView Selection Changed (selected: " + newValue.toString() + ")\n");
        messageTextArea.setFocusTraversable(false);
    });

'generateShapes'方法允许创建形状文本

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++) {