Вращение цилиндра 3D.js Object3d для выравнивания с вектором
Я искал по всему миру, но, похоже, не мог понять эту довольно элементарную вещь. Я видел другие примеры в stackoverflow и других местах за год или два назад, но они не работают с последней версией Three.js.
Вот версия того, над чем я работаю: http://medschoolgunners.com/sandbox/3d/.
Я стараюсь, чтобы серый конус точно совпадал с немаркированным красным вектором. То есть. Я хочу, чтобы кончик конуса был точно выровнен с вектором и указывал из начала координат в этом направлении.
Вот код, который у меня есть прямо сейчас:
//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);
Ответы
Ответ 1
Если у вас есть произвольный вектор:
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
, и работает, потому что я установил длину цилиндра vector.length()
.
Ответ 2
Я знаю, что это старый вопрос, но если кому-то все еще интересно, то, что сработало для меня, было добавление вектора в позицию сетки и использование 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);
Ответ 3
К сожалению, я не работал с Quaternions, так что не могу много помочь. Мне кажется, что требуется какое-то смещение, так как ось цилиндра находится в центре сетки, а не на одном конце.
Если вы играете с матрицами немного, и у меня есть достойные результаты.
Здесь один путь к этому, используя метод 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 );
Это должно привести цилиндр, который начинается с 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 делает магию для ориентации, геометрия сетки все равно может понадобиться перемещаться (поворачиваться с одного конца, а не центра).
НТН