javascript Three.js Object3d 圆柱体旋转对齐向量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9038465/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 05:24:41  来源:igfitidea点击:

Three.js Object3d cylinder rotation to align to a vector

javascriptrotationthree.jsquaternions

提问by coder99

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. I want the tip of the cone to be exactly aligned with the vector and point out from the origin in that direction.

我试图让灰色锥体与未标记的红色矢量完全对齐。IE。我希望锥体的尖端与向量完全对齐,并从原点指向该方向。

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

回答by peterjwest

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 axisis 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, 0and 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()

回答by Kevin Kuyl

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

回答by George Profenza

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:

这是使用 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. Offsetting might need some tweaking, but the geometry follows the vector direction pretty close.

这应该绘制一个从 p1 开始,在 p2 结束并朝向它的圆柱体。偏移可能需要一些调整,但几何形状非常接近矢量方向。

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