How to animate the drawing of a mesh in three.js

后端 未结 1 406
忘了有多久
忘了有多久 2021-01-03 15:39

I\'ve got a ribbon-like mesh made out of a buffer geometry, the faces of which are invisible. I\'m using a custom shader for this -- gl_FragColor = vec4(1.0,1.0,1.0,0.

相关标签:
1条回答
  • 2021-01-03 16:26

    You can render only part of your BufferGeometry by setting drawRange like so:

    mesh.geometry.setDrawRange( startIndex, count );
    

    For more information and a live example see this answer.


    EDIT - As mentioned in the comments below, another approach is to add an attribute to your geometry which represents the fractional distance each vertex is positioned along the geometry. This approach will require a custom ShaderMaterial, but you will be able to smoothly animate the drawing of your mesh.

    Here is an example vertex shader and fragment shader

    <script id="vertex_shader" type="x-shader/x-vertex">
    
    attribute float distance;
    
    varying float vDistance;
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    
    void main() {
    
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    
        vDistance = distance;
        vNormal = normalize( normalMatrix * normal );
        vViewPosition = - mvPosition.xyz;
    
        gl_Position = projectionMatrix * mvPosition;
    
    }
    
    </script>
    
    <script id="fragment_shader" type="x-shader/x-fragment">
    
    uniform float fraction;
    
    varying float vDistance;
    varying vec3 vNormal;
    varying vec3 vViewPosition;
    
    void main() {
    
        if ( vDistance > fraction ) discard;
    
        vec3 color = vec3( 0.25, 0.5, 1.0 );
    
        // hack in a fake pointlight at camera location, plus ambient
        vec3 normal = normalize( vNormal );
        vec3 lightDir = normalize( vViewPosition );
    
        float dotProduct = max( dot( normal, lightDir ), 0.0 ) + 0.2;
    
        // trick to make the clipped ends appear solid
        gl_FragColor = ( gl_FrontFacing ) ? vec4( color * dotProduct, 1.0 ) : vec4( color, 1.0 );
    
    }
    
    </script>
    

    Here is how to add an attribute to your BufferGeometry and instantiate the material.

    // new attribute
    var numVertices = geometry.attributes.position.count;
    var distance = new Float32Array( numVertices * 1 ); // 1 value per vertex
    geometry.addAttribute( 'distance', new THREE.BufferAttribute( distance, 1 ) );
    
    // populate attribute
    for ( var i = 0, l = numVertices; i < l; i ++ ) {
    
        // set new attribute
        distance[ i ] = ( geometry.attributes.position.getY( i ) + 10 ) / 20;
    
        // wiggle geometry a bit while we're at it
        var x = geometry.attributes.position.getX( i )
        var y = geometry.attributes.position.getY( i );
        geometry.attributes.position.setX( i, x + 2 * Math.sin( y ) );
    
    }
    
    // uniforms
    var uniforms = {
        "fraction" : { value: 0 }
    };
    
    // material
    var material = new THREE.ShaderMaterial( {
        uniforms        : uniforms,
        vertexShader    : document.getElementById( 'vertex_shader' ).textContent,
        fragmentShader  : document.getElementById( 'fragment_shader' ).textContent,
        side: THREE.DoubleSide
    } );
    
    // mesh
    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
    

    Then, in your render loop, set the desired fraction to render:

    mesh.material.uniforms.fraction.value = 0.5 * ( 1 + Math.cos( t ) );
    

    fiddle: http://jsfiddle.net/m99aj10b/

    three.js r.76

    0 讨论(0)
提交回复
热议问题