我有三个模型,我在three.js中创建。基于一些数据,我想创建一组由小文本标签装饰的箭头。这些标签应为2D。
似乎我有两种选择:使用单独的canvas元素创建一个纹理,然后在3D模型中使用它,或者在3D模型的canvas元素上使用HTML。
我想知道如何解决这个问题。这是“正确”的方法吗?任何建议和示例代码都是非常受欢迎的!
答案 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
它允许您使用渲染文本获得Sprite
或Mesh
,而无需手动处理画布。
示例:强>
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++) {