javascriptanimationthree.jssurfacenurbs

How to animate in oscillation a N surface in Three.js


I want to create a NURBS surface that is animated in oscillation with Three.js.

This is my initialization of the NURBS:

this.nsControlPoints = [
      [
        new THREE.Vector4 ( -20, -20, 10, 1 ),
        new THREE.Vector4 ( -20, -10, -20, 1 ),
        new THREE.Vector4 ( -20, 10, 25, 1 ),
        new THREE.Vector4 ( -20, 20, -10, 1 )
      ],
      [
        new THREE.Vector4 ( 0, -20, 0, 1 ),
        new THREE.Vector4 ( 0, -10, -10, 5 ),
        new THREE.Vector4 ( 0, 10, 15, 5 ),
        new THREE.Vector4 ( 0, 20, 0, 1 )
      ],
      [
        new THREE.Vector4 ( 20, -20, -10, 1 ),
        new THREE.Vector4 ( 20, -10, 20, 1 ),
        new THREE.Vector4 ( 20, 10, -25, 1 ),
        new THREE.Vector4 ( 20, 20, 10, 1 )
      ]
    ];
    var degree1 = 2;
    var degree2 = 3;
    var knots1 = [0, 0, 0, 1, 1, 1];
    var knots2 = [0, 0, 0, 0, 1, 1, 1, 1];
    this.nurbsSurface = new NURBSSurface(degree1, degree2, knots1, knots2, this.nsControlPoints);

    var map = new THREE.TextureLoader().load( 'assets/img/tile.jpg' );
    map.wrapS = map.wrapT = THREE.RepeatWrapping;
    map.anisotropy = 16;

    let nurbsSurface = this.nurbsSurface;
    let start = this.start;

    function getSurfacePoint(u, v, target) {

      return nurbsSurface.getPoint(u, v, target);

    }

    this.geometry = new THREE.ParametricBufferGeometry( getSurfacePoint, 20, 20 );
    var material = new THREE.MeshBasicMaterial( { wireframe: true, color: 0x666666, opacity: 1.0 } );
    this.objectSurface = new THREE.Mesh( this.geometry, material );
    this.objectSurface.position.set( - 200, -100, 0 );
    this.objectSurface.rotateX(90);
    this.objectSurface.scale.multiplyScalar( 50 );
    this.scene.add( this.objectSurface );

And this is what I tried to do the animation:

...
var position = this.geometry.attributes.position;
position.setXYZ(1, position.x + ( Date.now() - this.start ), position.y + ( Date.now() - this.start ), position.z + ( Date.now() - this.start ));

if(position instanceof THREE.BufferAttribute) {
    position.needsUpdate = true;
} else {
    position.data.needsUpdate = true;
}
...
this.renderer.render(this.scene, this.camera);

Any suggestions to do that? I didn't find any solution on the internet with this theme.

Thanks in advance!!!


Solution

  • The following live snippet shows you can transform the vertices of a NURBS surface represented by an instance of ParametricBufferGeometry. Notice how BufferAttribute.setUsage() is used in order to tell WebGL that you are going to change the position attribute dynamically per frame.

    The animation is very basic but the code should illustrate the intended approach.

    var geometry, renderer, scene, camera, controls, clock;
    
    var vertex = new THREE.Vector3();
    
    init();
    animate();
    
    function init() {
    
        // renderer
        renderer = new THREE.WebGLRenderer();
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.setPixelRatio( window.devicePixelRatio );
        document.body.appendChild( renderer.domElement );
    
        // scene
        scene = new THREE.Scene();
        
        // camera
        camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
        camera.position.set( 50, 50, 50 );
    
        // controls
        controls = new THREE.OrbitControls( camera, renderer.domElement );
    		
        clock = new THREE.Clock();
        
        // axes
        scene.add( new THREE.AxesHelper( 20 ) );
    
        var nsControlPoints = [
          [
            new THREE.Vector4 ( -20, -20, 10, 1 ),
            new THREE.Vector4 ( -20, -10, -20, 1 ),
            new THREE.Vector4 ( -20, 10, 25, 1 ),
            new THREE.Vector4 ( -20, 20, -10, 1 )
          ],
          [
            new THREE.Vector4 ( 0, -20, 0, 1 ),
            new THREE.Vector4 ( 0, -10, -10, 5 ),
            new THREE.Vector4 ( 0, 10, 15, 5 ),
            new THREE.Vector4 ( 0, 20, 0, 1 )
          ],
          [
            new THREE.Vector4 ( 20, -20, -10, 1 ),
            new THREE.Vector4 ( 20, -10, 20, 1 ),
            new THREE.Vector4 ( 20, 10, -25, 1 ),
            new THREE.Vector4 ( 20, 20, 10, 1 )
          ]
        ];
        var degree1 = 2;
        var degree2 = 3;
        var knots1 = [0, 0, 0, 1, 1, 1];
        var knots2 = [0, 0, 0, 0, 1, 1, 1, 1];
        var nurbsSurface = new THREE.NURBSSurface(degree1, degree2, knots1, knots2, nsControlPoints);
    
        function getSurfacePoint(u, v, target) {
    
                return nurbsSurface.getPoint(u, v, target);
    
        }
    
        geometry = new THREE.ParametricBufferGeometry( getSurfacePoint, 20, 20 );
        var material = new THREE.MeshBasicMaterial( { wireframe: true, color: 0x666666 } );
        var objectSurface = new THREE.Mesh( geometry, material );
        scene.add( objectSurface );
    		
        geometry.attributes.position.setUsage( THREE.DynamicDrawUsage );
    
    }
    
    function animate() {
    
        requestAnimationFrame( animate );
    		
        var elapsedTime = clock.getElapsedTime();
    	
        var position = geometry.attributes.position;
        
        for ( var i = 0; i < position.count; i ++ ) {
    		
    	        var z = position.getZ( i );
    
    	        z += Math.sin( elapsedTime ) * 0.1;
    			
    	        position.setZ( i, z );
    		
        }
    		
        position.needsUpdate = true;
    
        renderer.render( scene, camera );
    
    }
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r113/build/three.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r113/examples/js/controls/OrbitControls.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r113/examples/js/curves/NURBSUtils.js"></script>
    <script src="https://rawcdn.githack.com/mrdoob/three.js/r113/examples/js/curves/NURBSSurface.js"></script>