javascript - texture - Rotación del cilindro de Object.d de Three.js para alinearse con un vector
three.js tutorial (3)
Desafortunadamente no he trabajado con Quaternions, así que no puedo ayudar mucho. Me parece que es necesaria cierta compensación, ya que el pivote del cilindro está en el centro de la malla, no en un extremo.
Si se juega con matrices un poco, y tengo resultados decentes.
Aquí hay una forma de hacerlo, utilizando el método lookAt () de Mesh:
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 );
Esto debería dibujar un cilindro que comienza en p1, termina en p2 y está orientado hacia él. La compensación puede necesitar algunos ajustes, pero la geometría sigue la dirección del vector bastante cerca.
También existe la versión más larga de calcular manualmente las matrices, en lugar de confiar en la funcionalidad 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 );
Esto me parece más trabajo que usar quaternion, por lo que veo en tu código. Si el setFromEuler hace la magia para la orientación, la geometría de la malla aún podría necesitar moverse (pivotar desde un extremo en lugar de centrar)
HTH
He buscado por todas partes, pero parece que no puedo entender esta cosa bastante básica. He visto otros ejemplos en stackoverflow y en otros lugares hace un año o dos, pero no funcionan con la última versión de Three.js.
Aquí hay una versión de lo que estoy trabajando: http://medschoolgunners.com/sandbox/3d/ .
Intento que el cono gris se alinee exactamente con el vector rojo sin etiqueta. Es decir. Quiero que la punta del cono esté exactamente alineada con el vector y señale desde el origen en esa dirección.
Aquí está el código que tengo ahora:
//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);
Sé que esta es una vieja pregunta, pero en caso de que alguien todavía se esté preguntando, lo que funcionó para mí fue agregar el vector a la posición de malla y usar lookAt para alinearlo con el 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);
Si tienes un vector arbitrario:
var vector = new THREE.Vector3(100, 60, 20);
Puede alinear un objeto, como un cilindro, con el vector así:
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());
Donde axis
es la dirección original del cilindro (apuntando hacia arriba).
También puede mover el cilindro para que coincida con la posición del vector de esta manera:
mesh.position.copy(vector.clone().multiplyScalar(0.5));
Esto pone un extremo del cilindro en el 0, 0, 0
y el otro en 100, 60, 20
, y funciona porque vector.length()
longitud del cilindro a vector.length()
.