最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • three.js 实现3D动态文字

    正文概述 掘金(alphardex)   2021-02-21   656

    前言

    大家好,这里是 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.);
    }
    

    此时文本显示到了屏幕上

    three.js 实现3D动态文字

    创建渲染目标

    为了将字体对象本身作为贴图,创建了一个渲染目标

    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动态文字

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元