Three.js Object3d cylinder rotation to align to a vector

后端 未结 3 1411
梦毁少年i
梦毁少年i 2020-12-13 21:39

I have searched far and wide, but can\'t seem to figure this pretty basic thing out. I have seen other examples on stackoverflow and elsewhere from a year or two ago, but t

3条回答
  •  有刺的猬
    2020-12-13 21:49

    Unfortunately I haven't worked with Quaternions, so can't help much. It seems to my that some offsetting is needed, since the cylinder's pivot is at the centre of the mesh, not at one end.

    If played with matrices a bit, and I've got decent results.

    Here's one way to this, using Mesh's lookAt() method:

    var HALF_PI = -Math.PI * .5;
    var p1 = new THREE.Vector3(Math.random()-.5,Math.random()-.5,Math.random()-.5).multiplyScalar(30);
    var p2 = new THREE.Vector3(Math.random(),Math.random(),Math.random()).multiplyScalar(300);
    var halfLength = diff.length() * .5;
    
    var c = new THREE.CylinderGeometry(10, 10, halfLength * 2, 12, 1, false );
    var orientation = new THREE.Matrix4();
    orientation.setRotationFromEuler(new THREE.Vector3(HALF_PI,0,0));//rotate on X 90 degrees
    orientation.setPosition(new THREE.Vector3(0,0,halfLength));//move half way on Z, since default pivot is at centre
    c.applyMatrix(orientation);//apply transformation for geometry
    
    var m = new THREE.Mesh( c, new THREE.MeshLambertMaterial( { color: 0x009900, wireframe: true, shading: THREE.FlatShading } ) );
    scene.add(m);
    m.lookAt(p2);//tell mesh to orient itself towards p2
    //just for debugging - to illustrate orientation
    m.add(new THREE.Axes());
    
    //visualize p1,p2 vectors
    var PI2 = Math.PI * 2;
    var program = function ( context ) {
    
        context.beginPath();
        context.arc( 0, 0, 1, 0, PI2, true );
        context.closePath();
        context.fill();
    
    }
    
    particleMaterial = new THREE.ParticleCanvasMaterial( { color: 0x990000, program: program } );
    var pp1 = new THREE.Particle( new THREE.ParticleCanvasMaterial( { color: 0x990000, program: program } ) );
    pp1.scale.multiplyScalar(10);
    pp1.position.copy(p1);
    scene.add( pp1 );   
    var pp2 = new THREE.Particle( new THREE.ParticleCanvasMaterial( { color: 0x009900, program: program } ) );
    pp2.scale.multiplyScalar(10);
    pp2.position.copy(p2);
    scene.add( pp2 );
    

    This should draw a cylinder that starts at p1, ends at p2 and is oriented towards it. Offsetting might need some tweaking, but the geometry follows the vector direction pretty close.

    There's also the longer version of manually computing the matrices, as opposed to relying on the lookAt() functionality:

    plane.add(getCylinderBetweenPoints(p1,p2,new THREE.MeshLambertMaterial( { color: 0x009900, wireframe: true, shading: THREE.FlatShading } )));
    
    function getCylinderBetweenPoints(point1,point2,material){
        var HALF_PI = -Math.PI * .5;
        var diff = new THREE.Vector3().sub(point1,point2);//delta vector
        var halfLength = diff.length() * .5;
        var c = new THREE.CylinderGeometry(10, 10, halfLength * 2, 12, 1, false );
        var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
        var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
        var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
        orientation.lookAt(point1,point2,new THREE.Vector3(0,1,0));//look at destination
        offsetRotation.setRotationX(HALF_PI);//rotate 90 degs on X
        offsetPosition.setPosition(new THREE.Vector3(-point1.x,diff.length()*.5+point1.z,point1.y*.5));//move by pivot offset on Y
        orientation.multiplySelf(offsetRotation);//combine orientation with rotation transformations
        orientation.multiplySelf(offsetPosition);//combine orientation with position transformations
        c.applyMatrix(orientation);//apply the final matrix
        var m = new THREE.Mesh( c, material );
        m.add(new THREE.Axes());
        return m;
    }
    
    var PI2 = Math.PI * 2;
    var program = function ( context ) {
    
        context.beginPath();
        context.arc( 0, 0, 1, 0, PI2, true );
        context.closePath();
        context.fill();
    
    }
    
    //visualize p1,p2 vectors
    particleMaterial = new THREE.ParticleCanvasMaterial( { color: 0x990000, program: program } );
    var pp1 = new THREE.Particle( new THREE.ParticleCanvasMaterial( { color: 0x990000, program: program } ) );
    pp1.scale.multiplyScalar(10);
    pp1.position.copy(p1);
    plane.add( pp1 );   
    var pp2 = new THREE.Particle( new THREE.ParticleCanvasMaterial( { color: 0x009900, program: program } ) );
    pp2.scale.multiplyScalar(10);
    pp2.position.copy(p2);
    plane.add( pp2 );
    

    This looks like me more work than using quaternion, from what I see in you're code. If the setFromEuler does the magic for orientation, the mesh's geometry still might need to move (pivot from one end rather than centre)

    HTH

提交回复
热议问题