如何在 Three.js 中将两个形状绑定在一起?

发布于 2024-12-18 17:39:51 字数 55 浏览 0 评论 0原文

我可以将两个不同的形状绑定在一起作为一个形状吗?

例如,将球体和圆柱体结合在一起?

Can I bind two different shapes together as one shape?

For example, binding sphere and cylinder together as one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情徒 2024-12-25 17:39:51

有点,是的。有多种选择:

  1. 通过层次结构,您可以
  2. 通过 GeometryUtil 的 merge() 函数使用 add() 函数将一个网格简单地添加到另一个网格,以合并两个 add() 函数的顶点和网格 将 em>Geometry 对象合并为一个
  3. 使用基本的 3D 编辑器

,该编辑器支持网格之间的布尔运算和导出。方法 1 非常简单:

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

请注意,重复了 16 个网格,因此一个网格中的细分级别与另一个网格中的细分级别相匹配(以获得不错的外观)。

方法 2.1 - 通过 GeometryUtils

// Make a sphere

var sg = new THREE.SphereGeometry(100, 16, 12);
// Make a 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);

方法 2.2 合并 车床半球 和圆柱体:

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

我目前无法解决的一个问题来自网格内部的面。理想情况下,这些法线会翻转,因此它们不会渲染,但我还没有找到快速的解决方案。

第三个相当简单。大多数3D 软件包允许对网格进行布尔运算(例如,通过ADD 操作将两个网格合并在一起(meshA + meshB))。尝试在 Blender(免费且开源)中创建一个圆柱体和一个球体,它已经有一个 Three.js 导出器。或者,您可以从 3D 编辑器或选择导出合并网格体的 .obj 文件,并使用 convert_obj_third 脚本。

我发现了另一种方法,它可能更容易/更直观。还记得我上面提到的布尔运算吗?

事实证明,有一个很棒的 JavaScript 库专门用于此目的:构造实体几何

来自所有者的 GitHub 页面的 CSG 库预览

Chandler Prall 编写了一些方便的函数来连接 CSG 与 Three.js。因此,有了 CSG 库和Three.js 包装器,你可以简单地这样做:

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

这会给你一个很好的结果(没有额外的面/翻转法线等问题):

Cylinder-sphere union with CSG and Three.js

Kind of, yes. There are multiple options:

  1. via hierarchy you can simply add one mesh to another using the add() function
  2. via the GeometryUtil's merge() function to merge vertices and meshes of two Geometry objects into one
  3. using a basic 3D editor that supports Boolean operations between meshes and exporting.

Method 1 is pretty straightforward:

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

Notice that 16 is repeated, so the subdivisions level in one mesh matches the other (for a decent look).

Method 2.1 - via GeometryUtils

// Make a sphere

var sg = new THREE.SphereGeometry(100, 16, 12);
// Make a 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);

Method 2.2 merging a Lathe half-sphere and a cylinder:

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

The one problem I can't address at the moment comes from the faces that are inside the mesh. Ideally, those would have normals flipped, so they wouldn't render, but I haven't found a quick solution for that.

The third is fairly straightforward. Most 3D packages allow Boolean operation on meshes (e.g., merging two meshes together with the ADD operation (meshA + meshB)). Try creating a cylinder and a sphere in Blender (free and opensource), which already has a Three.js exporter. Alternatively you can export an .obj file of the merged meshes from your 3D editor or choice and use the convert_obj_three script.

I've found yet another method, which might be easier/more intuitive. Remember the Boolean operations I've mentioned above?

It turns out there is an awesome JavaScript library just for that: Constructive Solid Geometry:

CSG library preview from the owner's GitHub page

Chandler Prall wrote some handy functions to connect CSG with three.js. So with the CSG library and the Three.js wrapper for it, you can simply do this:

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

Which gives you a nice result (no problems with extra faces/flipping normals, etc.):

Cylinder-sphere union with CSG and Three.js

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文