zoukankan      html  css  js  c++  java
  • threejs 贴图动画总结

    引言

    在三维可视化中,会涉及到很多动画,其中贴图动画是其中很重要的一种,本文介绍几种贴图动画的思路,供大家一起探讨。

    流动动画

    流动动画通过设置贴图的repeat属性,并不断改变贴图对象的offset让贴图产生流动效果。 这种动画不难实现,首先加载贴图,如下所示:

    let img = new Image();
    img.src = './images/path.png';
    let texture = new eg.Texture(img);
    img.onload = function () {
        texture.needsUpdate = true;
    }
    texture.repeat.set(100,1);
    tube.material.map= texture;
    
    function render(){
     tube.material.map.wrapS = eg.RepeatWrapping;
     tube.material.map.offset.set(offset,0);
     tube.material.map.needsUpdate = true;
     offset += 0.01;
    }
    
    

    上面代码,实现了一个tube(管道),然后给管道加了一个贴图texture。 在渲染的时候,不断更新texture对象的offset的值,此时就可以生产流动的动画。如下图所示:

    flow.gif

    雪碧图动画(Sprite Sheet)

    图集也就是常说的雪碧图,就是把一系列小图按照一定的布局放到一张大图上面。 在使用的时候,截取大图的一部分来获取某个小图。 这在web端是一种常用的手段,通常用于减少图片数量,从而降低网络请求数量。

    通过雪碧图的方式,可以把动画的系列动作的每一帧都布局在雪碧图上。 然后通过雪碧图创建texture对象,设置贴图的repeat和offset,让每次绘制获取雪碧图上的某一帧图像,不断改变offset,就可以形成绘制不同帧的动画效果。比如下面的图片:

    image.png

    image.png

    下面这个threejs的demo,就是这样的效果,所以此处不再赘述代码,有兴趣的读者可以查看demo的源代码。

    https://stemkoski.github.io/Three.js/Texture-Animation.html 效果如下图所示:

    flow2.gif

    GIF动画

    gif图片本身自带动画,如果gif放到Image对象上,动画会自动播放,只是当把gif作为贴图对象的图片的时候。 不会自动播放动画。
    要自动播放gif动画,需要使用解析gif的库,把gif图片的每一帧解析出来, 并把每一帧图像绘制到一个canvas上,把canvas作为贴图对象的图片。大致代码如下:

    加载gif图片,并解析图片。其中解析图片用到了一个库omggif,利用里面的GifReader可以解析gif图片的帧数据:

    import { GifReader } from 'omggif';
      const loader = new FileLoader(this.manager);
        loader.setPath(this.path);
        loader.setResponseType('arraybuffer');
    
        loader.load(url, (response) => {
          const gifData = new Uint8Array(response);
          const reader = new GifReader(gifData);
          if (onLoad) onLoad(reader);
        }, onProgress, onError);
    

    然后不断的更新贴图的图像:

     draw() {
          if (!this.reader) {
            return;
          }
          
          const { reader, image, context } = this;
          const { width, height } = image;
      
          const frameNum = ++this.frameNumber % reader.numFrames();
          const frameInfo = reader.frameInfo(frameNum);
      
          if (frameNum === 0) {
            // always clear canvas to start
            context.clearRect(0, 0, width, height);
          } else if (this.previousFrameInfo && this.previousFrameInfo.disposal === 2) {
            // disposal was "restore to background" which is essentially "restore to transparent"
            context.clearRect(this.previousFrameInfo.x,
                              this.previousFrameInfo.y,
                              this.previousFrameInfo.width,
                              this.previousFrameInfo.height);
          }
      
          const imageData = context.getImageData(0, 0, width, height);
          reader.decodeAndBlitFrameRGBA(frameNum, imageData.data);
          context.putImageData(imageData, 0, 0);
      
          this.needsUpdate = true;
      
          this.previousFrameInfo = frameInfo;
          this.timeoutId = setTimeout(this.draw.bind(this), (frameInfo.delay || 2) * 10);
        }
    

    最终的gif贴图效果如下图所示

    flow3.gif

    APNG动画

    APNG图片和gif图片是类似的,也是动画图片。 不过相对于gif来说。APNG可以设置半透明,边缘锯齿不严重,所以使用APNG的图片的效果要优于gif图片。

    原理上类似,也是解析APNG图片,然后把没一帧一次绘制到canvas上,并不断更新texture对象。 解析APNG图片,使用了一个开源库,APNG-canvas。 有兴趣读者可以自行研究,此处不重点讲述。

    解析完成后,可以把解析的帧集合进行绘制,代码如下:

    draw() {
          if (!this.reader) {
            return;
          }
          
          const { reader, image, context } = this;
          const { width, height } = image;
      
          const frameNum = ++this.frameNumber % reader.numFrames;
          const frameInfo = reader.frames[frameNum];
      
          if (frameNum === 0) {
            // always clear canvas to start
            context.clearRect(0, 0, width, height);
          // } else if (this.previousFrameInfo && this.previousFrameInfo.disposal === 2) {
          } else if (this.previousFrameInfo) {
            // disposal was "restore to background" which is essentially "restore to transparent"
            context.clearRect(this.previousFrameInfo.left,
                              this.previousFrameInfo.top,
                              this.previousFrameInfo.width,
                              this.previousFrameInfo.height);
          }
      
          const imageData = context.getImageData(0, 0, width, height);
          // reader.decodeAndBlitFrameRGBA(frameNum, imageData.data);
          // context.putImageData(imageData, 0, 0);
          context.drawImage(frameInfo.img,frameInfo.left,frameInfo.top,frameInfo.width,frameInfo.height);
      
          this.needsUpdate = true;
      
          this.previousFrameInfo = frameInfo;
          this.timeoutId = setTimeout(this.draw.bind(this), frameInfo.delay);
    

    最终的apng贴图效果如下图所示

    1.gif

    总结

    本文介绍了 theejs 贴图动画的多种实现思路。 包括 纹理流动,雪碧图,gif和apng动画。 通过这些动画能力,可以创建出丰富多彩的可视化效果。

    如果对可视化感兴趣,可以和我交流,微信541002349(可入微信群)。

    关注公号“ITMan彪叔” 可以及时收到更多有价值的文章。

  • 相关阅读:
    模板方法设计模式
    单一职责原则
    开闭原则
    uml
    迭代器模式
    观察者模式
    工厂模式
    代理模式
    idea本地Maven仓库不能下载依赖jar包的解决方案
    selenium 使用教程详解-java版本
  • 原文地址:https://www.cnblogs.com/flyfox1982/p/15701795.html
Copyright © 2011-2022 走看看