three.js Text2D 模块实战:用 createText 在 WebXR 场景中创建 Canvas 文本标签
2026/9/10 7:18:17 网站建设 项目流程

three.js Text2D 模块实战:用 createText 在 WebXR 场景中创建 Canvas 文本标签

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

导读

Text2D 是 three.js 官方仓库中位于examples/jsm/webxr/下的一个轻量辅助模块,核心只提供一个createText()方法:把一段字符串渲染到 Canvas 上并生成纹理,最终返回一个可直接放入场景的平面网格(Plane Mesh),常用于 WebXR 场景中的按钮文字、操作提示、状态标签等。读完本文,你将掌握该模块的导入方式、createText的参数语义、底层 Canvas 纹理与几何体尺寸的计算原理,以及如何把它挂接到按钮、跟随手势交互等实战场景中。

模块定位与导入方式

Text2D 属于 three.js 的 addon(附加模块),不会随主库自动打包,必须显式导入。官方推荐通过three/addons/路径引用:

import * as Text2D from 'three/addons/webxr/Text2D.js';

由于模块内部实际只导出了一个具名函数createText,也可以按需导入以配合打包器做 tree-shaking:

import { createText } from 'three/addons/webxr/Text2D.js';

上面的第二种写法正是官方示例 examples/webxr_vr_handinput_pointerdrag.html 与 examples/webxr_vr_handinput_pressbutton.html 中实际使用的形式。从 examples/jsm/Addons.js 的导出注册看,Text2D位于webxr分组,与ARButtonVRButtonXRControllerModelFactory等 WebXR 基础设施并列,说明它的设计定位就是服务于 XR 场景下的轻量文本标签。

createText 方法:签名与参数语义

方法签名

createText( message : string, height : number ) : Mesh

createText是一个纯函数式辅助方法:输入要显示的文本和标签的物理高度(世界单位),返回一个用于表示文本标签的平面网格(Mesh)。

参数说明

参数类型含义取值建议
messagestring要显示的消息文本,会原样绘制到 Canvas 上任意字符串;过长的文本会被压缩进固定宽度,注意可读性
heightnumber标签在三维空间中的高度(世界单位)以米为单位的数值,如0.040.06适合近景 UI,具体参考下方示例

返回值

一个Mesh(由PlaneGeometry+MeshBasicMaterial组成),材质上挂载了一张携带 Canvas 纹理的map。网格平面会始终面向观察方向?不——它默认不带自动朝向逻辑,需要自行旋转或用lookAt控制朝向,这一点在实战中需特别注意。

源码实现剖析:一张 Canvas 如何变成 3D 标签

完整实现见 examples/jsm/webxr/Text2D.js,全文仅 52 行,可分为四个阶段。

1. 用 Canvas 2D 测量并绘制文本

const canvas = document.createElement( 'canvas' ); const context = canvas.getContext( '2d' ); let metrics = null; const textHeight = 100; context.font = 'normal ' + textHeight + 'px Arial'; metrics = context.measureText( message ); const textWidth = metrics.width; canvas.width = textWidth; canvas.height = textHeight;

实现采用固定字号策略:先在内存中把字体设置为normal 100px Arial,调用context.measureText( message )得到文本的实际像素宽度textWidth,再以textWidth × 100作为 Canvas 尺寸。也就是说 Canvas 的像素高度被固定为 100px,宽度由文本内容自适应,从而保证不同长度的字符串都能完整绘制且不留多余空白。

2. 居中对齐填充文本

context.font = 'normal ' + textHeight + 'px Arial'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#ffffff'; context.fillText( message, textWidth / 2, textHeight / 2 );

因为textAlign = 'center'textBaseline = 'middle',文字以画布中心为锚点绘制,最终纹理中的文字天然居中,映射到几何体上也是居中的。填充色固定为白色#ffffff,最终颜色由材质color决定(两者相乘)。

3. 生成纹理与材质

const texture = new Texture( canvas ); texture.needsUpdate = true; const material = new MeshBasicMaterial( { color: 0xffffff, side: DoubleSide, map: texture, transparent: true, } );
  • new Texture( canvas )直接以 Canvas 作为纹理源;
  • 由于 Canvas 在纹理创建前就已绘制完毕,必须显式设置texture.needsUpdate = true通知 GPU 上传,否则可能出现空白纹理;
  • side: DoubleSide让标签在 VR 里从背面看也可见(绕到标签另一侧时不会消失);
  • transparent: true开启透明通道,Canvas 中未绘制的区域保持透明,不会出现黑色底块;
  • 使用MeshBasicMaterial,标签不受场景光照影响,在任何环境亮度下都保持清晰,这符合 UI 标签的需求。

4. 按文本宽高比计算几何体

const geometry = new PlaneGeometry( ( height * textWidth ) / textHeight, height );

这是整个函数最精妙的一步:几何体高度直接取传入的height(世界单位),宽度则按textWidth / textHeight的像素宽高比等比换算为height * textWidth / textHeight。由于textHeight恒为 100,实际宽度计算公式可写作height * textWidth / 100。这样无论height取多大,标签在世界空间中的宽高比都严格等于 Canvas 像素宽高比,文字不会变形拉伸。

最终new Mesh( geometry, material )组装成平面网格并返回,调用方可直接scene.add()或挂到其他物体上。

实战:在 WebXR 手部交互示例中挂接文本标签

官方仓库用 Text2D 生成按钮文字与操作提示的最佳范例是 examples/webxr_vr_handinput_pointerdrag.html(拖拽立方体)和 examples/webxr_vr_handinput_pressbutton.html(按按钮),它们都在同一场景中演示了四种典型用法。

按钮文字:作为子节点叠加在按钮几何体上

pointerdrag示例为例(examples/webxr_vr_handinput_pointerdrag.html#L156-L168):

const resetButton = makeButtonMesh( 0.2, 0.1, 0.01, 0x355c7d ); const resetButtonText = createText( 'reset', 0.06 ); resetButton.add( resetButtonText ); resetButtonText.position.set( 0, 0, 0.0051 ); resetButton.position.set( 0, - 0.06, 0 ); menuMesh.add( resetButton );

关键点:

  • createText( 'reset', 0.06 )生成了高度 0.06 米的文字标签,与 0.1 米高的按钮相比占约 60% 高度,比例协调;
  • 通过resetButton.add( resetButtonText )把文字挂为按钮子节点,再设置position.set( 0, 0, 0.0051 )在 Z 轴方向偏移 0.0051 米,使其浮在按钮表面之上(按钮厚度 0.01 米,文字贴在前表面外侧);
  • 文字作为子节点,会随按钮整体变换,无需单独维护位置。

场景级提示文字:直接加入场景

instructionText = createText( 'This is a WebXR Hands demo, please explore with hands.', 0.04 ); instructionText.position.set( 0, 1.6, - 0.6 ); scene.add( instructionText );

较长的说明性文案使用0.04米的高度(约 4 厘米),放在眼睛高度(0, 1.6, -0.6)前方。注意:createText返回的平面默认位于 XY 平面且法线朝向 +Z,此例将标签放在相机前方偏下位置,视线斜向下时文字正面自然朝向观察者,无需额外旋转;若标签与相机朝向不匹配,需要手动rotation.y旋转或用lookAt对准相机。

动态显隐的临时状态文本

const exitText = createText( 'Exiting session...', 0.04 ); exitText.position.set( 0, 1.5, - 0.6 ); exitText.visible = false; scene.add( exitText );

标签本质是普通 Mesh,可以像任何物体一样通过visible控制显隐。示例在退出会话前 2 秒显示 "Exiting session..."(examples/webxr_vr_handinput_pointerdrag.html#L199-L207),由于 Text2D 已把文本烘焙进 Canvas 纹理,显隐切换零成本,非常适合这种低频状态提示。

pressbutton 示例中的紧凑尺寸

examples/webxr_vr_handinput_pressbutton.html#L158-L166 使用createText( 'reset', 0.03 )createText( 'exit', 0.03 ),按钮文字高度取 0.03 米,与 0.04 米的提示文字形成视觉层级。两处示例分别用0.03/0.04/0.06三档高度,可作为不同场景的参考量级。

局限性与注意事项

从源码与使用方式可以归纳出以下边界,便于在项目中做取舍:

  1. 字体固定为 Arial:源码写死'normal 100px Arial',不支持自定义字体、字重、颜色或描边,样式诉求超出此范围时需自行 fork 或改用CSS2DRendererTextGeometry(需加载字体 JSON)等方案。
  2. 尺寸受 Canvas 精度限制height过小(如 0.01 米以下)时,文本在远处会因纹理采样而发虚;过大则 Canvas 像素密度相对不足,可能出现锯齿。
  3. 不自动面向相机:返回的平面法线固定朝 +Z,在自由移动的 XR 场景中建议作为子节点挂载(随父物体变换),或定期执行label.lookAt( camera.position )
  4. 无换行与多行支持measureText只度量单行文本,长文案需自行按行拆分为多个createText调用。
  5. 每次调用都会新建 Canvas、纹理与几何体:适合少量标签,若需高频更新文案,建议在外部复用CanvasTexture并自行管理纹理内容。

小结

Text2D 模块用不足 60 行代码,把"字符串 → Canvas 纹理 → 等比平面几何体 → Mesh"这一完整链路封装进一个createText(message, height)函数,是 WebXR 场景中快速生成按钮文字与提示标签的实用工具。理解其textHeight固定为 100 像素的度量逻辑与height * textWidth / 100的几何换算规则,就能在任意尺度下稳定控制文字比例。结合官方手部交互示例中的挂载、偏移与显隐模式,你可以直接复用这套模式构建自己的 XR 交互界面。

【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询