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

发布于 2024-12-29 12:37:50 字数 1226 浏览 1 评论 0原文

我进行了广泛的搜索,但似乎无法弄清楚这个非常基本的事情。我在一两年前在 stackoverflow 和其他地方看到过其他示例,但它们无法与最新版本的 Three.js 一起使用。

这是我正在研究的一个版本:http://medschoolgunners.com/sandbox/3d/。

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

这是我现在的代码:

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

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.

Here is a version of what i'm working on: 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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

森林迷了鹿 2025-01-05 12:37:50

如果您有任意向量:

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

您可以将一个对象(例如圆柱体)与该向量对齐,如下所示:

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

其中 axis 是圆柱体的原始方向(指向上方)。

您还可以移动圆柱体以匹配矢量的位置,如下所示:

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

这会将圆柱体的一端置于 0, 0, 0 处,另一端置于 100, 60, 20< /code>,并且可以工作,因为我将圆柱体长度设置为 vector.length()

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().

彻夜缠绵 2025-01-05 12:37:50

我知道这是一个老问题,但如果有人仍然想知道,对我有用的是将向量添加到网格位置并使用 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); 

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); 
狂之美人 2025-01-05 12:37:50

不幸的是我没有使用过四元数,所以帮不上什么忙。在我看来,需要进行一些偏移,因为圆柱体的枢轴位于网格的中心,而不是一端。

如果稍微玩一下矩阵,我会得到不错的结果。

这是一种方法,使用 Mesh 的 viewAt() 方法:

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

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

还有更长的手动计算矩阵的版本,而不是依赖 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 );

从我在代码中看到的情况来看,这看起来比使用四元数需要更多的工作。如果 setFromEuler 发挥了定向作用,网格体的几何形状仍然可能需要移动(从一端而不是中心旋转)

HTH

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文