如何实现这种运动模糊着色器效果?

时间:2018-12-04 09:49:51

标签: javascript three.js glsl webgl shader

我试图创建这种链接http://blog.edankwan.com/post/my-first-christmas-experiment底部页面上的雪花效果。其他所有工作都很好,但是无法使运动模糊效果正常工作。有任何想法吗?

用于实现运动模糊效果的纹理精灵

enter image description here

代码如下:

(function(global) {

  var img = 'https://i.imgur.com/hlmsgWA.png'

  var renderer, scene, camera
  var w = 800, h = 320
  var uniforms
  
  var geometry
  var texture, material
  var gui
  var conf = {
    amount: 200,
    speed: 0.5,
    time: 0
  }

  var obj = {
    init: function() {
      renderer = new THREE.WebGLRenderer({
        antialias: true
      })
      renderer.setPixelRatio(window.devicePixelRatio)
      renderer.setSize(w, h)
      camera = new THREE.Camera
      scene = new THREE.Scene()

      geometry = new THREE.BufferGeometry()

      var positions = []
      
      for(var i = 0, l = conf.amount; i < l; i++) {
        positions[i * 3] = Math.random() * 800 - 400
        positions[i * 3 + 1] = i
        positions[i * 3 + 2] = Math.random() * 800
      }
      geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3))

      var vs = document.getElementById('vertexShader').textContent
      var fs = document.getElementById('fragmentShader').textContent

      uniforms = {
        u_amount: {
          type: 'f',
          value: conf.amount
        },
        u_speed: {
          type: 'f',
          value: conf.speed
        },
        u_time: {
          type: 'f',
          value: conf.time
        },
        u_resolution: {
          type: 'vec2',
          value: new THREE.Vector2(w, h)
        },
        u_texture : {
          value: new THREE.TextureLoader().load(img)
        }
      }
      material = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vs,
        fragmentShader: fs,
        transparent: true
      })

      var points = new THREE.Points(geometry, material)
      scene.add(points)

      document.body.appendChild(renderer.domElement)
      this.render()
      this.createGui()
  
    },

    createGui: function() {
      gui = new dat.GUI()
      gui.add(conf, 'speed', -1, 1)
    },

    render: function() {
      requestAnimationFrame(this.render.bind(this))
      uniforms.u_time.value += conf.speed * 0.003
      renderer.render(scene, camera)
    }

  }

  obj.init()

})(window)
<script id="vertexShader" type="x-shader/x-vertex">
    precision highp float;
    vec3 getPosOffset(float ratio, float thershold) {
      return vec3(
        cos((ratio * 80.0 + 10.0) * thershold) * 20.0 * thershold,
        (sin((ratio * 90.0 + 30.0) * thershold) + 1.0) * 5.0 * thershold + mix(500.0, -500.0, ratio / thershold),
        sin((ratio * 70.0 + 20.0) * thershold) * 20.0 * thershold
      );
    }

    uniform vec2 u_resolution;
    uniform float u_amount;
    uniform float u_speed;
    uniform float u_time;
    varying float v_alpha;
    varying float v_rotation;
    varying float v_index;
    void main() {
      float indexRatio = position.y / u_amount;
      float thershold = 0.7 + indexRatio * 0.3;
      float ratio = mod(u_time - indexRatio * 3.0, thershold);
      float prevRatio = mod(u_time - u_speed - indexRatio * 3.0, thershold);
      vec3 offsetPos = getPosOffset(ratio, thershold);
      vec3 prevOffsetPos = getPosOffset(prevRatio, thershold);
      vec3 pos = position;
      pos += offsetPos;

      float perspective = (2000.0 - pos.z) / 2000.0;
      pos.x *= perspective;
      pos.y *= perspective;

      float delta = length(offsetPos.xy - prevOffsetPos.xy);
      float maxDelta = 2.7;
      v_index = floor(pow(clamp(delta, 0.0, maxDelta) / maxDelta, 5.0) * 15.99);  
      v_rotation = atan((offsetPos.x - prevOffsetPos.x) / (offsetPos.y - prevOffsetPos.y));

      pos.x *= 2.0 / u_resolution.x;
      pos.y *= 2.0 / u_resolution.y;
      pos.z = 0.0;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
      gl_PointSize = 24.0 * perspective;
      v_alpha = perspective;
    }
  </script>
  <script id="fragmentShader" type="x-shader/x-fragment">
  
    uniform sampler2D u_texture;
    varying float v_rotation;
    varying float v_index;
    varying float v_alpha;
    void main() {
      vec2 coord =  gl_PointCoord.xy;
      coord = vec2(
        clamp(cos(v_rotation) * (coord.x - 0.5) + sin(v_rotation) * (coord.y - 0.5) + 0.5, 0.0, 1.0),
        clamp(cos(v_rotation) * (coord.y - 0.5) - sin(v_rotation) * (coord.x - 0.5) + 0.5, 0.0, 1.0)
      );
      float index = floor(v_index + 0.5);
      coord.y = - coord.y + 4.0;
      coord.x += (index - floor(index / 4.0) * 4.0);
      coord.y -= floor(index / 4.0);
      coord *= 0.25;
      vec4 color = texture2D(u_texture, coord);
      color.a *= v_alpha;
      gl_FragColor = color;
    }
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.3/dat.gui.js"></script>

http://blog.edankwan.com/post/my-first-christmas-experiment

1 个答案:

答案 0 :(得分:3)

在摘要代码中,您在更新循环和顶点着色器中对变量speed的处理不同。

  1. 更新:uniforms.u_time.value += conf.speed * 0.003
  2. 着色器用法:float prevRatio = mod(u_time - u_speed - indexRatio * 3.0, thershold);

如果您在着色器代码中将u_speed更改为u_speed * 0.003(或者最好将该乘法移动到javascript中的定义),您将获得理想的结果。

调试此类事情的最快方法-将值传递给输出颜色,并检查其是否符合您的期望。

=================

如果某人想要制作 非平坦曲率的尾巴 ,则可以存储每个粒子路径的最后N个点。然后,您可以在CPU上完全计算尾迹网格几何并将其流式传输到GPU。

另一种方式:您可以将所有路径上传到单个“统一缓冲区对象”,并找到要用pointIduv的尾网格顶点提取的正确点。