17

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

3 に答える 3

8

私はこれが古い質問であることを知っていますが、誰かがまだ疑問に思っている場合に備えて、メッシュ位置にベクトルを追加し、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); 
于 2014-06-06T20:41:19.277 に答える
4

残念ながら、私はクォータニオンを扱ったことがないので、あまり役に立ちません。円柱のピボットはメッシュの一端ではなく中心にあるため、オフセットが必要なようです。

マトリックスで少し遊んだら、まともな結果が得られました。

Mesh の lookAt() メソッドを使用して、これを行う 1 つの方法を次に示します。

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 で終了し、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

于 2012-01-29T08:39:55.993 に答える