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

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

  往期文章:

 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>

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

有只老羊在发呆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值