Three.js 入门系列(7):让甜甜圈围着我转 —— 3D文字生成

235 阅读7分钟

时隔一个多月,我重新打开了 Three.js 编辑器。今天来做 3D 文字,并在周围散布 100 个甜甜圈。

效果预览

效果预览.gif

接下来我会从7个方面仔细讲解制作的流程,跟我一起来吧!

1.引入文字加载器

我们需要认识两个Three.js 官方示例里的扩展模块: 1.FontLoader:专门加载字体文件(.typeface.json--这个是一种字体格式)的加载器。

2.TextGeometry:根据字体数据生成文字形状的几何体。

那FontLoader 和 TextGeometry 究竟是什么?

生活比喻:做饼干

想象你要做一批字母饼干:

  • FontLoader 是饼干模具的图纸——它告诉系统“字母A应该长什么样、B应该长什么样”
  • TextGeometry 是压模的动作——用图纸把面团压成字母形状
  • 两者配合,才能做出真正的字母饼干
js
    import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'
    import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'

注意:两个模块都需要单独引入

小贴士:Three.js 核心库(three)只包含最基础的功能——场景、相机、渲染器、几何体、材质等。这些是“地基”。

但是文字生成不是所有3D场景都需要的功能,所以 Three.js 团队把它放在 examples/jsm/  目录下,作为“扩展包”提供。你需要的时候再单独导入。

2.创建文字加载器

这个非常简单,只需:

    const fontLoader = new FontLoader()

和纹理加载器(TextureLoader)类似,字体加载器也是用来异步加载外部资源的。

3.加载字体 + 创建文字

js

fontLoader.load(
    '/fonts/helvetiker_regular.typeface.json',  // ① 字体文件路径
    (font) => {                                  // ② 加载完成的回调函数
        const textGeometry = new TextGeometry(
            'King Yueyue!',                      // ③ 要显示的文字
            {                                    // ④ 文字配置参数
                font: font,
                size: 0.5,
                depth: 0.2,
                curveSegments: 4,
                bevelEnabled: true,
                bevelThickness: 0.03,
                bevelSize: 0.02,
                bevelOffset: 0,
                bevelSegments: 3
            }
        )
        // 后续处理:居中、材质、添加到场景
    }
)

① 字体文件路径:'/fonts/helvetiker_regular.typeface.json'

这是字体数据文件的位置。helvetiker_regular 是 Three.js 官方提供的默认字体(Helvetica 的变体),.typeface.json 是一种特殊的 JSON 格式,包含了字体的轮廓数据——也就是“每个字母由哪些点和曲线组成”。

② 回调函数:(font) => { ... }

fontLoader.load() 是异步的——加载字体文件需要时间(从服务器读取文件)。回调函数会在字体加载完成后执行,font 参数就是加载好的字体数据。

④ 文字配置参数(这里是重点)

参数说明示例值生活比喻
font加载好的字体数据font选定了“什么字体”(黑体/宋体/楷体)
size文字大小0.5字号大小(相当于Word里的字号)
depth文字的厚度(Z轴方向)0.2字母的“高度”,像浮雕凸起多少
curveSegments曲线平滑度4字母边缘的分段数,越高越平滑
bevelEnabled是否启用倒角true字母边缘是否做圆角处理
bevelThickness倒角的厚度0.03边缘圆角的“宽度”
bevelSize倒角的大小0.02边缘圆角的“深度”
bevelOffset倒角偏移0倒角位置偏移(一般不动)
bevelSegments倒角分段数3圆角过渡的平滑度

关于 bevel(倒角)的直观理解

javascript

bevelEnabled: true   // 开圆角
bevelThickness: 0.03 // 圆角有多厚
bevelSize: 0.02      // 圆角有多大

三个参数分别控制什么?

1. bevelEnabled: true —— 要不要开倒角?

true = 字母边缘做圆角处理(推荐,看起来更精致) 圆角边.gif false = 字母边缘是锐利的直角(硬边风格)

硬边.gif

2. bevelThickness: 0.03 —— 倒角延伸了多远

这个参数控制的是:圆角从边缘往内部走了多少距离。

3. bevelSize: 0.02 —— 倒角向上走了多高

这个参数控制的是:圆角从底部往上延伸了多少距离。

以上两个参数同时增加的话,这个圆角会变的很大,很圆,就好像桌角被打磨成一个光滑的圆角一样。

当前的值(前面有相关代码):

小的.gif

当我给一个大一些的值:

     bevelEnabled: true,
     bevelThickness: 0.09,
     bevelSize: 0.09,

大一些的值.gif

通过以上的对比可以看出,当这两个数值增加的时候,圆角看起来弧度更大,同时你也可以只增加其中一个值。

4. 居中(重点)

TextGeometry 生成的文字,默认原点在左下角,需要把它移到中心。

如果我们肉眼看不清楚具体的中心在哪里,可以借助之前学习的坐标轴辅助器(axesHelper)

    //坐标轴
    const axesHelper = new THREE.AxesHelper()
    scene.add(axesHelper)

居中方法我们直接使用内置方法,一行代码,自动居中。简单直接,建议用这个。

    textGeometry.center()

居中.gif

直接居中了,节约了非常多的时间。

5.赋予材质

终于来到最好玩步骤之一,我们使用的材质依旧是之前代码中出现过的,这里用了 Matcap 材质(纹理贴图模拟光照),不需要添加光源就能有立体感。

MeshMatcapMaterial 是什么?

一句话解释:

Matcap(Material Capture,材质捕获)是一种“假光照”技术——用一张贴图模拟光照效果,不需要在场景里添加任何光源,物体就有立体感,就像日常拍照喜欢添加滤镜一样,不需要真实的光源就可以让照片拥有质感,Matcap就是3d世界的“光照滤镜”。

/**
 * Textures创建纹理加载器,加载所需纹理
 */
const textureLoader = new THREE.TextureLoader()
const matcapTexture = textureLoader.load('/textures/matcaps/8.png')

 //把纹理涂在文字上,文字出现在场景中
 const material = new THREE.MeshMatcapMaterial({ matcap: matcapTexture })
        const text = new THREE.Mesh(textGeometry, material)
        scene.add(text)

可以看到文字拥有一种光泽,柔和的陶瓷或塑料质感。

材质.gif

6. 创建甜甜圈(装饰元素)

const donutGeometry = new THREE.TorusGeometry(0.3, 0.2, 20, 45)

TorusGeometry 是什么?

一句话解释:

TorusGeometry 是 Three.js 用来生成“甜甜圈”形状(数学上叫圆环体)的几何体。它像一个被拉圆的游泳圈,或者一个饱满的甜甜圈。

四个参数详解

    new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments)
//                            ↑       ↑         ↑                ↑
//                         主半径   管道半径  径向分段数       管状分段数
  • radius(0.3):甜甜圈整体大小,从中心到管道的距离
  • tube(0.2):甜甜圈的粗细,管道半径
  • radialSegments(20):环绕圈数的分段数,值越大越圆滑
  • tubularSegments(45):管道截面的分段数,值越大横截面越圆

这四个值一起决定了甜甜圈的胖瘦、大小和光滑程度。我用的这组数值做出来就是比较饱满圆润的效果,看着挺好吃的。

7. 调整甜甜圈的位置、旋转、大小

用循环生成 100 个甜甜圈,每个都随机分布:

for(let i = 1; i < 100; i++) {
    const donut = new THREE.Mesh(donutGeometry, material)
    
    // 位置随机(范围 -5 到 5)
    donut.position.x = (Math.random() - 0.5) * 10
    donut.position.y = (Math.random() - 0.5) * 10
    donut.position.z = (Math.random() - 0.5) * 10
    
    // 旋转随机(0 到 180度)
    donut.rotation.x = Math.random() * Math.PI
    donut.rotation.y = Math.random() * Math.PI
    
    // 缩放随机(0 到 1 之间)
    const scale = Math.random()
    donut.scale.set(scale, scale, scale)
    
    scene.add(donut)
}

至于我们为什么要调整这些参数,就是想把甜甜圈分散开来,文字是主角,甜甜圈是气氛组。100个随机洒在周围,有大有小、转得乱七八糟,画面就不那么死板了。主要是让场景看着热闹一点,不然就几个字母杵在那儿太孤单了。

效果预览.gif

总结

今天搞了三件事:加载字体、生成文字、洒了100个甜甜圈当气氛组。

文字是主角,甜甜圈是群演,随机洒在周围,让画面看着热闹不孤单。TextGeometry 的参数调一调能玩出不少花样,matcap 材质也省去了打光的麻烦。

虽然就是个简单的3D文字,但加上甜甜圈之后,场景就变得不那么死板了。你可以换文字内容、换字体、换装饰物,或者寻找一些可以使用的matcap材质进行切换,甚至把甜甜圈换成别的东西——发挥空间还挺大的。

📚 本文是学习 Three.js Journey(Bruno Simon 的付费课程)的学习笔记,代码为个人练习所写,概念讲解融入了个人的理解和比喻。