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

Let's deeply understand the event object in js

We know that the commonly used events in JS are: ...

MySQL query tree structure method

Table of contents MySQL query tree structure 1. A...

CSS uses the autoflow attribute to achieve seat selection effect

1. Autoflow attribute, if the length and width of...

HTML code text box limit input text box becomes gray limit text box input

Method 1: Set the readonly attribute to true. INPU...

In-depth analysis of Nginx virtual host

Table of contents 1. Virtual Host 1.1 Virtual Hos...

Learn MySQL in a simple way

Preface The database has always been my weak poin...

Detailed explanation of Grid layout and Flex layout of display in CSS3

Gird layout has some similarities with Flex layou...

A brief discussion on the issue of element dragging and sorting in table

Recently, when using element table, I often encou...

30 minutes to give you a comprehensive understanding of React Hooks

Table of contents Overview 1. useState 1.1 Three ...

In-depth understanding of MySQL master-slave replication thread state transition

Preface The basic principle of MySQL master-slave...

Analysis of pitfalls in rounding operation of ROUND function in MySQL

This article uses examples to illustrate the pitf...

A brief understanding of MySQL storage field type query efficiency

The search performance from fastest to slowest is...