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 相关文章推荐
Display SQL Server Version Information
Jun 21 Javascript
基于jQuery的message插件实现右下角弹出消息框
Jan 11 Javascript
JS弹出层单纯的绝对定位居中示例代码
Feb 18 Javascript
简洁实用的BootStrap jQuery手风琴插件
Aug 31 Javascript
深入理解Vue.js源码之事件机制
Sep 27 Javascript
vue实现商城购物车功能
Nov 27 Javascript
JS组件系列之Gojs组件 前端图形化插件之利器
Nov 29 Javascript
vue自定义指令的创建和使用方法实例分析
Dec 04 Javascript
浅谈laytpl 模板空值显示null的解决方法及简单的js表达式
Sep 19 Javascript
layui时间控件选择时间范围的实现方法
Sep 28 Javascript
d3.js实现图形缩放平移
Dec 19 Javascript
vue项目配置同一局域网可使用ip访问的操作
Oct 23 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合并数组的2种方法小结
2016/11/24 PHP
js将long日期格式转换为标准日期格式实现思路
2013/04/07 Javascript
javascript实现跨域的方法汇总
2015/06/25 Javascript
基于jquery实现鼠标左右拖动滑块滑动附源码下载
2015/12/23 Javascript
AngularJS入门教程之MVC架构实例分析
2016/11/01 Javascript
微信小程序 tabs选项卡效果的实现
2017/01/05 Javascript
jQuery查找和过滤_动力节点节点Java学院整理
2017/07/04 jQuery
vue中子组件向父组件传递数据的实例代码(实现加减功能)
2018/04/20 Javascript
jQuery使用each遍历循环的方法
2018/09/19 jQuery
javascript+HTML5 canvas绘制时钟功能示例
2019/05/15 Javascript
vue中@change兼容问题详解
2019/10/25 Javascript
JavaScript实现简易聊天对话框(加滚动条)
2020/02/10 Javascript
Electron 打包问题:electron-builder 下载各种依赖出错(推荐)
2020/07/09 Javascript
[23:21]Ti4 冒泡赛第二轮DK vs C9 2
2014/07/14 DOTA
[02:12]Dota 2 推出全新英雄—— 电炎绝手
2019/08/23 DOTA
Python安装Imaging报错:The _imaging C module is not installed问题解决方法
2014/08/22 Python
利用python生成一个导出数据库的bat脚本文件的方法
2016/12/30 Python
Python查看微信撤回消息代码
2018/06/07 Python
python2与python3中关于对NaN类型数据的判断和转换方法
2018/10/30 Python
用django-allauth实现第三方登录的示例代码
2019/06/24 Python
Python3简单爬虫抓取网页图片代码实例
2019/08/26 Python
python科学计算之narray对象用法
2019/11/25 Python
Python定义函数时参数有默认值问题解决
2019/12/19 Python
新手常见Python错误及异常解决处理方案
2020/06/18 Python
如何使用 Python 读取文件和照片的创建日期
2020/09/05 Python
Python QT组件库qtwidgets的使用
2020/11/02 Python
pytorch学习教程之自定义数据集
2020/11/10 Python
Pycharm如何自动生成头文件注释
2020/11/14 Python
麦德龙官方海外旗舰店:德国麦德龙超市
2017/12/23 全球购物
Keds加拿大官网:购买帆布运动鞋和皮鞋
2019/09/26 全球购物
Moss Bros官网:英国排名第一的西装店
2020/02/26 全球购物
中学门卫岗位职责
2013/12/26 职场文书
中文专业毕业生自荐书范文
2014/01/04 职场文书
物流管理专业推荐信
2014/09/06 职场文书
大四学生个人总结
2015/02/15 职场文书
2016春季校长开学典礼致辞
2015/11/26 职场文书