【threejs教程5】threejs添加文字标注,且始终面向屏幕

本文介绍了如何在three.js中加载和显示3D文字,包括使用FontLoader加载字体文件,创建TextGeometry,以及实现文字始终面向屏幕的动画效果。通过实例展示了如何为场景中的方块添加文字标注,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【图片完整效果代码位于文章末】

  往期文章:

 threejs基础开发应用示例

【threejs教程2】threejs物体点击交互事件

【threejs教程3】threejs物体轮廓发光

前言

        在三维场景中,我们经常需要添加文字对场景物体等进行标注说明,本文将介绍如何添加三维场景的文字,并实现文字始终面向屏幕的效果。

1.准备工作

1.1引入必要的库

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';

1.2准备字体文件

        由于threejs添加文字必须要一个字体文件且没有默认提供字体文件,所以在使用时,需要准备一个字体文件。示例中的文件下载地址点击下载字体,如果链接失效可以私信。

大家也可以使用facetype.js进行自定义文字转换,支持.ttf格式的字体文件,链接如下:

http://gero3.github.io/facetype.js/

本文使用的是华为开源免费字体HONOR_Sans_CN_Regular,具体字体资源可以私信获取。

2.创建文字

        首先创建字体加载器loader,并进行字体资源加载。回调函数中设定字体的样式,下面的函数创建了一个如图所示的白色半透明3d文字。

const loader = new FontLoader() // 创建字体加载器
function addText() {
	loader.load(
		// font资源URL
		'./font/HONOR_Sans_CN_Regular.json',
		// onLoad回调
		function (font) {
			const geometry = new TextGeometry('测试文字', {
				font: font,
				size: 0.3, // 字体大小
				height: 0.1, // 挤出文本的厚度
			})
			geometry.center() // 居中文本
			const materials = new THREE.MeshBasicMaterial({
				color: 0xffffffff,
				transparent: true,
				opacity: 0.5,
			})
			const textMesh = new THREE.Mesh(geometry, materials)
			textMesh.position.set(0,0,0)
			scene.add(textMesh)
		}
	)
}

字体其他可选的属性:

curveSegments:控制文本曲线的平滑度。默认值为 12,增加此值可以使文本更加平滑,但也会增加计算成本。

bevelEnabled:一个布尔值,用于指定是否启用文字的斜角。默认为 false。

bevelThickness:如果启用了斜角,此属性控制斜角的厚度。默认值为 6。

bevelSize:如果启用了斜角,此属性控制斜角的尺寸。默认值为 4。

bevelSegments:如果启用了斜角,此属性控制斜角的分段数。默认值为 3。

需要特别注意的是,代码中用到了一个geometry.center()方法,它的作用是把文字居中,如果没有这个,文字加载时会以左下角为中心。大家可以根据自己的需求进行设置。

3.让文字作为标注始终面向屏幕

在往期文章中,我们经常用到两个方块,下面的代码我们也将用两个方块进行文字标注的演示

1.添加两个方块

<template>
</template>
<script setup>
import * as THREE from 'three'
import { onMounted, ref } from 'vue'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'
const msg = ref('')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
	75,
	window.innerWidth / window.innerHeight,
	0.1,
	1000
)
const renderer = new THREE.WebGLRenderer({ antialias: true })
const controls = new OrbitControls(camera, renderer.domElement)
const loader = new FontLoader() // 创建字体加载器
onMounted(() => {
	init()
})
function init() {
	camera.position.set(0, 0, 5)
	renderer.setSize(window.innerWidth, window.innerHeight)
	document.body.appendChild(renderer.domElement)
	const geometry = new THREE.BoxGeometry(1, 1, 1)
	const material1 = new THREE.MeshBasicMaterial({
		color: 0xff00a2d7,
		transparent: true,
		opacity: 0.5,
	})
	const material2 = new THREE.MeshBasicMaterial({
		color: 0xffd3e3fd,
		transparent: true,
		opacity: 0.5,
	})
	const cube1 = new THREE.Mesh(geometry, material1)
	const cube2 = new THREE.Mesh(geometry, material2)
	scene.add(cube1, cube2)
	cube1.position.set(0, 0, 0)
	cube1.name = '方块1'
	cube2.position.set(2, 0, 0)
	cube2.name = '方块2'
	cube1.position.x = -2
	controls.update()
	function animate() {
		requestAnimationFrame(animate)
		controls.update()
		cube1.rotation.y += 0.01
		cube2.rotation.y -= 0.01
		renderer.render(scene, camera)
	}
	animate()
	addEachText()
}
</script>

2.为场景中的每个方块添加文字标注

        我们为每个方块添加了名字,可以根据名字进行筛选。大家也可以根据其他的信息,例如给物体添加userData自定义信息等方法进行筛选。

// 为每个物体添加文字标注
function addEachText() {
	// 遍历场景中的所有物体
	scene.children.forEach((child) => {
		if (child.name.includes('方块')) {
			addText(child)
		}
	})
}
// 为指定物体添加文字标注
function addText(obj) {
	loader.load(
		// font资源URL
		'./font/HONOR_Sans_CN_Regular.json',
		// onLoad回调
		function (font) {
			const geometry = new TextGeometry(obj.name, {
				font: font,
				size: 0.3, // 字体大小
				height: 0.1, // 挤出文本的厚度
			})
			geometry.center() // 居中文本
			const materials = new THREE.MeshBasicMaterial({
				color: 0xffffffff,
				transparent: true,
				opacity: 0.5,
			})
			const textMesh = new THREE.Mesh(geometry, materials)
			textMesh.position.copy(obj.position)
			textMesh.position.y = 1.2
			scene.add(textMesh)
		}
	)
}

3.让文字始终面向屏幕

我们需要创建一个循环渲染的函数。使用lookAt()让文字一直面向相机的位置。

function animate() {
				requestAnimationFrame(animate)
				textMesh.lookAt(camera.position)
			}
			animate()

4.完整效果代码如下所示

<template>
	<div
		style="
			font-size: 24px;
			color: #ffffff;
			text-align: center;
			position: absolute;
			top: 20%;
			left: 50%;
			transform: translate(-50%, -50%);
		"
	>
		{{ msg }}
	</div>
</template>
<script setup>
import * as THREE from 'three'
import { onMounted, ref } from 'vue'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'
const msg = ref('')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
	75,
	window.innerWidth / window.innerHeight,
	0.1,
	1000
)
const renderer = new THREE.WebGLRenderer({ antialias: true })
const controls = new OrbitControls(camera, renderer.domElement)
const loader = new FontLoader() // 创建字体加载器
onMounted(() => {
	init()
})
function init() {
	camera.position.set(0, 0, 5)
	renderer.setSize(window.innerWidth, window.innerHeight)
	document.body.appendChild(renderer.domElement)
	const geometry = new THREE.BoxGeometry(1, 1, 1)
	const material1 = new THREE.MeshBasicMaterial({
		color: 0xff00a2d7,
		transparent: true,
		opacity: 0.5,
	})
	const material2 = new THREE.MeshBasicMaterial({
		color: 0xffd3e3fd,
		transparent: true,
		opacity: 0.5,
	})
	const cube1 = new THREE.Mesh(geometry, material1)
	const cube2 = new THREE.Mesh(geometry, material2)
	scene.add(cube1, cube2)
	cube1.position.set(0, 0, 0)
	cube1.name = '方块1'
	cube2.position.set(2, 0, 0)
	cube2.name = '方块2'
	cube1.position.x = -2
	controls.update()
	function animate() {
		requestAnimationFrame(animate)
		controls.update()
		cube1.rotation.y += 0.01
		cube2.rotation.y -= 0.01
		renderer.render(scene, camera)
	}
	animate()
	addEachText()
}
// 为每个物体添加文字标注
function addEachText() {
	// 遍历场景中的所有物体
	scene.children.forEach((child) => {
		if (child.name.includes('方块')) {
			addText(child)
		}
	})
	console.log('添加文字', scene)
}
// 为指定物体添加文字标注
function addText(obj) {
	loader.load(
		'./font/HONOR_Sans_CN_Regular.json',
		function (font) {
			const geometry = new TextGeometry(obj.name, {
				font: font,
				size: 0.3, // 字体大小
				height: 0.1, // 挤出文本的厚度
			})
			geometry.center() // 居中文本
			const materials = new THREE.MeshBasicMaterial({
				color: 0xffffffff,
				transparent: true,
				opacity: 0.5,
			})
			const textMesh = new THREE.Mesh(geometry, materials)
			textMesh.position.copy(obj.position)
			textMesh.position.y = 1.2
			scene.add(textMesh)
			// 可选:在渲染循环中保持文字面向摄像机
			function animate() {
				requestAnimationFrame(animate)
				textMesh.lookAt(camera.position)
			}
			animate()
		}
	)
}
</script>

 文章如有技术相关错误请各位批评指正 

### 在 Three.js 中实现 3D 模型标注 为了在 Three.js 中对 3D 模型进行标注,可以采用多种方式来增强用户体验并提供交互功能。一种常见的做法是在特定位置放置标签或文字说明,并确保这些标签能够跟随相机视角或其他动态因素保持可见。 #### 创建基本场景与加载模型 首先,构建基础的 Three.js 场景并引入所需的库: ```javascript import * as THREE from 'three'; // 如果使用GLTF格式,则还需要这个加载器 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 加载外部3D模型文件 const loader = new GLTFLoader().load('path/to/model.gltf', function(gltf){ scene.add(gltf.scene); }); ``` #### 添加标注对象 接着,在目标物体附近添加 HTML 或 Canvas 文本作为标注。这里展示了一种基于三维空间坐标的简单方法,即利用 `THREE.Sprite` 来承载文本材质[^4]。 ```javascript function createLabel(textContent, position) { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); // 设置画布大小适应字体高度宽度 context.font = "Bold 18px Arial"; let metrics = context.measureText(textContent); canvas.width = metrics.width + 20; canvas.height = 20; // 绘制背景颜色和边框 context.fillStyle = "#fff"; context.fillRect(0, 0, context.canvas.width, context.canvas.height); // 填充文字内容 context.textAlign = "center"; context.fillStyle = "#000"; context.fillText(textContent, canvas.width / 2, 15); var texture = new THREE.CanvasTexture(canvas), material = new THREE.SpriteMaterial({ map: texture, color: 0xffffff }), sprite = new THREE.Sprite(material); sprite.scale.set(16, 8, 1); // 调整缩放比例使文本合适显示 sprite.position.copy(position); return sprite; } // 应用于具体mesh节点上 this.labels = this.model.children.filter(v => v.type === 'Mesh').forEach(mesh => { const labelPosition = mesh.position.clone(); // 获取当前网格的位置 const labelText = `${Math.random()}`; // 这里仅为示例随机生成一些文本 const labelSprite = createLabel(labelText, labelPosition); scene.add(labelSprite); }); ``` 此代码片段展示了如何遍历已加载的模型子组件(`children`),对于每一个符合条件的对象(例如类型为'Mesh'),创建一个新的带有自定义文本的 Sprite 并将其置于该对象旁边。 #### 动态更新标注位置 为了让标注始终面向摄像机方向或是当被标记物移动时同步调整其相对位移,可以在渲染循环内加入额外逻辑处理: ```javascript animate(); function animate(){ requestAnimationFrame(animate); controls.update(); // 若有轨道控制器则需调用update刷新状态 // 更新所有label的位置使其紧跟关联mesh变动 labels.forEach((label,idx)=>{ if(this.model && this.model.children[idx]){ label.position.copy(this.model.children[idx].position); } }); renderer.render(scene,camera); } ``` 上述过程实现了在一个简单的 Three.js 环境下向 3D 对象附加静态或动态文本标签的效果。当然实际项目可能还会涉及到更复杂的样式定制、响应式布局以及性能优化等问题。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有只老羊在发呆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值