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创建和操作表格的函数集合
May 07 Javascript
js function使用心得
May 10 Javascript
jquery入门—编写一个导航条(可伸缩)
Jan 07 Javascript
jQuery循环动画与获取组件尺寸的方法
Feb 02 Javascript
全面解析JavaScript的Backbone.js框架中的Router路由
May 05 Javascript
浅谈jquery拼接字符串效率比较高的方法
Feb 22 Javascript
详解在Vue中通过自定义指令获取dom元素
Mar 04 Javascript
Vue 实用分页paging实例代码
Apr 12 Javascript
mac上node.js环境的安装测试
Jul 03 Javascript
javascript实现考勤日历功能
Nov 29 Javascript
vue-cli3使用 DllPlugin 实现预编译提升构建速度
Apr 24 Javascript
vue-admin-template配置快捷导航的代码(标签导航栏)
Sep 04 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 HandlerSocket的使用
2011/05/02 PHP
浅谈php安全性需要注意的几点事项
2014/07/17 PHP
php比较两个字符串长度的方法
2015/07/13 PHP
php实现当前页面点击下载文件的简单方法
2016/09/22 PHP
javascript 年月日联动实现核心代码
2009/12/21 Javascript
jQuery 获取对象 根据属性、内容匹配, 还有表单元素匹配
2010/05/31 Javascript
使用JavaScript实现网页版Pongo设计思路及源代码分享
2014/06/16 Javascript
jQuery实现拖动调整表格单元格大小的代码实例
2015/01/13 Javascript
深入理解JavaScript系列(42):设计模式之原型模式详解
2015/03/04 Javascript
js中setTimeout的妙用--防止循环超时
2017/03/06 Javascript
关于JavaScript的单双引号嵌套问题
2017/08/20 Javascript
node前端开发模板引擎Jade的入门
2018/05/11 Javascript
GOJS+VUE实现流程图效果
2018/12/01 Javascript
JavaScript数据结构与算法之检索算法实例分析【顺序查找、最大最小值、自组织查询】
2019/02/22 Javascript
vue防止花括号{{}}闪烁v-text和v-html、v-cloak用法示例
2019/03/13 Javascript
layui点击弹框页面 表单请求的方法
2019/09/21 Javascript
详解Python中的元组与逻辑运算符
2015/10/13 Python
Python编程中time模块的一些关键用法解析
2016/01/19 Python
Python 专题四 文件基础知识
2017/03/20 Python
Sublime开发python程序的示例代码
2018/01/24 Python
python3.x 将byte转成字符串的方法
2018/07/17 Python
python获取交互式ssh shell的方法
2019/02/14 Python
Python如何处理大数据?3个技巧效率提升攻略(推荐)
2019/04/15 Python
python的turtle库使用详解
2019/05/10 Python
Python+pyplot绘制带文本标注的柱状图方法
2019/07/08 Python
Python解决pip install时出现的Could not fetch URL问题
2019/08/01 Python
Python 实现try重新执行
2019/12/21 Python
x-ua-compatible content=”IE=7, IE=9″意思理解
2013/07/22 HTML / CSS
zooplus德国:便宜地订购动物用品、动物饲料、动物食品
2020/05/06 全球购物
信用卡工作证明模板
2014/09/14 职场文书
2014政府领导班子对照检查材料思想汇报(3篇)
2014/09/26 职场文书
2014年有孩子的离婚协议书范本
2014/10/08 职场文书
毕业生党员个人总结
2015/02/14 职场文书
面试通知单大全
2015/04/20 职场文书
房地产置业顾问工作总结
2015/10/23 职场文书
纯CSS3实现div按照顺序出入效果
2021/07/15 HTML / CSS