Skip to content

Three.js - 创建文本

在 3D 场景中显示文本是一个常见需求,无论是用于用户界面 (UI)、注释还是艺术效果。在 Three.js 中实现这一点有几种方法,每种方法都有其优缺点。我们将探讨三种主要方法。

方法一:HTML 和 CSS(UI 推荐方式)

Section titled “方法一:HTML 和 CSS(UI 推荐方式)”

创建标签和叠加层等 UI 元素最灵活、质量最高的方法是利用浏览器最擅长的功能:渲染 HTML 和 CSS。Three.js 提供了 CSS2DRenderer 和 CSS3DRenderer 来帮助您在 3D 场景中定位常规 HTML 元素。

  • 优点: 任何分辨率下文本都清晰锐利,可充分利用 CSS 进行样式设置和动画,有利于可访问性,支持可点击的链接和按钮。
  • 缺点: 元素渲染在 3D 场景之上,而非其中。它们不会被 3D 对象遮挡,也无法受场景光照影响。
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
// 1. 设置渲染器
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
document.body.appendChild(labelRenderer.domElement);
// 2. 创建 HTML 元素
const p = document.createElement('p');
p.textContent = 'Hello, World!';
p.style.color = 'white';
p.style.fontFamily = 'sans-serif';
p.style.fontSize = '2em';
// 3. 创建 CSS2DObject
const label = new CSS2DObject(p);
// 4. 定位并添加到 3D 对象或场景
// 例如,将其定位在一个立方体的上方
label.position.set(0, 0.75, 0); // 位置是相对于父对象的
cube.add(label);
// 5. 在动画循环中渲染
function animate() {
// ...
renderer.render(scene, camera); // WebGL 渲染器
labelRenderer.render(scene, camera); // CSS2D 渲染器
}

对于需要作为真实 3D 对象存在于场景中的文本——能够投射阴影、受光照影响并被其他对象遮挡——您可以使用 TextGeometry。

  • 优点: 是一个真实的 3D 网格,能与光照和其他对象交互,可以具有深度和倒角。
  • 缺点: 需要加载特殊的字体文件(.typeface.json),由于高多边形数量可能对性能造成负担,近距离观察时文本质量可能会下降。

这个过程包括加载字体,然后使用该字体生成几何体。

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';
const fontLoader = new FontLoader();
fontLoader.load(
'/fonts/helvetiker_regular.typeface.json', // typeface.json 字体文件的路径
(font) => {
const textGeometry = new TextGeometry(
'Three.js',
{
font: font,
size: 0.5,
height: 0.2, // 深度
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
bevelOffset: 0,
bevelSegments: 5
}
);
// 将几何体居中以便于定位
textGeometry.center();
const textMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
const textMesh = new THREE.Mesh(textGeometry, textMaterial);
scene.add(textMesh);
}
);

注意: 您可以在 Three.js 示例存储库中找到 typeface.json 字体,或者使用在线工具转换您自己的 TTF/OTF 字体。

方法三:在 Canvas 纹理上绘制文本

Section titled “方法三:在 Canvas 纹理上绘制文本”

这是一种混合方法。您将文本绘制到 2D HTML5 Canvas 上,然后将该 canvas 作为 3D 平面 (PlaneGeometry) 上的纹理使用。这是一种高性能的文本显示方式,让文本看起来像是置身于 3D 世界中。

  • 优点: 性能极佳,可应用于任何几何体,来自 2D canvas API 的高质量文本渲染。
  • 缺点: 文本是平面的(作为纹理的一部分),如果文本需要动态更改,则需要仔细管理纹理更新。
// 1. 在内存中创建一个 canvas 元素
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 512;
canvas.height = 128;
// 2. 在 canvas 上绘制文本
context.fillStyle = '#444444'; // 背景
context.fillRect(0, 0, canvas.width, canvas.height);
context.font = 'Bold 48px sans-serif';
context.fillStyle = 'white';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText('Canvas Texture', canvas.width / 2, canvas.height / 2);
// 3. 从 canvas 创建纹理
const texture = new THREE.CanvasTexture(canvas);
// 4. 将纹理应用于材质
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.PlaneGeometry(4, 1); // 宽高比与 canvas 匹配
const planeMesh = new THREE.Mesh(geometry, material);
scene.add(planeMesh);

如果您需要更新文本,只需在 canvas 上重新绘制,然后设置 texture.needsUpdate = true; 来通知 Three.js 重新将纹理上传到 GPU。

  • 对于 UI、HUD 和注释,使用 HTML/CSS (CSS2DRenderer)。
  • 对于必须作为场景一部分的 3D 标志或文本,使用 TextGeometry。
  • 对于世界中标志或表面上的高性能文本,使用 Canvas 纹理。