How to achieve 3D dynamic text effect with three.js

How to achieve 3D dynamic text effect with three.js

Preface

Hello everyone, this is the CSS wizard - alphardex.

When I was browsing foreign websites before, I found that some websites’ texts were engraved on 3D graphics and could move on the graphics. The visual effect was quite good, so I also wanted to use three.js to try to reproduce this effect.

The above picture is just one of all the effects. Let’s get started.

Preparation

The three.js template I packaged myself: Three.js Starter

Readers can click on the lower right corner to fork a copy before starting this project

This project requires bitmap fonts. You can directly copy the font code in the HTML of the demo.

One note: the three-bmfont-text library depends on the global three.js, so you need to import three.js once more in JS, as shown below

Implementation ideas

  1. Load the bitmap font file and convert it into the shape and material required by the text object
  2. Creating a Text Object
  3. Create a render target, which can be understood as a canvas in a canvas, because next we will use the text object itself as a texture
  4. Create a container to hold the font and paste the text object as a texture
  5. Animation

positive

Set up the scaffolding

<div class="relative w-screen h-screen">
 <div class="kinetic-text w-full h-full bg-blue-1"></div>
 <div class="font">
 <font>
  A bunch of font codes from the 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/[email protected]";
import MSDFShader from "https://cdn.skypack.dev/[email protected]/shaders/msdf";
import parseBmfontXml from "https://cdn.skypack.dev/[email protected]";

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

const kineticTextTorusKnotVertexShader = `(vertex shader code, empty for now, see below for details)`;

const kineticTextTorusKnotFragmentShader = `(fragment shader code, empty for now, see below for details)`;

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
 };
 }
 // Initialize async init() {
 this.createScene();
 this.createPerspectiveCamera();
 this.createRenderer(true);
 await this.createKineticText(this.params.text);
 this.createLight();
 this.createOrbitControls();
 this.addListeners();
 this.setLoop();
 }
 // Create dynamic text async createKineticText(text: string) {
 await this.createFontText(text);
 this.createRenderTarget();
 this.createTextContainer();
 }
}

Loading and creating fonts

First load the font file and create the shape and material. With these two, you can create a font object.

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;
 }
}

Shaders

Vertex Shader

Universal template, just use 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;
}

Fragment Shader

Use the fract function to create a repeated texture, add displacement to make the texture move over time, and then use the clamp function to limit the range of the shadow according to the z-axis size, which means that the farther away from the screen, the heavier the shadow, and vice versa, the closer to the screen, the lighter the shadow

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.);
}

The text is now displayed on the screen

Creating a Render Target

To use the font object itself as a texture, a render target is created

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;
 }
}

Creating a font container

Create a container and attach the font object itself as a texture, then apply the animation to complete it.

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;
 }
 }
}

Don't forget to move the camera farther away

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

The flirty dynamic text appears :)

Project gallery

Kinetic Text

There are more shapes in the demo than the one created in this article. Feel free to play with them.

Summarize

This is the end of this article about how to achieve 3D dynamic text effects with three.js. For more related three.js 3D dynamic text content, please search 123WORDPRESS.COM’s previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Three.js realizes Facebook Metaverse 3D dynamic logo effect
  • Detailed process of drawing three-dimensional arrow lines using three.js
  • Use three.js to achieve cool acid style 3D page effects
  • Three.js sample code for implementing dewdrop animation effect
  • Detailed explanation of the use and performance testing of multithreading in three.js
  • First experience of creating text with javascript Three.js

<<:  MySQL 5.6.33 installation and configuration tutorial under Linux

>>:  Implementation of ssh non-secret communication in linux

Recommend

Vue uses canvas to realize image compression upload

This article shares the specific code of Vue usin...

MySQL 5.7.23 decompression version installation tutorial with pictures and text

It is too troublesome to find the installation tu...

Understanding of the synchronous or asynchronous problem of setState in React

Table of contents 1. Is setState synchronous? asy...

How to use mqtt in uniapp project

Table of contents 1. Reference plugins in the uni...

Steps to configure nginx ssl to implement https access (suitable for novices)

Preface After deploying the server, I visited my ...

Detailed explanation of global parameter persistence in MySQL 8 new features

Table of contents Preface Global parameter persis...

Monitor changes in MySQL table content and enable MySQL binlog

Preface binlog is a binary log file, which record...

Zabbix monitors mysql instance method

1. Monitoring planning Before creating a monitori...

Detailed process of configuring Https certificate under Nginx

1. The difference between Http and Https HTTP: It...

The most common declaration merge in TS (interface merge)

Table of contents 1. Merge interface 1.1 Non-func...

503 service unavailable error solution explanation

1. When you open the web page, 503 service unavai...

Basic knowledge of website design: newbies please read this

Now many people are joining the ranks of website ...

Baidu Input Method opens API, claims it can be ported and used at will

The relevant person in charge of Baidu Input Metho...