【问题标题】:FBO Particles with Cumulative Movement具有累积运动的 FBO 粒子
【发布时间】:2019-09-14 00:14:07
【问题描述】:

线程threejs话语链接:https://discourse.threejs.org/t/fbo-particles-with-cumulative-movement/7221

由于我对该主题的了解有限,这对我来说很难解释,但我会尽力而为..

此时,我已经有了一个可以工作的基本 FBO 粒子系统。以下是它的设置方式:

var FBO = function( exports ){

    var scene, orthoCamera, rtt;
    exports.init = function( width, height, renderer, simulationMaterial, renderMaterial ){

        var gl = renderer.getContext();

        //1 we need FLOAT Textures to store positions
        //https://github.com/KhronosGroup/WebGL/blob/master/sdk/tests/conformance/extensions/oes-texture-float.html
        if (!gl.getExtension("OES_texture_float")){
            throw new Error( "float textures not supported" );
        }

        //2 we need to access textures from within the vertex shader
        //https://github.com/KhronosGroup/WebGL/blob/90ceaac0c4546b1aad634a6a5c4d2dfae9f4d124/conformance-suites/1.0.0/extra/webgl-info.html
        if( gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) == 0 ) {
            throw new Error( "vertex shader cannot read textures" );
        }

        //3 rtt setup
        scene = new THREE.Scene();
        orthoCamera = new THREE.OrthographicCamera(-1,1,1,-1,1/Math.pow( 2, 53 ),1 );

        //4 create a target texture
        var options = {
            minFilter: THREE.NearestFilter,//important as we want to sample square pixels
            magFilter: THREE.NearestFilter,//
            format: THREE.RGBAFormat,//180407 changed to RGBAFormat
            type:THREE.FloatType//important as we need precise coordinates (not ints)
        };
        rtt = new THREE.WebGLRenderTarget( width,height, options);


        //5 the simulation:
        //create a bi-unit quadrilateral and uses the simulation material to update the Float Texture
        var geom = new THREE.BufferGeometry();
        geom.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array([   -1,-1,0, 1,-1,0, 1,1,0, -1,-1, 0, 1, 1, 0, -1,1,0 ]), 3 ) );
        geom.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array([   0,1, 1,1, 1,0,     0,1, 1,0, 0,0 ]), 2 ) );
        scene.add( new THREE.Mesh( geom, simulationMaterial ) );


        //6 the particles:
        //create a vertex buffer of size width * height with normalized coordinates
        var l = (width * height );
        var vertices = new Float32Array( l * 3 );
        for ( var i = 0; i < l; i++ ) {

            var i3 = i * 3;
            vertices[ i3 ] = ( i % width ) / width ;
            vertices[ i3 + 1 ] = ( i / width ) / height;
        }

        //create the particles geometry
        var geometry = new THREE.BufferGeometry();
        geometry.addAttribute( 'position',  new THREE.BufferAttribute( vertices, 3 ) );

        //the rendermaterial is used to render the particles
        exports.particles = new THREE.Points( geometry, renderMaterial );
        exports.particles.frustumCulled = false;
        exports.renderer = renderer;

    };

    //7 update loop
    exports.update = function(){

        //1 update the simulation and render the result in a target texture
        // exports.renderer.render( scene, orthoCamera, rtt, true );
        exports.renderer.setRenderTarget( rtt );
        exports.renderer.render( scene, orthoCamera );
        exports.renderer.setRenderTarget( null );

        //2 use the result of the swap as the new position for the particles' renderer
        // had to add .texture on the end of rtt for r103
        exports.particles.material.uniforms.positions.value = rtt.texture;

    };
    return exports;
}({});

以下是它使用的着色器:

    <script type="x-shader/x-vertex" id="simulation_vs">
    //vertex shader
    varying vec2 vUv;
    void main() {
        vUv = vec2(uv.x, uv.y);

        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
    </script>

    <script type="x-shader/x-fragment" id="simulation_fs">
    //fragment Shader
    uniform sampler2D positions;//DATA Texture containing original positions
    varying vec2 vUv;
    void main() {

        //basic simulation: displays the particles in place.
        vec3 pos = texture2D( positions, vUv ).rgb;

        // we can move the particle here 


        gl_FragColor = vec4( pos,1.0 );
    }
    </script>

    <script type="x-shader/x-vertex" id="render_vs">
    //vertex shader
    uniform sampler2D positions;//RenderTarget containing the transformed positions
    uniform float pointSize;//size
    void main() {

        //the mesh is a nomrliazed square so the uvs = the xy positions of the vertices
        vec3 pos = texture2D( positions, position.xy ).xyz;
        //pos now contains a 3D position in space, we can use it as a regular vertex

        //regular projection of our position
        gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );

        //sets the point size
        gl_PointSize = pointSize;
    }
    </script>

    <script type="x-shader/x-fragment" id="render_fs">
    //fragment shader
    void main()
    {
        gl_FragColor = vec4( vec3( 1. ), .25 );
    }
    </script>

我知道我会在“simulation_fs”中移动粒子,但是如果我在那个着色器中移动一个粒子,如果我尝试做这样的事情,

pos.x += 1.0;

它仍然只会从原始纹理位置移动一个单位。我希望运动是累积的。

使用第二组模拟着色器可以让我以累积的方式移动粒子吗?这是一个实用的解决方案吗?

【问题讨论】:

    标签: three.js glsl particles fbo


    【解决方案1】:

    累积移动,需要使用uniforms

    考虑将名为timeuniform 传递给您的顶点着色器。然后你可以每帧更新一次时间,你可以用它来动画你的顶点位置。例如:

    position.x = 2.0 * time; // Increment linearly

    position.x = sin(time); // Sin wave back-forth animation

    没有变化的变量,你的顶点动画从一帧到下一帧都是静态的。

    【讨论】:

    • 我可能错了,但这是我对我的 FBO 系统的理解:模拟片段着色器正在输出一个纹理,该纹理被馈送到渲染顶点着色器中。渲染顶点着色器读取该纹理以确定放置粒子的位置。我认为这就是它的工作原理。通常,是的,顶点位置会在顶点着色器中改变。
    • 至于累积运动,是的,它会以增量的方式移动它,但是,我将如何停止粒子并将其留在停止的位置?
    • 哦,我的错。我错过了您在第二个渲染过程中使用颜色输出更改位置的部分。你是对的,在这种情况下,你可以在片段着色器中计算.rgb。至于粒子动画,我不能帮你一步一步写出你想要的动画。您必须使用您可以使用的工具。这是 GLSL 中所有可用函数的good cheat-sheet,您应该查看step() 函数,它可以用作if() 语句。
    • 感谢您的回复。他们在帮忙。还有一个问题,我如何将粒子属性传递给模拟片段着色器?看起来它必须是另一个纹理数据的一部分。
    【解决方案2】:

    我需要完成这样的事情,并设置一个绝对最小的示例,我可以在将来对其进行调整。您会看到位置变化是累积的。

    以下内容是从 Nicolas Barradeau(Webgl 向导)的 wonderful discussion of FBO's 简化而来的:

    // specify the container where we'll render the scene
    var elem = document.querySelector('body'),
        elemW = elem.clientWidth,
        elemH = elem.clientHeight
    
    // generate a scene object
    var scene = new THREE.Scene();
    
    // generate a camera
    var camera = new THREE.PerspectiveCamera(75, elemW/elemH, 0.001, 100);
    
    // generate a renderer
    var renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(elemW, elemH);
    elem.appendChild(renderer.domElement);
    
    // generate controls
    var controls = new THREE.TrackballControls(camera, renderer.domElement);
    
    // position camera and controls
    camera.position.set(0.5, 0.5, -5);
    controls.target = new THREE.Vector3(0.5, 0.5, 0);
    
    /**
    * FBO
    **/
    
    // verify browser agent supports "frame buffer object" features
    gl = renderer.getContext();
    if (!gl.getExtension('OES_texture_float') ||
         gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) == 0) {
      alert(' * Cannot create FBO :(');
    }
    
    // set initial positions of `w*h` particles
    var w = h = 256,
        i = 0,
        data = new Float32Array(w*h*3);
    for (var x=0; x<w; x++) {
      for (var y=0; y<h; y++) {
        data[i++] = x/w;
        data[i++] = y/h;
        data[i++] = 0;
      }
    }
    
    // feed those positions into a data texture
    var dataTex = new THREE.DataTexture(data, w, h, THREE.RGBFormat, THREE.FloatType);
    dataTex.minFilter = THREE.NearestFilter;
    dataTex.magFilter = THREE.NearestFilter;
    dataTex.needsUpdate = true;
    
    // add the data texture with positions to a material for the simulation
    var simMaterial = new THREE.RawShaderMaterial({
      uniforms: { posTex: { type: 't', value: dataTex }, },
      vertexShader: document.querySelector('#sim-vs').textContent,
      fragmentShader: document.querySelector('#sim-fs').textContent,
    });
    
    // delete dataTex; it isn't used after initializing point positions
    delete dataTex;
    
    THREE.FBO = function(w, simMat) {
      this.scene = new THREE.Scene();
      this.camera = new THREE.OrthographicCamera(-w/2, w/2, w/2, -w/2, -1, 1);
      this.scene.add(new THREE.Mesh(new THREE.PlaneGeometry(w, w), simMat));
    };
    
    // create a scene where we'll render the positional attributes
    var fbo = new THREE.FBO(w, simMaterial);
    
    // create render targets a + b to which the simulation will be rendered
    var renderTargetA = new THREE.WebGLRenderTarget(w, h, {
      wrapS: THREE.RepeatWrapping,
      wrapT: THREE.RepeatWrapping,
      minFilter: THREE.NearestFilter,
      magFilter: THREE.NearestFilter,
      format: THREE.RGBFormat,
      type: THREE.FloatType,
      stencilBuffer: false,
    });
    
    // a second render target lets us store input + output positional states
    renderTargetB = renderTargetA.clone();
    
    // render the positions to the render targets
    renderer.render(fbo.scene, fbo.camera, renderTargetA, false);
    renderer.render(fbo.scene, fbo.camera, renderTargetB, false);
    
    // store the uv attrs; each is x,y and identifies a given point's
    // position data within the positional texture; must be scaled 0:1!
    var geo = new THREE.BufferGeometry(),
        arr = new Float32Array(w*h*3);
    for (var i=0; i<arr.length; i++) {
      arr[i++] = (i%w)/w;
      arr[i++] = Math.floor(i/w)/h;
      arr[i++] = 0;
    }
    geo.addAttribute('position', new THREE.BufferAttribute(arr, 3, true))
    
    // create material the user sees
    var material = new THREE.RawShaderMaterial({
      uniforms: {
        posMap: { type: 't', value: null }, // `posMap` is set each render
      },
      vertexShader: document.querySelector('#ui-vert').textContent,
      fragmentShader: document.querySelector('#ui-frag').textContent,
      transparent: true,
    });
    
    // add the points the user sees to the scene
    var mesh = new THREE.Points(geo, material);
    scene.add(mesh);
    
    function render() {
      // at the start of the render block, A is one frame behind B
      var oldA = renderTargetA; // store A, the penultimate state
      renderTargetA = renderTargetB; // advance A to the updated state
      renderTargetB = oldA; // set B to the penultimate state
    
      // pass the updated positional values to the simulation
      simMaterial.uniforms.posTex.value = renderTargetA.texture;
    
      // run a frame and store the new positional values in renderTargetB
      renderer.render(fbo.scene, fbo.camera, renderTargetB, false);
    
      // pass the new positional values to the scene users see
      material.uniforms.posMap.value = renderTargetB.texture;
    
      // render the scene users see as normal
      renderer.render(scene, camera);
      controls.update();
      requestAnimationFrame(render);
    };
    
    render();
      html, body { width: 100%; height: 100%; background: #000; }
      body { margin: 0; overflow: hidden; }
      canvas { width: 100%; height: 100%; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
    
    <!-- The simulation shaders update positional attributes -->
    <script id='sim-vs' type='x-shader/x-vert'>
      precision mediump float;
    
      uniform mat4 projectionMatrix;
      uniform mat4 modelViewMatrix;
    
      attribute vec2 uv; // x,y offsets of each point in texture
      attribute vec3 position;
    
      varying vec2 vUv;
    
      void main() {
        vUv = vec2(uv.x, 1.0 - uv.y);
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    </script>
    
    <script id='sim-fs' type='x-shader/x-frag'>
      precision mediump float;
    
      uniform sampler2D posTex;
    
      varying vec2 vUv;
    
      void main() {
    
        // read the supplied x,y,z vert positions
        vec3 pos = texture2D(posTex, vUv).xyz;
    
        // update the positional attributes here!
        pos.x += cos(pos.y) / 100.0;
        pos.y += tan(pos.x) / 100.0;
    
        // render the new positional attributes
        gl_FragColor = vec4(pos, 1.0);
      }
    </script>
    
    <!-- The ui shaders render what the user sees -->
    <script id='ui-vert' type='x-shader/x-vert'>
      precision mediump float;
    
      uniform sampler2D posMap; // contains positional data read from sim-fs
      uniform mat4 projectionMatrix;
      uniform mat4 modelViewMatrix;
    
      attribute vec2 position;
    
      void main() {
    
        // read this particle's position, which is stored as a pixel color
        vec3 pos = texture2D(posMap, position.xy).xyz;
    
        // project this particle
        vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
        gl_Position = projectionMatrix * mvPosition;
    
        // set the size of each particle
        gl_PointSize = 0.3 / -mvPosition.z;
      }
    
    </script>
    
    <script id='ui-frag' type='x-shader/x-frag'>
      precision mediump float;
    
      void main() {
        gl_FragColor = vec4(0.0, 0.5, 1.5, 1.0);
      }
    </script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-10
      • 2019-09-29
      • 2021-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多