【Three.js】第十三章 3D Text 3D文字

13. 3D Text 3D文字

介绍

我们已经了解足够的基础知识,现在可以创作一些好看的效果了。对于我们第一个正式的项目,我们将复刻一个开发者ilithya的作品(https://www.ilithya.rocks/),这个作品在场景中间有一个大的 3D 文本,很多几何体漂浮在文字的周围。
这个作品是学习 Three.js 早期可以实现的一个很好的例子。它简单、高效,而且特效看起来很棒。
Three.js 已经通过TextGeometry类支持 3D 文本几何图形。问题是你必须先指定一种字体,而且这个字体必须是一种叫做 typeface 的特定 json 格式。
我们不会涉及数字字体版权授权相关的问题,您使用下载字体后,使用字体时必须保证有权使用该字体,或者字体版权是供开发者免费使用的。

如何获得字体

有很多方法可以获取 typeface 格式的字体。首先,您可以使用如下转换器转换您的字体:https://gero3.github.io/facetype.js/。您必须提供一个文件并单击转换按钮。
您还可以在node_modules文件夹中的 Three.js 库示例中找到字体。/node_modules/three/examples/fonts/你可以把这些字体放在/static/文件夹中,或者你可以直接在你的 JavaScript 文件中导入它们,因为它们是 json 并且 Vite 中.json文件就像.js的文件一样被支持:

import typefaceFont from 'three/examples/fonts/helvetiker_regular.typeface.json'

我们打开/node_modules/three/examples/fonts/,获取helvetiker_regular.typeface.json文件和LICENSE文件并将它们放入/static/fonts/文件夹(您需要创建fonts文件夹)来混合使用这两种技术。
现在只需在基本 URL 的末尾写入即可访问该/fonts/helvetiker_regular.typeface.json字体。

加载字体

要加载字体,我们必须使用一个名为FontLoader的新加载器类。
此类在THREE变量中不可用。像在前面课程中我们所做的,像导入OrbitControls那样导入它:

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js'

这个加载器像TextureLoader一样工作。在该部分之后添加以下代码textureLoader(如果您使用的是其他字体,请不要忘记更改路径):

/**
 * Fonts
 */
const fontLoader = new FontLoader()

fontLoader.load(
    '/fonts/helvetiker_regular.typeface.json',
    (font) =>
    {
        console.log('loaded')
    }
)

进入你的控制台发现打印了'loaded'。如果不是,请检查前面的步骤并在控制台中搜索潜在的错误。
我们现在可以通过使用函数内的font变量来访问字体。与TextureLoader不同,我们必须在该函数中的成功回调编写其余代码。

创建几何体

正如我们之前所说,我们将使用TextGeometry来创建几何体。
就像FontLoader一样,我们需要导入它:

import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'

请注意文档页面上的示例代码;这些值比我们场景中的值大得多。
确保在成功的回调函数中编写代码:

fontLoader.load(
    '/fonts/helvetiker_regular.typeface.json',
    (font) =>
    {
        const textGeometry = new TextGeometry(
            'Hello Three.js',
            {
                font: font,
                size: 0.5,
                height: 0.2,
                curveSegments: 12,
                bevelEnabled: true,
                bevelThickness: 0.03,
                bevelSize: 0.02,
                bevelOffset: 0,
                bevelSegments: 5
            }
        )
        const textMaterial = new THREE.MeshBasicMaterial()
        const text = new THREE.Mesh(textGeometry, textMaterial)
        scene.add(text)
    }
)


您应该得到一个需要改进的白色 3D 文本。
首先,注释掉立方体的代码。其目的是确保一切正常。

如果您想看到一些很酷的网格,请添加wireframe: true到您的材料中。

const textMaterial = new THREE.MeshBasicMaterial({ wireframe: true })


您现在可以看到几何体是有很多三角形生成的。创建文本几何图形对计算机来说既漫长又困难。curveSegments避免这样做太多次,并通过减少多边形和bevelSegments属性使几何体尽可能保持低。
一旦您对几何体渲染详细程度感到满意,请删除wireframe

文本居中

有几种方法可以使文本居中。一种方法是使用边界。边界是与几何相关联的信息,它告诉该几何占用了哪些空间。它可以是一个盒子或一个球体。

你实际上看不到这些边界,但它可以帮助 Three.js 轻松计算对象是否在屏幕上,如果不在屏幕上,则对象甚至不会被渲染。这称为视锥体剔除,但这不是本课的主题。
我们想要的是使用这个边界来了解几何体的大小并使它重新居中。默认情况下,Three.js 使用球体边界。我们想要的是一个盒子边界,更准确地说。为此,我们可以要求 Three.js 通过调用computeBoundingBox()几何来计算此框边界:

textGeometry.computeBoundingBox()

我们可以使用boundingBox几何属性选中此框。

console.log(textGeometry.boundingBox)

结果是一个名为Box3 的对象,它有一个min属性和一个max属性。该min0并不像我们预期的那样。这是由于bevelThicknessand bevelSize,但我们现在可以忽略它。
现在我们有了措施,我们可以移动对象。我们不移动网格,而是移动整个几何体。这样,网格仍将位于场景的中心,文本几何体也将在我们的网格内居中。
为此,我们可以在方法translate(...)之后立即在几何体上使用该方法computeBoundingBox()

textGeometry.translate(
    - textGeometry.boundingBox.max.x * 0.5,
    - textGeometry.boundingBox.max.y * 0.5,
    - textGeometry.boundingBox.max.z * 0.5
)


文本居中,但如果你想非常精确,你还应该减去 bevelSizeis 0.02

textGeometry.translate(
    - (textGeometry.boundingBox.max.x - 0.02) * 0.5, // Subtract bevel size
    - (textGeometry.boundingBox.max.y - 0.02) * 0.5, // Subtract bevel size
    - (textGeometry.boundingBox.max.z - 0.03) * 0.5  // Subtract bevel thickness
)

我们在这里所做的实际上可以通过调用几何上的方法center()更快地完成:

textGeometry.center()

容易多了,不是吗?我们手写居中做的目的是了解边界和截锥体剔除

添加matcap材质

是时候为我们的文本添加一个很酷的材料了。我们将使用 MeshMatcapMaterial 替换 MeshBasicMaterial,因为它看起来很酷,而且性能更好。
首先,让我们选择一个 matcap 纹理。我们将使用位于/static/textures/matcaps/文件夹中的 matcaps,但您可以随意使用您自己的 matcaps
您也可以从此存储库https://github.com/nidorx/matcaps下载一个。不要花太多时间去选择它!如果不是供个人使用,请确保您有版权使用它。您不需要高分辨率的纹理,256x256应该绰绰有余。
我们现在可以使用代码中已有的TextureLoader来加载纹理:

const matcapTexture = textureLoader.load('/textures/matcaps/1.png')

我们现在可以用漂亮的MeshMatcapMaterial替换丑陋的MeshBasicMaterial并将我们的matcapTexture变量与matcap属性一起使用:

const textMaterial = new THREE.MeshMatcapMaterial({ matcap: matcapTexture })


你应该可以渲染出一个可爱的文字,上面有一个很酷的材料。

添加对象

让我们添加漂浮的对象。为此,我们将在循环函数内创建一个甜甜圈。
在成功函数中,紧跟在该text部分之后,添加循环函数:

for(let i = 0; i < 100; i++)
{
    
}

我们可以在 success 函数之外完成此操作,但我们需要将文本和对象一起创建,这是有充分理由的,您稍后会看到。
在此循环中,创建一个TorusGeometry(例如甜甜圈的技术名称),其材质与文本和Mesh相同:

for(let i = 0; i < 100; i++)
{
    const donutGeometry = new THREE.TorusGeometry(0.3, 0.2, 20, 45)
    const donutMaterial = new THREE.MeshMatcapMaterial({ matcap: matcapTexture })
    const donut = new THREE.Mesh(donutGeometry, donutMaterial)
    scene.add(donut)
}


你应该在同一个地方得到 100 个甜甜圈。
让我们为他们的位置添加一些随机性:

donut.position.x = (Math.random() - 0.5) * 10
donut.position.y = (Math.random() - 0.5) * 10
donut.position.z = (Math.random() - 0.5) * 10


你应该把 100 个甜甜圈分散在现场。
为旋转添加随机性。无需旋转所有 3 个轴,并且由于甜甜圈是对称的,旋转半圈就足够了:

donut.rotation.x = Math.random() * Math.PI
donut.rotation.y = Math.random() * Math.PI


甜甜圈应该向各个方向旋转。
最后,我们可以为比例添加随机性。不过要小心;我们需要对所有 3 个轴( x, y, z)使用相同的值:

const scale = Math.random()
donut.scale.set(scale, scale, scale)

优化

我们的代码不是最优的。正如我们在上一课中看到的,我们可以在多个网格上使用相同的材质,但我们也可以使用相同的几何体来节省性能。
thedonutGeometrythedonutMaterial移出循环:

const donutGeometry = new THREE.TorusGeometry(0.3, 0.2, 20, 45)
const donutMaterial = new THREE.MeshMatcapMaterial({ matcap: matcapTexture })

for(let i = 0; i < 100; i++)
{
    // ...
}

你应该得到相同的结果,但我们可以走得更远。text的材料与donut 的相同。
让我们删除donutMaterial,重命名textMaterialbymaterial并将其用于 thetextdonut

const material = new THREE.MeshMatcapMaterial({ matcap: matcapTexture })
                
// ...

const text = new THREE.Mesh(textGeometry, material)

// ...

for(let i = 0; i < 100; i++)
{
    const donut = new THREE.Mesh(donutGeometry, material)
    
    // ...
}

我们可以继续优化,但是有一个关于优化的专门课程,所以先按下不表。

更多优化

如果需要,您可以添加更多形状,为它们设置动画,甚至可以尝试其他 matcaps

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 three.js文字生成库(例如 Three-bmfont-text 或 opentype.js)来实现 3D 粒子动态生成文字。下面是一个简单的示例代码: ```javascript // 创建场景、相机、渲染器等 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建粒子系统 const particleCount = 5000; const particles = new THREE.Geometry(); for (let i = 0; i < particleCount; i++) { const particle = new THREE.Vector3( Math.random() * 500 - 250, Math.random() * 500 - 250, Math.random() * 500 - 250 ); particles.vertices.push(particle); } const particleMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 2, }); const particleSystem = new THREE.Points(particles, particleMaterial); scene.add(particleSystem); // 使用 Three-bmfont-text 创建文字 const fontLoader = new THREE.FontLoader(); fontLoader.load('path/to/font.json', (font) => { const textGeometry = createTextGeometry('Hello, World!', { font, size: 50, height: 10, }); const textMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff }); const textMesh = new THREE.Mesh(textGeometry, textMaterial); // 将文字放置到粒子系统中心位置 const center = new THREE.Vector3(0, 0, 0); textGeometry.computeBoundingBox(); const textCenter = textGeometry.boundingBox.getCenter(center); textMesh.position.set(-textCenter.x, -textCenter.y, -textCenter.z); particleSystem.add(textMesh); }); // 渲染场景 function animate() { requestAnimationFrame(animate); particleSystem.rotation.y += 0.005; renderer.render(scene, camera); } animate(); ``` 需要注意的是,上述示例代码仅仅是一个简单的示例,实际应用中还需要考虑更多的细节,例如相机和粒子系统的位置、粒子系统的大小和形状等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值