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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15248872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 06:11:08  来源:igfitidea点击:

Dynamically create 2D text in three.js

javascripthtmlthree.js

提问by anders

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.

我有我在three.js中创建的3D模型。根据一些数据,我想创建一组由小文本标签装饰的箭头。这些标签应该是二维的。

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!

我想知道如何解决这个问题。哪个是“正确”的方法来做到这一点?非常欢迎任何建议和示例代码!

回答by kronuus

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.

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

将 style.top 和 style.left 变量中的 200 替换为您希望放置文本的 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;
}

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

确保您事先调用了 camera.updateMatrixWorld()。

回答by SeregPie

Check out the demo.

查看 演示

TextGeometryconsumes 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.

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

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.

您可以在画布上绘制文本并通过Sprite. 它的问题在于您需要计算字体大小。如果你有一个移动的相机,那么你需要定期计算字体大小。否则,如果您用相机离文本太近,文本会变得模糊。

I wrote a class TextSpritewhich 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 .onBeforeRenderof the class Object3D, which receives the camera and the renderer.

我编写了一个TextSprite类,它根据到相机的距离和渲染器元素的大小自动计算可能的最佳字体大小。诀窍是使用Object3D.onBeforeRender类的回调,它接收相机和渲染器。

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;

回答by Lee Stemkoski

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

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

回答by Ishan Sharma

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;

回答by Endel

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 Spriteor Meshwith 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) 

回答by Licson

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.

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

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

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

回答by Hugo Nava Kopp

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

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

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.

我希望这有帮助。

回答by Sylvain Lugez

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

});