Three.js线向量转换为圆柱体?

14

我有以下代码可以在两点之间创建一条线:

var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, 0));
geometry.vertices.push(new THREE.Vector3(20, 100, 55));
var line = new THREE.Line(geometry, material, parameters = { linewidth: 400 });
scene.add(line);

(线宽度不起作用。)

我的问题是如何将其转换为圆柱体?我想在两个点之间创建一个圆柱体。


这似乎是一个非常类似的问题:在两个向量之间显示一个圆柱体。 - jdregister
这是 https://dev59.com/Hmox5IYBdhLWcg3wr2To#31987883 的副本。 - peterjwest
10个回答

11

我曾经遇到过完全相同的问题,就是在WebGL中线的宽度总是1。所以这里有一个函数,我写了它可以接受两个Vector3对象并生成一个圆柱网格:

var cylinderMesh = function( pointX, pointY )
{
    // edge from X to Y
    var direction = new THREE.Vector3().subVectors( pointY, pointX );
    var arrow = new THREE.ArrowHelper( direction, pointX );

    // cylinder: radiusAtTop, radiusAtBottom, 
    //     height, radiusSegments, heightSegments
    var edgeGeometry = new THREE.CylinderGeometry( 2, 2, direction.length(), 6, 4 );

    var edge = new THREE.Mesh( edgeGeometry, 
        new THREE.MeshBasicMaterial( { color: 0x0000ff } ) );
    edge.rotation = arrow.rotation.clone();
    edge.position = new THREE.Vector3().addVectors( pointX, direction.multiplyScalar(0.5) );
    return edge;
}

1
应该是 .subVectors( pointY, pointX ).addVectors( pointX, direction.multiplyScalar(0.5) ) - mrdoob
似乎没有任何效果。我想我犯了一个错误。向量线出现了,但是没有圆柱体,也没有错误。这是我使用的代码:http://pastie.org/private/znr7wa4d4delrjifxbegq - Rails beginner
我认为错误可能在这里 edge.rotation = arrow.rotation.clone(),但我没有去检查,最终手动旋转了。 - user151496
Object3D的位置和旋转现在是只读的,因此最后两行应为:edge.setRotationFromEuler(arrow.rotation); edge.position.copy(direction.multiplyScalar(0.5).add(pointX)); - Eran
只有在将向量方向归一化之后才能起作用。 - undefined

8
截至2020年12月:
我尝试了之前回答中提到的所有方法,但都没有奏效。
我的解决方案:
cylinderMesh = function (pointX, pointY) {
  // edge from X to Y
  var direction = new THREE.Vector3().subVectors(pointY, pointX);
  const material = new THREE.MeshBasicMaterial({ color: 0x5B5B5B });
  // Make the geometry (of "direction" length)
  var geometry = new THREE.CylinderGeometry(0.04, 0.04, direction.length(), 6, 4, true);
  // shift it so one end rests on the origin
  geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, direction.length() / 2, 0));
  // rotate it the right way for lookAt to work
  geometry.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(90)));
  // Make a mesh with the geometry
  var mesh = new THREE.Mesh(geometry, material);
  // Position it where we want
  mesh.position.copy(pointX);
  // And make it point to where we want
  mesh.lookAt(pointY);

  return mesh;
}

2
这个答案截至2022年1月完美运行! - Hoff
1
applyMatrix在r113中被重命名为applyMatrix4,同时Math也被重命名为MathUtils。 - Eran

4
在70版本中停止工作,但是通过此更新可以解决 :)
function cylinderMesh(pointX, pointY, material) {
            var direction = new THREE.Vector3().subVectors(pointY, pointX);
            var orientation = new THREE.Matrix4();
            orientation.lookAt(pointX, pointY, new THREE.Object3D().up);
            orientation.multiply(new THREE.Matrix4().set(1, 0, 0, 0,
                0, 0, 1, 0,
                0, -1, 0, 0,
                0, 0, 0, 1));
            var edgeGeometry = new THREE.CylinderGeometry(2, 2, direction.length(), 8, 1);
            var edge = new THREE.Mesh(edgeGeometry, material);
            edge.applyMatrix(orientation);
            // position based on midpoints - there may be a better solution than this
            edge.position.x = (pointY.x + pointX.x) / 2;
            edge.position.y = (pointY.y + pointX.y) / 2;
            edge.position.z = (pointY.z + pointX.z) / 2;
            return edge;
        }

这与Kenny的答案有何不同? - kon psych
非常好,谢谢。使用r91。您知道如何为2个移动点制作动画吗?您需要重新创建圆柱体,还是有其他方法?谢谢(我是新手) - Jarrod McGuire

4

实际上,其他答案都对我没用,但为了给予信任,我使用了能够调整边缘位置且功能良好的代码。

使用这个答案的部分内容,以及查看threejs源代码后,我最终得到了以下结果:

var cylinderMesh = function( pointX, pointY )
{
    /* edge from X to Y */
    var direction = new THREE.Vector3().subVectors( pointY, pointX );
    var orientation = new THREE.Matrix4();
    /* THREE.Object3D().up (=Y) default orientation for all objects */
    orientation.lookAt(pointX, pointY, new THREE.Object3D().up);
    /* rotation around axis X by -90 degrees 
     * matches the default orientation Y 
     * with the orientation of looking Z */
    orientation.multiply(new THREE.Matrix4(1,0,0,0,
                                            0,0,1,0, 
                                            0,-1,0,0,
                                            0,0,0,1));

    /* cylinder: radiusAtTop, radiusAtBottom, 
        height, radiusSegments, heightSegments */
    var edgeGeometry = new THREE.CylinderGeometry( 2, 2, direction.length(), 8, 1);
    var edge = new THREE.Mesh( edgeGeometry, 
            new THREE.MeshBasicMaterial( { color: 0x0000ff } ) );

    edge.applyMatrix(orientation)
    edge.position = new THREE.Vector3().addVectors( pointX, direction.multiplyScalar(0.5) );
    return edge;
}

一个可能的改进是添加edgeGeometry和material作为参数,以便某人可以重复使用相同的对象,而不必在每次调用中创建新对象。


截至2017年10月仍然有效 - 此页面上的其他解决方案已过时。 - David
这在r84上对我没有用。我必须先创建一个Matrix4,然后设置它的参数。像这样:var m = new THREE.Matrix4(); m.set(1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1); orientation.multiply(m); - Stian Jensen

2
“ArrowHelper自从拉请求#3307以四元数为基础。”
“这在three.js r58中有效:”
    var cylinderMesh = function(point1, point2, material)
    {
        var direction = new THREE.Vector3().subVectors(point2, point1);
        var arrow = new THREE.ArrowHelper(direction.clone().normalize(), point1);

        var rotation = new THREE.Vector3().setEulerFromQuaternion(arrow.quaternion);

        var edgeGeometry = new THREE.CylinderGeometry( 2, 2, direction.length(), 10, 4 );

        var edge = new THREE.Mesh(edgeGeometry, material);
        edge.rotation = rotation.clone();
        edge.position = new THREE.Vector3().addVectors(point1, direction.multiplyScalar(0.5));

        return edge;
    }

2

我觉得我应该发布这篇文章,因为其他答案没有完全解决我的问题。我注意到气缸的方向是正确的,但它们是相对于原点定位的。以下代码(基于此问题的其他答案)适用于我:

function cylinderMesh(pointX, pointY, material) {
    var direction = new THREE.Vector3().subVectors(pointY, pointX);
    var orientation = new THREE.Matrix4();
    orientation.lookAt(pointX, pointY, new THREE.Object3D().up);
    orientation.multiply(new THREE.Matrix4(1, 0, 0, 0,
                                           0, 0, 1, 0,
                                           0, -1, 0, 0,
                                           0, 0, 0, 1));
    var edgeGeometry = new THREE.CylinderGeometry(2, 2, direction.length(), 8, 1);
    var edge = new THREE.Mesh(edgeGeometry, material);
    edge.applyMatrix(orientation);
    // position based on midpoints - there may be a better solution than this
    edge.position.x = (pointY.x + pointX.x) / 2;
    edge.position.y = (pointY.y + pointX.y) / 2;
    edge.position.z = (pointY.z + pointX.z) / 2;
    return edge;
}

希望这能帮助到某些人!

2
一个适用于r94版本的解决方案:
function cylindricalSegment(A, B, radius, material) {
  var vec = B.clone(); vec.sub(A);
  var h = vec.length();
  vec.normalize();
  var quaternion = new THREE.Quaternion();
  quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), vec);
  var geometry = new THREE.CylinderGeometry(radius, radius, h, 32);
  geometry.translate(0, h / 2, 0);
  var cylinder = new THREE.Mesh(geometry, material);
  cylinder.applyQuaternion(quaternion);
  cylinder.position.set(A.x, A.y, A.z);
  return cylinder;
}

编辑(2022年)

我觉得这个解决方案不再有效。无论如何,下面的解决方案更有效(假设您所有的圆柱体半径相同):

    const radius = 0.07;
    let geomUnitCylinder = new THREE.CylinderGeometry(radius, radius, 1, 64, 1, true);
    geomUnitCylinder.translate(0, 0.5, 0);

    const J = new THREE.Vector3(0, 1, 0);

    function geomTube(P, Q) {
        let vec = Q.clone().sub(P);
        const h = vec.length();
        vec.normalize();
        const quaternion = new THREE.Quaternion().setFromUnitVectors(J, vec);
        let geometry = geomUnitCylinder.clone();
        geometry.scale(1, h, 1);
        geometry.applyQuaternion(quaternion);
        geometry.translate(P.x, P.y, P.z);
        return geometry;
    }

1
旋转方向,您可以使用:

edge.rotateX(Math.PI / 2);

或者

edge.rotateY(Math.PI / 2);

或者

edge.rotateZ(Math.PI / 2);

根据您想要旋转的坐标,因此上面发布的代码为:
var orientation = new THREE.Matrix4();
/* THREE.Object3D().up (=Y) default orientation for all objects */
orientation.lookAt(pointX, pointY, new THREE.Object3D().up);
/* rotation around axis X by -90 degrees 
 * matches the default orientation Y 
 * with the orientation of looking Z */
orientation.multiply(new THREE.Matrix4(1,0,0,0,
                                       0,0,1,0, 
                                       0,-1,0,0,
                                       0,0,0,1));

不需要。

0

虽然这是一个老问题,但今天仍然很相关。在Lee Stemkoski的回答基础上,我最终得到了以下可行的解决方案:

const cylinderMesh = function( pointX, pointY )
{
    // edge from X to Y
    const direction = new THREE.Vector3().subVectors( pointY, pointX );
    const arrow = new THREE.ArrowHelper( direction.clone().normalize(), pointY );

    // cylinder: radiusAtTop, radiusAtBottom, 
    //     height, radiusSegments, heightSegments
    const edgeGeometry = new THREE.CylinderGeometry( 2, 2, direction.length(), 6, 4 );

    const edge = new THREE.Mesh( edgeGeometry, 
        new THREE.MeshBasicMaterial( { color: 0x0000ff } ) );
    edge.rotation = arrow.rotation.clone();
    edge.position = new THREE.Vector3().addVectors( pointX, direction.multiplyScalar(0.5) );
    return edge;
}

请注意主要区别:您需要对direction向量进行归一化,并将原点设为pointY。归一化direction向量需要使用.clone(),以便稍后在position计算中可以使用它。

-1
我发现只有一个点能够匹配,所以我将其编辑为以下内容(将pointX改为pointY),然后它就起作用了。
edge.position = new
THREE.Vector3().addVectors(pointY,direction.multiplyScalar(0.5));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接