如何用Three.js的TextGeometry实现3D文本动态绘制效果?
实现Three.js TextGeometry涂鸦式逐步构建动画
嘿,我完全懂你想要的那种效果——游戏结束时,得分数字像涂鸦一样在墙上慢慢“画”出来,而不是直接跳出来,对吧?用TextGeometry完全能实现这个需求,你之前想到的顶点渐进显示思路是对的,下面给你具体的实现方案和代码示例:
核心思路:用着色器控制顶点渐进显现
直接操作几何体顶点来显示/隐藏虽然可行,但性能不够友好(尤其是复杂字体)。更优的方式是用自定义着色器材质(ShaderMaterial),给每个顶点添加延迟显示的属性,通过着色器根据时间动态控制每个顶点的可见性,这样能实现流畅的涂鸦动画。
具体步骤 + 代码示例
1. 加载字体并创建TextGeometry
首先加载字体文件,创建文本几何体,然后给每个顶点添加自定义的延迟属性:
import * as THREE from 'three'; import { TextGeometry } from 'three/addons/geometries/TextGeometry.js'; import { FontLoader } from 'three/addons/loaders/FontLoader.js'; // 初始化场景、相机、渲染器(基础代码省略,你应该已经有了) 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); camera.position.z = 5; // 加载字体(替换成你自己的字体路径) const fontLoader = new FontLoader(); fontLoader.load('fonts/helvetiker_regular.typeface.json', (font) => { // 创建得分文本几何体 const scoreText = '9999'; // 这里可以替换成动态得分 const textGeometry = new TextGeometry(scoreText, { font: font, size: 1.5, height: 0.15, curveSegments: 12, bevelEnabled: true, bevelThickness: 0.08, bevelSize: 0.08 }); textGeometry.center(); // 让文本居中 // 给每个顶点添加延迟显示属性 const delayArray = []; const totalVertices = textGeometry.attributes.position.count; // 这里我们按顶点索引分配延迟,让文本从左到右、从上到下逐步显现 // 如果要更精准的笔画顺序,后面会讲优化方案 textGeometry.attributes.position.array.forEach((_, idx) => { // 计算每个顶点的延迟时间,总动画时长设为2秒 const delay = (idx / totalVertices) * 2; delayArray.push(delay, delay, delay); // 每个顶点对应xyz三个分量 }); // 将延迟属性添加到几何体 textGeometry.setAttribute('aDelay', new THREE.Float32BufferAttribute(delayArray, 3)); // 创建自定义着色器材质 const graffitiMaterial = new THREE.ShaderMaterial({ uniforms: { uTime: { value: 0 }, uFadeDuration: { value: 0.3 }, // 单个顶点的淡入时长 uTextColor: { value: new THREE.Color(0x00ff00) } // 绿色涂鸦效果,可自定义 }, vertexShader: ` attribute float aDelay; uniform float uTime; uniform float uFadeDuration; varying float vOpacity; void main() { // 计算当前顶点的透明度:时间超过延迟后开始淡入 vOpacity = clamp((uTime - aDelay) / uFadeDuration, 0.0, 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` varying float vOpacity; uniform vec3 uTextColor; void main() { // 完全透明时丢弃片段,避免影响性能 if (vOpacity < 0.05) discard; gl_FragColor = vec4(uTextColor, vOpacity); } `, transparent: true, side: THREE.DoubleSide }); // 创建文本网格并添加到场景 const textMesh = new THREE.Mesh(textGeometry, graffitiMaterial); scene.add(textMesh); // 动画循环 function animate() { requestAnimationFrame(animate); // 更新时间,控制动画进度 graffitiMaterial.uniforms.uTime.value += 0.01; renderer.render(scene, camera); } animate(); });
2. 优化:按笔画顺序显现
上面的示例是按顶点索引顺序显现,如果你想要更贴近真实涂鸦的笔画顺序,可以:
- 从
TextGeometry中提取每个字形的Shape对象 - 使用
THREE.ShapeUtils遍历Shape的曲线路径,计算每个顶点在路径上的位置占比,以此作为延迟时间的依据 - 这样每个数字会按照它的笔画顺序逐步显现,更符合涂鸦的感觉
3. 额外效果增强
- 可以在顶点着色器中添加微小的位移动画,让顶点显现时稍微“跳动”一下,模拟涂鸦的随性
- 给材质添加纹理,模拟喷漆或粉笔的质感,让效果更真实
关键提示
- 字体加载是异步操作,一定要在加载完成后再创建几何体
- 如果得分是动态变化的,每次更新得分时需要重新生成
TextGeometry并添加延迟属性 - 调整
uFadeDuration和总动画时长参数,可以控制涂鸦速度的快慢
内容的提问来源于stack exchange,提问作者JJ Gerrish
相关产品推荐
相关产品推荐

