Tres.js - ¿Unir dos formas juntas como una?
-
26-10-2019 - |
Pregunta
Soy nuevo en el mundo tres.js ... mi pregunta es: ¿puedo unir dos formas diferentes como una forma? Por ejemplo, la esfera de unión y el cilindro juntos como uno?
Solución
Algo así, sí, hay múltiples opciones:
- a través de la jerarquía simplemente puede agregar una malla a otra usando el
add()
función - a través de geometryutil's
merge()
función para fusionar vértices y mallas de dos objetos de geometría en uno - Uso de un editor 3D básico que admite operaciones booleanas entre mallas y exportación.
El método 1 es bastante sencillo:
var sphere = new THREE.Mesh( new THREE.SphereGeometry(100,16,12),new THREE.MeshLambertMaterial( { color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ));
var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false ),new THREE.MeshLambertMaterial( { color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ));
cylinder.position.y = -100;
scene.add(sphere);
scene.add(cylinder);
Observe que 16 se repite, por lo que el nivel de las subdivisiones en una malla coincide con la otra (para un aspecto decente)
Método 2.1 - a través de geometrutils
//make a sphere
var sg = new THREE.SphereGeometry(100,16,12);
//make cylinder - ideally the segmentation would be similar to predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false );
//move vertices down for cylinder, so it maches half the sphere - offset pivot
for(var i = 0 ; i < cg.vertices.length; i++) cg.vertices[i].position.y -= 100;
//merge meshes
THREE.GeometryUtils.merge(sg,cg);
var mesh = new THREE.Mesh( sg,new THREE.MeshLambertMaterial( { color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ));
scene.add(mesh);
Método 2.2 Fusionar un Espigas de torno y un cilindro:
var pts = [];//points array
var detail = .1;//half-circle detail - how many angle increments will be used to generate points
var radius = 100;//radius for half_sphere
var total = Math.PI * .51;
for(var angle = 0.0; angle < total ; angle+= detail)//loop from 0.0 radians to PI (0 - 180 degrees)
pts.push(new THREE.Vector3(0,Math.cos(angle) * radius,Math.sin(angle) * radius));//angle/radius to x,z
var lathe = new THREE.LatheGeometry( pts, 16 );//create the lathe with 12 radial repetitions of the profile
//rotate vertices in lathe geometry by 90 degrees
var rx90 = new THREE.Matrix4();
rx90.setRotationFromEuler(new THREE.Vector3(-Math.PI * .5,0,0));
lathe.applyMatrix(rx90);
//make cylinder - ideally the segmentation would be similar for predictable results
var cg = new THREE.CylinderGeometry(100, 100, 200, 16, 4, false );
//move vertices down for cylinder, so it maches half the sphere
for(var i = 0 ; i < cg.vertices.length; i++) cg.vertices[i].position.y -= 100;
//merge meshes
THREE.GeometryUtils.merge(lathe,cg);
var mesh = new THREE.Mesh( lathe, new THREE.MeshLambertMaterial( { color: 0x2D303D, wireframe: true, shading: THREE.FlatShading } ) );
mesh.position.y = 150;
scene.add(mesh);
El único problema que no puedo abordar en este momento proviene de las caras que están dentro de la malla. Idealmente, esos tendrían normales volteados para que no renderizaran, pero no han encontrado una solución rápida para eso.
El tercero es bastante sencillo. La mayoría de los paquetes 3D permiten la operación booleana en mallas (por ejemplo, fusionar dos mallas junto con la operación Agregar (Mesha + Meshb)). Intente crear un cilindro y una esfera en Licuadora(GRATIS, OpenSource), que ya tiene un tres.js exportador. Alternativamente, puede exportar un archivo .obj de las mallas fusionadas de su editor o elección 3D y usar el convert_obj_three guion.
Actualizar
He encontrado otro método, que podría ser más fácil/más intuitivo. ¿Recuerdas las operaciones booleanas que he mencionado anteriormente?
Resulta que hay una increíble biblioteca JS solo para eso: Geometría sólida constructiva:
Chandler Prall escribió algunas funciones útiles para conectar CSG con tres.js. Entonces con la biblioteca CSG y el tres.js envoltura para ello, simplemente puedes hacer esto:
var cylinder = THREE.CSG.toCSG(new THREE.CylinderGeometry(100, 100, 200, 16, 4, false ),new THREE.Vector3(0,-100,0));
var sphere = THREE.CSG.toCSG(new THREE.SphereGeometry(100,16,12));
var geometry = cylinder.union(sphere);
var mesh = new THREE.Mesh(THREE.CSG.fromCSG( geometry ),new THREE.MeshNormalMaterial());
Que le da un buen resultado (sin problemas con caras adicionales/volteo normales/etc.):
Otros consejos
Actualicé Wrapper para tres.js R62, puedes encontrarlo aquí: https://github.com/kraag22/csg-wrapper