Three.js - 创建文本
通常您需要向场景中添加文本。在本章中,让我们看看如何向场景中添加 2D 和 3D 文本。
将文本绘制到画布并用作纹理
这是向场景添加 2D 文本的最简单方法。您可以使用 JavaScript 创建画布并将其添加到 dom。
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
上面的代码创建了一个画布元素,我们将上下文设置为 2d。 canvas.getContext() 方法返回一个对象,该对象提供在画布上绘图的方法和属性,可用于绘制文本、线条、方框、圆圈等。
context.fillStyle = 'green'
context.font = '60px sans-serif
context.fillText('Hello World!', 0, 60)
fillText() 是一种 2D 绘图上下文方法。fillText() 方法允许您在坐标处绘制文本字符串,并使用您提供的 fillStyle 派生的填充(颜色)。您可以使用 font 属性设置文本的字体。
上述代码将字体设置为 60 像素高的 san-serif,并将填充样式设置为绿色。文本"Hello, World!"从坐标 (0, 60) 开始绘制。
// 画布内容用于纹理 const Texture = new THREE.Texture(canvas) texture.needsUpdate = true
要从画布元素创建纹理,我们需要创建一个新的 THREE.Texture 实例并传入我们创建的画布元素。上面的代码使用画布(在本例中为我们的文本)创建纹理。纹理的 needsUpdate 参数设置为 true。它通知 Three.js 我们的画布纹理已更改,需要在下次渲染场景时更新。
现在,创建一个平面几何图形并将其作为纹理添加到材质中。
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
})
material.transparent = true
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(50, 10), material)
使用文本几何图形
THREE.TextGeometry 是另一种将文本生成为单个几何图形的几何图形。它需要两个参数,text - 您要渲染的文本,以及其他参数。
参数
font − 这是字体的名称。
size − 文本的大小。默认值为 100。
height − height 属性定义文本的深度;换句话说,文本突出多远才能使其成为 3D。默认值为 50。
curveSegments − 曲线上的点数。默认值为 12。
bevelEnabled − 斜面提供从文本正面到侧面的平滑过渡。如果将此值设置为 true,则会为渲染的文本添加斜面。默认情况下,此值为 false。
bevelThickness − 如果将 bevelEnabled 设置为 true,则它定义斜面的深度。默认值为 10。
bevelSize − 它确定斜面的高度。默认值等于 8。
bevelOffset − 斜面从文本轮廓开始的距离。默认值为 0。
bevelSegments − 斜面段的数量。默认值为 3。
您需要使用 THREE.FontLoader 从其 typeface.json 文件加载字体。
const loader = new THREE.FontLoader()
loader.load('fonts/helvetiker_regular.typeface.json', function (font) {
const geometry = new THREE.TextGeometry('Hello Three.js!', {
font: font,
size: 3,
height: 0.2,
curveSegments: 12,
bevelEnabled: false,
bevelThickness: 0.5,
bevelSize: 0.3,
bevelOffset: 0,
bevelSegments: 5,
})
})
现在,您应该向其中添加一些材质并创建一个网格。
const material = new THREE.MeshFaceMaterial([
new THREE.MeshPhongMaterial({
color: 0xff22cc,
flatShading: true,
}), // front
new THREE.MeshPhongMaterial({
color: 0xffcc22
}), // side
])
const mesh = new THREE.Mesh(geometry, material)
mesh.name = 'text'
scene.add(mesh)
注意 − 使用 THREE.TextGeometry 和材料时,您需要考虑一件事。它可以将两种材料作为一个数组:一种用于渲染文本的正面,另一种用于文本的侧面。如果您只传入一种材料,它将同时应用于正面和侧面。
示例
现在,您可以看到渲染到场景中的文本。查看以下示例。
2d-text.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Three.js - 2d text</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
html,
body {
height: 100vh;
width: 100vw;
}
#threejs-container {
position: block;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
</head>
<body>
<div id="threejs-container"></div>
<script type="module">
// 将 2D 文本添加到 Three.js 场景
// 在画布上书写,然后将画布作为纹理添加到材质中
// GUI
const gui = new dat.GUI()
// 尺寸
let width = window.innerWidth
let height = window.innerHeight
const size = 256
const container = document.querySelector('#threejs-container')
const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d')
function changeCanvas() {
ctx.font = '20pt Arial'
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = 'black'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('Tutorialspoint!', canvas.width / 2, canvas.height / 2)
}
// 场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x262626)
// lights
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
const pointLight = new THREE.PointLight(0xffffff, 0.5)
pointLight.position.x = 20
pointLight.position.y = 30
pointLight.position.z = 40
scene.add(pointLight)
// 相机
const camera = new THREE.PerspectiveCamera(70, width / height, 1, 1000)
camera.position.z = 500
scene.add(camera)
// 渲染器
const renderer = new THREE.WebGL1Renderer({ antialias: true })
renderer.setSize(width, height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
container.append(renderer.domElement)
renderer.render(scene, camera)
// cube
const texture = new THREE.Texture(canvas)
const material = new THREE.MeshStandardMaterial({ map: texture })
const geometry = new THREE.BoxGeometry(200, 200, 200)
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
canvas.width = canvas.height = size
// 响应性
window.addEventListener('resize', () => {
width = window.innerWidth
height = window.innerHeight
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.render(scene, camera)
})
// 动画
function animate() {
requestAnimationFrame(animate)
changeCanvas()
texture.needsUpdate = true
mesh.rotation.y += 0.01
renderer.render(scene, camera)
}
animate()
</script>
</body>
</html>
输出
示例
现在让我们再举一个例子来了解如何在场景中添加 3D 文本。
3d-text.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Three.js - 3d text</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -applesystem, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
html,
body {
height: 100vh;
width: 100vw;
}
#threejs-container {
position: block;
width: 100%;
height: 100%;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
<head>
<body>
<div id="threejs-container"></div>
<script type="module">
// Creating 3d text using Text Geometry in Three.js
// GUI
const gui = new dat.GUI()
// 尺寸
let width = window.innerWidth
let height = window.innerHeight
// 场景
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x262626)
// lights
const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
const pointLight = new THREE.PointLight(0xffffff, 0.5)
pointLight.position.x = 20
pointLight.position.y = 30
pointLight.position.z = 40
scene.add(pointLight)
// 相机
const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 1000)
camera.position.set(0, 0, 50)
const camFolder = gui.addFolder('Camera')
camFolder.add(camera.position, 'z').min(10).max(500).step(10)
camFolder.open()
function createMaterial() {}
const loader = new THREE.FontLoader()
// promisify font loading
function loadFont(url) {
return new Promise((resolve, reject) => {
loader.load(url, resolve, undefined, reject)
})
}
async function doit() {
const font = await loadFont('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json')
let text = 'Hello World !'
const geometry = new THREE.TextGeometry(text, {
font: font,
size: 3,
height: 0.2,
curveSegments: 12,
bevelEnabled: true,
bevelOffset: 0,
bevelThickness: 0.5,
bevelSize: 0.3,
bevelSegments: 5
})
const material = [
new THREE.MeshPhongMaterial({
color: 0xff22cc,
flatShading: true
}), // front
new THREE.MeshPhongMaterial({
color: 0xffcc22
}) // side
]
const mesh = new THREE.Mesh(geometry, material)
geometry.computeBoundingBox()
geometry.computeVertexNormals()
geometry.boundingBox.getCenter(mesh.position).multiplyScalar(-1)
mesh.position.x = -geometry.boundingBox.max.x / 2
const parent = new THREE.Object3D()
parent.add(mesh)
scene.add(parent)
const opts = geometry.parameters.options
console.log(opts)
const geoProps = {
font: opts.font,
size: opts.size,
height: opts.height,
curveSegments: opts.curveSegments,
bevelEnabled: opts.bevelEnabled,
bevelOffset: opts.bevelOffset,
bevelThickness: opts.bevelThickness,
bevelSize: opts.bevelSize,
bevelSegments: opts.bevelSegments
}
console.log(geoProps)
// GUI for exporimenting cube properties
const props = gui.addFolder('Properties')
props
.add(geoProps, 'size', 1, 30)
.step(1)
.onChange(redraw)
.onFinishChange(() => console.dir(mesh.geometry))
props.add(geoProps, 'height', 0, 30).step(0.1).onChange(redraw)
props.add(geoProps, 'curveSegments', 1, 30).step(1).onChange(redraw)
props.add(geoProps, 'bevelEnabled').onChange(redraw)
props.add(geoProps, 'bevelOffset', 0, 1).onChange(redraw)
props.add(geoProps, 'bevelThickness', 0, 3).onChange(redraw)
props.add(geoProps, 'bevelSize', 0, 3).onChange(redraw)
props.add(geoProps, 'bevelSegments', 1, 8).step(1).onChange(redraw)
props.open()
function redraw() {
camera.position.set(0, 0, 80)
let newGeometry = new THREE.TextGeometry(text, {
font: geoProps.font,
size: geoProps.size,
height: geoProps.height,
curveSegments: geoProps.curveSegments,
bevelEnabled: geoProps.bevelEnabled,
bevelOffset: geoProps.bevelOffset,
bevelThickness: geoProps.bevelThickness,
bevelSize: geoProps.bevelSize,
bevelSegments: geoProps.bevelSegments
})
mesh.geometry.dispose()
mesh.geometry = newGeometry
mesh.geometry.parameters.options.depth = 0.2
console.log(mesh.geometry.parameters.options)
}
}
doit()
// 响应性
window.addEventListener('resize', () => {
width = window.innerWidth
height = window.innerHeight
camera.aspect = width / height
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.render(scene, camera)
})
// 渲染器
const renderer = new THREE.WebGL1Renderer({ antialias: true })
renderer.setSize(width, height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
// 动画
function animate() {
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
// 渲染场景
const container = document.querySelector('#threejs-container')
container.append(renderer.domElement)
renderer.render(scene, camera)
animate()
</script>
</body>
</html>

