简体   繁体   English

Three.js Object3d柱面旋转以对齐矢量

[英]Three.js Object3d cylinder rotation to align to a vector

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 they fail to work with the latest version of Three.js. 我已经在一年或两年前看过stackoverflow和其他地方的其他例子,但他们无法使用最新版本的Three.js。

Here is a version of what i'm working on: http://medschoolgunners.com/sandbox/3d/ . 这是我正在研究的版本: http//medschoolgunners.com/sandbox/3d/

I'm trying to get the grey cone to exactly align with the unlabeled red vector. 我试图让灰色圆锥与未标记的红色矢量完全对齐。 Ie. IE浏览器。 I want the tip of the cone to be exactly aligned with the vector and point out from the origin in that direction. 我希望锥体的尖端与矢量精确对齐,并从该方向的原点指出。

Here is the code I have right now: 这是我现在的代码:

    //FUNCTION TO CREATE A CYLINDER
function create_cylinder(radiusTop,radiusBottom, height, segmentsRadius, segmentsHeight, openEnded, color)
{
var material = new THREE.MeshLambertMaterial({
    color: color, //0x0000ff
    opacity: 0.2
});
var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(radiusTop,radiusBottom, height, segmentsRadius, segmentsHeight, openEnded), material);

cylinder.overdraw = true;

return cylinder;
}

//ALIGN THE CYLINDER TO A GIVEN VECTOR
var alignVector=new THREE.Vector3(-50,50,50); //the vector to be aligned with

var newcylinder = create_cylinder(0.1, 10, 40, 50, 50, false, "0x0ff0f0");  // the object to be aligned with the vector above

var cylinderQuaternion = new THREE.Quaternion();
cylinderQuaternion.setFromEuler(alignVector);
newcylinder.useQuaternion = true;
newcylinder.quaternion=cylinderQuaternion;

scatterPlot.add(newcylinder);

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). 其中axis是圆柱体的原始方向(向上)。

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, 0 ,另一个在100, 60, 20 ,和工作,因为我设置气缸长度vector.length()

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: 我知道这是一个老问题,但万一有人还在想,对我有用的是将矢量添加到网格位置并使用lookAt将其与矢量对齐:

//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); 

Unfortunately I haven't worked with Quaternions, so can't help much. 不幸的是我没有使用Quaternions,因此无济于事。 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: 这是一种方法,使用Mesh的lookAt()方法:

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. 这应绘制一个从p1开始,在p2结束并朝向它的圆柱体。 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: 还有更长版本的手动计算矩阵,而不是依赖于lookAt()功能:

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) 如果setFromEuler为方向做了魔术,网格的几何体仍然可能需要移动(从一端而不是从中心转动)

HTH HTH

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM