image.png
- 最终效果如上图所示,实现一个3D Text,使用了
MeshMatcapMaterial
、FontLoader
、TorusGeometry
等
- 首先创建一个带有坐标系的基础场景
// 导入three.js
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
/**
* scene
**/
const scene = new THREE.Scene()
/**
* camera
**/
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10)
camera.lookAt(0, 0, 0)
/**
* renderer
**/
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
/**
* axesHelper
**/
const axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)
/**
* controls
**/
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
/**
* render
**/
function animate () {
controls.update()
requestAnimationFrame(animate)
renderer.render(scene, camera)
}
animate()
// 导入FontLoader
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
// 导入TextGeometry
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
- 导入成功后,准备一个字体文件,我们这里用的是three.js自带的,将
nodel_modules
文件夹下'three/examples/fonts/'
该路径下的helvetiker_regular.typeface.json
和LICENSE
文件提取出来,放在我们项目的public
路径下,一个通常用于存放静态文件的地方
image.png
- 使用
FontLoader
加载字体,并用TextGeometry
生成几何体
- 这里建议先使用
MeshBasicMaterial
,设置wireframe: true
便于观察字体的相关属性,例如bevelThickness
、bevelSize
、bevelSegments
等
- 这一步完成后的效果如下图
/*
* font
*/
const fontLoader = new FontLoader()
fontLoader.load('../public/font/helvetiker_regular.typeface.json', (font) => {
const textGeometry = new TextGeometry('Hello Three.js', {
font, // font实例
size: 0.5, // 字体大小
height: 0.2, // 文本厚度
curveSegments: 6, // 曲线上点的数量
bevelEnabled: true, // 开启斜角
bevelThickness: 0.03, // 上斜角深度
bevelSize: 0.02, // 斜角与原始文本之间的延伸距离
bevelOffset: 0,
bevelSegments: 3, // 斜角分段数
})
const material = new THREE.MeshBasicMaterial({
wireframe: true
})
const text = new THREE.Mesh(textGeometry, material)
scene.add(text)
})
image.png
- 如上图所示,当前字体在坐标系中不在居中的位置,
three.js
提供了简单的居中方法,手动居中的计算方式可参考以下注释的部分,同时可对比2次打印textGeometry.boundingBox
的坐标数值
fontLoader.load(..., (font) => {
...
...
textGeometry.computeBoundingBox() // 计算物体边界
// console.log(textGeometry.boundingBox)
// 移动物体至坐标系中心
// textGeometry.translate(
// - (textGeometry.boundingBox.max.x - 0.02) * 0.5, // 减去bevelSize
// - (textGeometry.boundingBox.max.y - 0.02) * 0.5, // 减去bevelSize
// - (textGeometry.boundingBox.max.z - 0.03) * 0.5, // 减去bevelThickness
// )
textGeometry.center()
// console.log(textGeometry.boundingBox)
})
- 下一步添加torus的部分,这一步需要注意的是,我们将创建
TorusGeometry
的部分放在for循环外面,避免频繁创建所造成的性能消耗,可以通过console.time
观察不同的写法所消耗的时间
fontLoader.load(..., (font) => {
...
...
...
// console.time('donuts')
// torus
const donutGeometry = new THREE.TorusGeometry(0.3, 0.2, 20, 45)
for(let i = 0; i < 100; i++) {
const donut = new THREE.Mesh(donutGeometry, material)
donut.position.x = (Math.random() - 0.5) * 10
donut.position.y = (Math.random() - 0.5) * 10
donut.position.z = (Math.random() - 0.5) * 10
donut.rotation.x = Math.random() * Math.PI
donut.rotation.y = Math.random() * Math.PI
const scale = Math.random()
donut.scale.set(scale, scale, scale)
// 这里特别注意,不能写成这样,xyz坐标值需要保持统一变化
// donut.scale.set(Math.random(), Math.random(), Math.random())
scene.add(donut)
}
// console.timeEnd('donuts')
})
- 最后,通过
TextureLoader
加载新的纹理,替换material
,删除坐标系
/*
* texture
*/
const textureLoader = new THREE.TextureLoader()
const matcapTexture = textureLoader.load('../public/imgs/mat-cap-4.jpg')
fontLoader.load(..., (font) => {
...
...
// const material = new THREE.MeshBasicMaterial({
// wireframe: true
// })
const material = new THREE.MeshMatcapMaterial({
matcap: matcapTexture
})
})
/**
* axesHelper
**/
const axesHelper = new THREE.AxesHelper(5)
// scene.add(axesHelper)
网友评论