在three.js中动态创建二维文本
Posted
技术标签:
【中文标题】在three.js中动态创建二维文本【英文标题】:Dynamically create 2D text in three.js 【发布时间】:2013-02-21 08:00:55 【问题描述】:我有在 three.js 中创建的 3D 模型。根据一些数据,我想创建一组由小文本标签装饰的箭头。这些标签应该是二维的。
似乎我有两种选择:要么使用单独的画布元素创建纹理,然后在 3D 模型中使用该纹理,或者在 3D 模型的画布元素顶部使用 html。
我想知道如何解决这个问题。执行此操作的“正确”方法是哪种?非常欢迎任何建议和示例代码!
【问题讨论】:
【参考方案1】:如果您不介意文本始终位于顶部(例如,如果对象被其他东西挡住了,它的文本标签仍然可见并且位于其他所有内容的顶部),并且文本不会位于受到任何渲染(如灯光/阴影等)的影响,那么 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 变量中的 200 替换为您希望放置文本的 y 和 x 坐标(分别)。它们将是正值,其中 (0,0) 是画布的左上角。有关如何从画布中的 3D 点投影到浏览器窗口中以像素为单位的 2D 点的一些指导,请使用如下代码 sn-p:
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()。
【讨论】:
感谢您的回答!我自己在投影坐标时遇到了一些问题,但结果是我在投影矢量之前忘了做一个 camera.updateMatrixWorld() 。结果是无法使用的值远离屏幕。 嘿,谢谢你的回答,我在三个 r79 中试过,它返回这个错误projector is not defined
,有解决方法吗?谢谢
当我移动物体或旋转相机时,我必须手动更新文本位置(?)有什么方法可以让这个更新自动进行吗?例如,如果我想在场景中每个对象的顶部制作 2D 标签,同时一切都在移动。
Prokop Hapala——听起来最好的办法是让标签成为 3D 场景的一部分;请参阅下面@LeeStemkoski 的解决方案
@kronuus 嗨,你能看看我的问题吗? ***.com/questions/61631545/…【参考方案2】:
查看demo。
TextGeometry
消耗大量内存,您需要加载一个字体,其中包含您将使用的每个字母的几何图形。如果场景中有超过 100 个文本网格,我的浏览器就会崩溃。
您可以在画布上绘制文本并通过Sprite
将其包含在您的场景中。它的问题是您需要计算字体大小。如果你有一个移动的相机,那么你需要定期计算字体大小。否则,如果您使用相机离文字太近,文字会变得模糊。
我编写了一个类TextSprite,它会根据到相机的距离和渲染器元素的大小自动计算最佳字体大小。诀窍是使用Object3D 类的回调.onBeforeRender
,它接收相机和渲染器。
let sprite = new THREE.TextSprite(
text: 'Hello World!',
fontFamily: 'Arial, Helvetica, sans-serif',
fontSize: 12,
color: '#ffbbff',
);
scene.add(sprite);
您还可以随时更改文本和字体。
sprite.text = 'Hello Stack Overflow!';
sprite.fontFamily = 'Tahoma, Geneva, sans-serif';
sprite.fontSize = 24;
【讨论】:
嗯。我没有这样做,而是使用从相机投影的方法来查找 html 元素的位置,然后使用绝对定位进行定位。不过,我有点希望我用过这个。好工作! :D 想提一下,如果您要渲染数百个 TextSprite,这个解决方案并不令人惊奇。性能受到影响 @SeregPie 你好,请看一下我的问题***.com/questions/61631545/… 尝试了演示,但屏幕上看不到任何内容(Firefox 83、32 位)。 @steveOw 哦,谢谢。我现在修好了。有 AFrame 更新,unpkg 无法解析包。【参考方案3】:如果您确实想将画布对象中的文本(或任何图像)作为 3D 场景的一部分,请查看以下示例代码: http://stemkoski.github.com/Three.js/Texture-From-Canvas.html
【讨论】:
我第一次解决这个问题时使用了你的例子 :) 非常感谢你提供的精彩教程!不过我想知道,为什么使用 canvas 方法会导致文本模糊(至少它在我的机器上呈现的那样)? 也许是抗锯齿的产物? 另外我相信这使用了 Three.js 中不再包含的一些功能 出现模糊文本的原因(主要)是因为three.js 将精灵渲染为正方形,即使画布可能比它的高度更宽。如果将精灵缩放到与画布相同的比例,它应该看起来更好。【参考方案4】:更新:此方法现已弃用,占用大量 CPU,请勿使用。
我发现了另一个只有基于三个.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;
现在动态编辑此文本,
var textShapes = THREE.FontUtils.generateShapes( text, options );
var text = new THREE.ShapeGeometry( textShapes );
textMesh.geometry = text;
textMesh.geometry.needsUpdate = true;
【讨论】:
应该注意,虽然这是一种用于创建 2D 文本的非常昂贵的方法。如果您正在考虑显示标准 2D 文本,则在画布中使用 HTML 更为明智。 您在定义之前使用文本。请检查变量名称。 我认为现在不赞成使用 TextShape【参考方案5】:我在 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)
【讨论】:
您能举例说明如何使用您的模块吗?我刚开始使用 ThreeJS,所以我只有 ThreeJS hello-world 示例。我相信我不能在里面 require('three-text2d') ,所以我应该使用像 browserify 这样的包来用 ThreeJS 编译你的包吗?谢谢!【参考方案6】:您可以创建一个 2d 画布并使用 css 将其定位到 webgl 画布的顶部。然后您可以使用二维画布上下文提供的ctx.fillText(text,x,y)
或ctx.strokeText(text,x,y)
方法在其上绘制文本。使用此方法,您可以绘制文本以外的其他内容,例如箭头和米。
您可以使用@kronuus 建议的方法将点从3d 空间转换为2d 空间。
【讨论】:
这比我的threejs画布上的HTML元素表现更好吗?【参考方案7】:@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
(您的“箭头”)而不是将其添加到场景中强>。
我希望这会有所帮助。
【讨论】:
【参考方案8】:来自三个js的“几何/文字/形状”: https://threejs.org/examples/?q=text#webgl_geometry_text_shapes
我简化了代码,只需要添加一个 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++)
“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);
);
【讨论】:
以上是关于在three.js中动态创建二维文本的主要内容,如果未能解决你的问题,请参考以下文章
使用vue学习three.js之加载和使用纹理-使用CubeCamera创建反光效果,动态环境贴图实现,立方体全景贴图
javascript 3d网页 简单几行代码创建一个动态水潭, 湖面 示例 ( three.js r114 初探 四)
Three.js地球开发—3D经纬度等比地图,3D飞行航线最终效果