three.js如何实现3D动态文字效果


Posted in Javascript onMarch 03, 2021

前言

大家好,这里是 CSS 魔法使——alphardex。

之前在逛国外网站的时候,发现有些网站的文字是刻在3D图形上的,并且能在图形上运动,视觉效果相当不错,于是笔者就也想用three.js来尝试复现出这种效果

three.js如何实现3D动态文字效果

上图只是所有效果的其中之一,接下来让我们一起开干吧~

准备工作

笔者自行封装的three.js模板:Three.js Starter

读者可以点击右下角fork一份后再开始本项目

本项目需要用到位图字体,可以直接复制demo的HTML里的font字体代码

一个注意点:three-bmfont-text这个库依赖全局的three.js,因此要在JS里额外引入一次three.js,如下图

three.js如何实现3D动态文字效果

实现思路

  1. 加载位图字体文件,将其转化为文字对象所需要的形状和材质
  2. 创建文字对象
  3. 创建渲染目标,可以理解为canvas中的canvas,因为接下来我们要将文字对象本身当做贴图
  4. 创建承载字体的容器,将文字对象作为贴图贴上去
  5. 动画

正片

搭好架子

<div class="relative w-screen h-screen">
 <div class="kinetic-text w-full h-full bg-blue-1"></div>
 <div class="font">
 <font>
  一坨从demo里CV而来的字体代码
 </font>
 </div>
</div>
:root {
 --blue-color-1: #2c3e50;
}

.bg-blue-1 {
 background: var(--blue-color-1);
}
import createGeometry from "https://cdn.skypack.dev/three-bmfont-text@3.0.1";
import MSDFShader from "https://cdn.skypack.dev/three-bmfont-text@3.0.1/shaders/msdf";
import parseBmfontXml from "https://cdn.skypack.dev/parse-bmfont-xml@1.1.4";

const font = parseBmfontXml(document.querySelector(".font").innerHTML);
const fontAtlas = "https://i.loli.net/2021/02/20/DcEhuYNjxCgeU42.png";

const kineticTextTorusKnotVertexShader = `(顶点着色器代码,先空着,具体见下文)`;

const kineticTextTorusKnotFragmentShader = `(片元着色器代码,先空着,具体见下文)`;

class KineticText extends Base {
 constructor(sel: string, debug: boolean) {
 super(sel, debug);
 this.cameraPosition = new THREE.Vector3(0, 0, 4);
 this.clock = new THREE.Clock();
 this.meshConfig = {
  torusKnot: {
  vertexShader: kineticTextTorusKnotVertexShader,
  fragmentShader: kineticTextTorusKnotFragmentShader,
  geometry: new THREE.TorusKnotGeometry(9, 3, 768, 3, 4, 3)
  }
 };
 this.meshNames = Object.keys(this.meshConfig);
 this.params = {
  meshName: "torusKnot",
  velocity: 0.5,
  shadow: 5,
  color: "#000000",
  frequency: 0.5,
  text: "ALPHARDEX",
  cameraZ: 2.5
 };
 }
 // 初始化
 async init() {
 this.createScene();
 this.createPerspectiveCamera();
 this.createRenderer(true);
 await this.createKineticText(this.params.text);
 this.createLight();
 this.createOrbitControls();
 this.addListeners();
 this.setLoop();
 }
 // 创建动态文字
 async createKineticText(text: string) {
 await this.createFontText(text);
 this.createRenderTarget();
 this.createTextContainer();
 }
}

加载和创建字体

首先加载字体文件,并创建出形状和材质,有了这两样就能创建出字体对象了

class KineticText extends Base {
 loadFontText(text: string): any {
 return new Promise((resolve) => {
  const fontGeo = createGeometry({
  font,
  text
  });
  const loader = new THREE.TextureLoader();
  loader.load(fontAtlas, (texture) => {
  const fontMat = new THREE.RawShaderMaterial(
   MSDFShader({
   map: texture,
   side: THREE.DoubleSide,
   transparent: true,
   negate: false,
   color: 0xffffff
   })
  );
  resolve({ fontGeo, fontMat });
  });
 });
 }
 async createFontText(text: string) {
 const { fontGeo, fontMat } = await this.loadFontText(text);
 const textMesh = this.createMesh({
  geometry: fontGeo,
  material: fontMat
 });
 textMesh.position.set(-0.965, -0.525, 0);
 textMesh.rotation.set(ky.deg2rad(180), 0, 0);
 textMesh.scale.set(0.008, 0.025, 1);
 this.textMesh = textMesh;
 }
}

着色器

顶点着色器

通用模板,直接CV即可

varying vec2 vUv;
varying vec3 vPosition;

void main(){
 vec4 modelPosition=modelMatrix*vec4(position,1.);
 vec4 viewPosition=viewMatrix*modelPosition;
 vec4 projectedPosition=projectionMatrix*viewPosition;
 gl_Position=projectedPosition;
 
 vUv=uv;
 vPosition=position;
}

片元着色器

利用fract函数创建重复的贴图,加上位移距离displacement使得贴图能随着时间的增加而动起来,再用clamp函数来根据z轴大小限定阴影的范围,意思是离画面越远则阴影越重,反之离画面越近则阴影越轻

uniform sampler2D uTexture;
uniform float uTime;
uniform float uVelocity;
uniform float uShadow;

varying vec2 vUv;
varying vec3 vPosition;

void main(){
 vec2 repeat=vec2(12.,3.);
 vec2 repeatedUv=vUv*repeat;
 vec2 displacement=vec2(uTime*uVelocity,0.);
 vec2 uv=fract(repeatedUv+displacement);
 vec3 texture=texture2D(uTexture,uv).rgb;
 // texture*=vec3(uv.x,uv.y,1.);
 float shadow=clamp(vPosition.z/uShadow,0.,1.);// farther darker (to 0).
 vec3 color=vec3(texture*shadow);
 gl_FragColor=vec4(color,1.);
}

此时文本显示到了屏幕上

创建渲染目标

为了将字体对象本身作为贴图,创建了一个渲染目标

class KineticText extends Base {
 createRenderTarget() {
 const rt = new THREE.WebGLRenderTarget(
  window.innerWidth,
  window.innerHeight
 );
 this.rt = rt;
 const rtCamera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
 rtCamera.position.z = this.params.cameraZ;
 this.rtCamera = rtCamera;
 const rtScene = new THREE.Scene();
 rtScene.add(this.textMesh);
 this.rtScene = rtScene;
 }
}

创建字体容器

创建一个容器,并将字体对象本身作为贴图贴上去,再应用动画即可完成

class KineticText extends Base {
 createTextContainer() {
 if (this.mesh) {
  this.scene.remove(this.mesh);
  this.mesh = null;
  this.material!.dispose();
  this.material = null;
 }
 this.rtScene.background = new THREE.Color(this.params.color);
 const meshConfig = this.meshConfig[this.params.meshName];
 const geometry = meshConfig.geometry;
 const material = new THREE.ShaderMaterial({
  vertexShader: meshConfig.vertexShader,
  fragmentShader: meshConfig.fragmentShader,
  uniforms: {
  uTime: {
   value: 0
  },
  uVelocity: {
   value: this.params.velocity
  },
  uTexture: {
   value: this.rt.texture
  },
  uShadow: {
   value: this.params.shadow
  },
  uFrequency: {
   value: this.params.frequency
  }
  }
 });
 this.material = material;
 const mesh = this.createMesh({
  geometry,
  material
 });
 this.mesh = mesh;
 }
 update() {
 if (this.rtScene) {
  this.renderer.setRenderTarget(this.rt);
  this.renderer.render(this.rtScene, this.rtCamera);
  this.renderer.setRenderTarget(null);
 }
 const elapsedTime = this.clock.getElapsedTime();
 if (this.material) {
  this.material.uniforms.uTime.value = elapsedTime;
 }
 }
}

别忘了把相机调远一些

this.cameraPosition = new THREE.Vector3(0, 0, 40);

风骚的动态文字出现了:)

three.js如何实现3D动态文字效果

项目地址

Kinetic Text

demo里不止本文创建的这一种形状,大家可以随意把玩。

总结

到此这篇关于three.js如何实现3D动态文字效果的文章就介绍到这了,更多相关three.js 3D动态文字内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
JS 两日期相减,获得天数的小例子(兼容IE,FF)
Jul 01 Javascript
js综合应用实例简单的表格统计
Sep 03 Javascript
$(document).ready(function() {})不执行初始化脚本
Jun 19 Javascript
JavaScript数据结构和算法之图和图算法
Feb 11 Javascript
JavaScript SHA1加密算法实现详细代码
Oct 06 Javascript
vue-axios使用详解
May 10 Javascript
理解javascript async的用法
Aug 22 Javascript
Bootstrap模态对话框中显示动态内容的方法
Aug 10 Javascript
如何给element添加一个抽屉组件的方法步骤
Jul 14 Javascript
vue filter 完美时间日期格式的代码
Aug 14 Javascript
JavaScript常用工具函数大全
May 06 Javascript
vue实现打地鼠小游戏
Aug 21 Javascript
Webpack3+React16代码分割的实现
Mar 03 #Javascript
微信小程序input抖动问题的修复方法
Mar 03 #Javascript
微信小程序组件生命周期的踩坑记录
Mar 03 #Javascript
vite2.0+vue3移动端项目实战详解
Mar 03 #Vue.js
基于JavaScript实现简单的轮播图
Mar 03 #Javascript
js面向对象方式实现拖拽效果
Mar 03 #Javascript
Vue多选列表组件深入详解
Mar 02 #Vue.js
You might like
php使用base64加密解密图片示例分享
2014/01/20 PHP
php数组去除空值函数分享
2015/02/02 PHP
Symfony2框架学习笔记之HTTP Cache用法详解
2016/03/18 PHP
PHP静态延迟绑定和普通静态效率的对比
2017/10/20 PHP
浅析PHP反序列化中过滤函数使用不当导致的对象注入问题
2020/02/15 PHP
input按钮的事件处理大全
2010/12/10 Javascript
jQuery获取文本节点之 text()/val()/html() 方法区别
2011/03/01 Javascript
浅谈JavaScript中Date(日期对象),Math对象
2015/02/05 Javascript
JQuery实现展开关闭层的方法
2015/02/17 Javascript
JS实现浏览器状态栏显示时间的方法
2015/10/27 Javascript
深入剖析JavaScript:Object类型
2016/05/10 Javascript
bootstrap按钮插件(Button)使用方法解析
2017/01/13 Javascript
JavaScript实现HTML5游戏断线自动重连的方法
2017/09/18 Javascript
JS中promise化微信小程序api
2018/04/12 Javascript
element-ui循环显示radio控件信息的方法
2018/08/24 Javascript
解决angular双向绑定无效果,ng-model不能正常显示的问题
2018/10/02 Javascript
JavaScript实现小球沿正弦曲线运动
2020/09/07 Javascript
JS数组求和的常用方法实例小结
2019/01/07 Javascript
JavaScript 判断iPhone X Series机型的方法
2019/01/28 Javascript
vue--vuex详解
2019/04/15 Javascript
一步一步实现Vue的响应式(对象观测)
2019/09/02 Javascript
机器学习python实战之手写数字识别
2017/11/01 Python
python获取时间及时间格式转换问题实例代码详解
2018/12/06 Python
Opencv求取连通区域重心实例
2020/06/04 Python
Python中flatten( ),matrix.A用法说明
2020/07/05 Python
python报错TypeError: ‘NoneType‘ object is not subscriptable的解决方法
2020/11/05 Python
python 用pandas实现数据透视表功能
2020/12/21 Python
什么是TCP/IP
2014/07/27 面试题
护士见习期自我鉴定
2014/02/08 职场文书
经典英文广告词
2014/03/18 职场文书
爽歪歪广告词
2014/03/20 职场文书
社区党建工作汇报材料
2014/08/14 职场文书
关于清明节的演讲稿
2014/09/13 职场文书
入队仪式主持词
2015/07/04 职场文书
Django 实现jwt认证的示例
2021/04/30 Python
Vue提供的三种调试方式你知道吗
2022/01/18 Vue.js