【问题标题】:Particle system design using Three.js and Shader使用 Three.js 和 Shader 设计粒子系统
【发布时间】:2020-02-17 01:14:53
【问题描述】:

我对这个社区很陌生。当我问问题时,如果有什么我认为不对,请纠正我。

现在,我正在使用 Three.js 库设计一个粒子系统,特别是我正在使用 THREE.Geometry() 并使用着色器控制顶点。我希望我的粒子运动限制在一个盒子内,这意味着当一个粒子穿过盒子的一个面时,它的新位置将位于该面的​​另一侧。

这是我在顶点着色器中的处理方式:

uniform float elapsedTime;

        void main() {
            gl_PointSize = 3.2;
            vec3 pos = position;

            pos.y -= elapsedTime*2.1;

            if( pos.y < -100.0) {
                pos.y = 100.0;
            }



            gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0 );
        }

ellapsedTime 是通过统一从 javascript 动画循环发送的。并且每个顶点的y位置会根据时间更新。作为测试,我希望如果一个粒子低于底部平面(y = -100),它将移动到顶部平面。那是我的计划。这是他们都到达底部后的结果:

开始下降

到达底部后

那么,我在这里缺少什么?

【问题讨论】:

    标签: three.js vertex-shader


    【解决方案1】:

    你可以实现它,使用mod函数:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
    camera.position.set(0, 0, 300);
    var renderer = new THREE.WebGLRenderer({
      antialis: true
    });
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var gridTop = new THREE.GridHelper(200, 10);
    gridTop.position.y = 100;
    var gridBottom = new THREE.GridHelper(200, 10);
    gridBottom.position.y = -100;
    scene.add(gridTop, gridBottom);
    
    var pts = [];
    for (let i = 0; i < 1000; i++) {
      pts.push(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(100));
    }
    var geom = new THREE.BufferGeometry().setFromPoints(pts);
    var mat = new THREE.PointsMaterial({
      size: 2,
      color: "aqua"
    });
    var uniforms = {
      time: {
        value: 0
      },
      highY: {
        value: 100
      },
      lowY: {
        value: -100
      }
    }
    mat.onBeforeCompile = shader => {
      shader.uniforms.time = uniforms.time;
      shader.uniforms.highY = uniforms.highY;
      shader.uniforms.lowY = uniforms.lowY;
      console.log(shader.vertexShader);
      shader.vertexShader = `
        uniform float time;
        uniform float highY;
        uniform float lowY;
      ` + shader.vertexShader;
      shader.vertexShader = shader.vertexShader.replace(
        `#include <begin_vertex>`,
        `#include <begin_vertex>
          
          float totalY = highY - lowY;
          transformed.y = highY - mod(highY - (transformed.y - time * 20.), totalY);
        `
      );
    }
    var points = new THREE.Points(geom, mat);
    scene.add(points);
    
    var clock = new THREE.Clock();
    
    renderer.setAnimationLoop(() => {
      uniforms.time.value = clock.getElapsedTime();
      renderer.render(scene, camera);
    });
    body {
      overflow: hidden;
      margin: 0;
    }
    <script src="https://threejs.org/build/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

    【讨论】:

      【解决方案2】:

      您不能在着色器中更改状态。顶点着色器的唯一输出是gl_Position(用于生成点/线/三角形)和传递给片段着色器的变量。片段着色器的唯一输出是gl_FragColor(通常)。所以试图改变pos.y 将无济于事。着色器退出的那一刻,您的更改就被遗忘了。

      对于您的粒子代码,尽管您可以使位置成为时间的重复函数

             const float duration = 5.0;
             float t = fract(elapsedTime / duration);
             pos.y = mix(-100.0, 100.0, t);
      

      假设 elapsedTime 以秒为单位,那么 pos.y 将在 5 秒内从 -100 变为 100 并重复。

      请注意,在这种情况下,所有粒子将同时下落。您可以添加一个属性来为它们各自赋予不同的时间偏移量,或者您可以将它们的位置纳入您自己的公式中。与此相关,您可能会发现 this article 很有用。

      您也可以在 JavaScript 中像 this examplethis one 那样进行粒子移动,更新 Geometry(或更好的 BufferGeometry)中的位置

      另一种解决方案是通过将位置存储在纹理中并将它们更新为新纹理来在单独的着色器中进行移动。然后使用该纹理作为输入另一组绘制粒子的着色器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-30
        • 2012-06-28
        • 2013-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多