Three.js Object3d cylinder rotation to align to a vector

后端 未结 3 1412
梦毁少年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

    0 讨论(0)
  • 2020-12-13 21:55

    i know this is an old question, but in case anyone is still wondering, what worked for me was adding the vector to the mesh position and use lookAt to align it to the vector:

    //Mesh to align
    var material = new THREE.MeshLambertMaterial({color: 0x0000ff});
    var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(10, 10, 15), material);
    
    //vector to align to
    var vector = new THREE.Vector3(
        5,//x
        10,//y
        15 //z
    );
    
    //create a point to lookAt
    var focalPoint = new THREE.Vector3(
        cylinder.position.x + vector.x,
        cylinder.position.y + vector.y,
        cylinder.position.z + vector.z
    );
    
    //all that remains is setting the up vector (if needed) and use lookAt
    cylinder.up = new THREE.Vector3(0,0,1);//Z axis up
    cylinder.lookAt(focalPoint); 
    
    0 讨论(0)
  • 2020-12-13 22:03

    If you have an arbitrary vector:

    var vector = new THREE.Vector3(100, 60, 20);
    

    You can align an object, such as a cylinder, to the vector like this:

    var geometry = new THREE.CylinderGeometry(2, 2, vector.length(), 4, 4);
    var mesh = new THREE.Mesh(geometry, someMaterial);
    var axis = new THREE.Vector3(0, 1, 0);
    mesh.quaternion.setFromUnitVectors(axis, vector.clone().normalize());
    

    Where axis is the original direction of the cylinder (pointing up).

    You can also move the cylinder to match the position of the vector like this:

    mesh.position.copy(vector.clone().multiplyScalar(0.5));
    

    This puts one end of the cylinder at the 0, 0, 0 and the other at 100, 60, 20, and works because I set the cylinder length to vector.length().

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