three.jsはどうやって3 D動的文字効果を実現しますか?


前言
こんにちは、ここはCSS魔法使いのalphardexです。
以前、海外のサイトを見ていたら、3 Dの図形に文字が刻まれていて、図形の上で動き、視覚効果がいいというサイトがありました。だから、私もthree.jsを使ってこの効果を再現してみたいです。

上の図はすべての効果の一つです。これから一緒にやりましょう。
準備工作
筆者が自分でカプセル化したthree.jsテンプレート:Three.js Starter
読者は右下の角forkをクリックしてから本プロジェクトを開始できます。
本プロジェクトは所定の位置にあるフォントを使う必要があります。デモのHTMLのフォントコードを直接コピーすることができます。
一つの注意点:three-bmfont-textこのライブラリは大域的なthree.jsに依存していますので、JSにもう一回three.jsを追加導入したいです。

構想を実現する
  • ビットマップフォントファイルを読み込み、テキストオブジェクトに変換するために必要な形状と材質
  • 文字オブジェクト
  • を作成します。
  • レンダリングターゲットを作成すると、canvasのcanvasと理解できます。これは文字オブジェクト自体をスタンプとして考えるからです。
  • は、キャリアフォントのコンテナを作成し、テキストオブジェクトをスタンプとして
  • に貼り付ける。
  • アニメーション
  • 正面の映画
    骨組みをととのえる
    
    <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/[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 = `(       ,   ,     )`;
    
    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でOKです。
    
    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);
    
    乱れた動的文字が現れました。)

    プロジェクトのアドレス
    Kineetic Text
    デモではこのような形を作成しただけではなく、皆さんも自由に遊んでください。
    締め括りをつける
    この記事では、three.jsがどのように3 Dダイナミックな文字効果を実現するかについて紹介します。three.jsの3 Dダイナミックな文字の内容については、以前の文章を検索したり、次の関連記事を見たりしてください。これからもよろしくお願いします。