如何使用three.js r71合并两个几何体或网格?

35

我遇到了一个问题,因为我需要将两个几何体(或网格)合并成一个。在使用较早版本的Three.js时,有一个很好的函数:

Here I bumped to the problem since I need to merge two geometries (or meshes) to one. Using the earlier versions of three.js there was a nice function:


THREE.GeometryUtils.merge(pendulum, ball);

不过,它在新版本中已经没有了。

我试图使用以下代码合并 pendulumball:

ball 是一个网格。

var ballGeo = new THREE.SphereGeometry(24,35,35);
var ballMat = new THREE.MeshPhongMaterial({color: 0xF7FE2E}); 
var ball = new THREE.Mesh(ballGeo, ballMat); 
ball.position.set(0,0,0);

var pendulum = new THREE.CylinderGeometry(1, 1, 20, 16);
ball.updateMatrix();
pendulum.merge(ball.geometry, ball.matrix);
scene.add(pendulum);

毕竟,我收到了以下错误:

THREE.Object3D.add: object not an instance of THREE.Object3D. THREE.CylinderGeometry {uuid: "688B0EB1-70F7-4C51-86DB-5B1B90A8A24C", name: "", type: "CylinderGeometry", vertices: Array[1332], colors: Array[0]…}THREE.error @ three_r71.js:35THREE.Object3D.add @ three_r71.js:7770(anonymous function) @ pendulum.js:20
5个回答

45

为了更清楚地解释Darius的答案(我在尝试更新Mr Doob的程序性城市版本以使其与Face3盒子兼容时遇到了困难),您需要将所有Mesh合并成一个Geometry。因此,如果您例如要合并一个立方体和一个球:

var box = new THREE.BoxGeometry(1, 1, 1);
var sphere = new THREE.SphereGeometry(.65, 32, 32);

将多个几何体合并为一个几何体:

var singleGeometry = new THREE.Geometry();

...你需要为每个几何体创建一个网格:

var boxMesh = new THREE.Mesh(box);
var sphereMesh = new THREE.Mesh(sphere);

接着对于每个几何体,调用单个几何体的合并方法,并将每个几何体和矩阵传入该方法:

boxMesh.updateMatrix(); // as needed
singleGeometry.merge(boxMesh.geometry, boxMesh.matrix);

sphereMesh.updateMatrix(); // as needed
singleGeometry.merge(sphereMesh.geometry, sphereMesh.matrix);

合并后,从单个几何体创建网格并添加到场景中:

var material = new THREE.MeshPhongMaterial({color: 0xFF0000});
var mesh = new THREE.Mesh(singleGeometry, material);
scene.add(mesh);

一个可用的示例:

<!DOCTYPE html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.js"></script>
<!-- OrbitControls.js is not versioned and may stop working with r77 -->
<script src='http://threejs.org/examples/js/controls/OrbitControls.js'></script>

<body style='margin: 0px; background-color: #bbbbbb; overflow: hidden;'>
  <script>
    // init renderer
    var renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // init scene and camera
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 3000);
    camera.position.z = 5;
    var controls = new THREE.OrbitControls(camera)
    
    // our code
    var box = new THREE.BoxGeometry(1, 1, 1);
    var sphere = new THREE.SphereGeometry(.65, 32, 32);

    var singleGeometry = new THREE.Geometry();

    var boxMesh = new THREE.Mesh(box);
    var sphereMesh = new THREE.Mesh(sphere);

    boxMesh.updateMatrix(); // as needed
    singleGeometry.merge(boxMesh.geometry, boxMesh.matrix);

    sphereMesh.updateMatrix(); // as needed
    singleGeometry.merge(sphereMesh.geometry, sphereMesh.matrix);

    var material = new THREE.MeshPhongMaterial({color: 0xFF0000});
    var mesh = new THREE.Mesh(singleGeometry, material);
    scene.add(mesh);

    // a light
    var light = new THREE.HemisphereLight(0xfffff0, 0x101020, 1.25);
    light.position.set(0.75, 1, 0.25);
    scene.add(light);
 
    // render
    requestAnimationFrame(function animate(){
     requestAnimationFrame(animate);
     renderer.render(scene, camera);  
    })
  </script>
</body>

至少,这是我对事情的解释;如果我有什么错误,请向任何人道歉,因为我远远不是一个three.js专家(目前正在学习)。 我只是不幸地尝试着定制Mr. Doob的过程性城市代码,当最新版本出问题时(其中包括合并工具等问题,而three.js不再使用四边形来表示立方体-咳咳-盒子几何体,另一个问题是所有种类的有趣问题都会使阴影和其他东西正常工作)。


谢谢。刚刚移植的代码有500,000个警告,更新起来看起来非常困难。我对这种处理方式并不满意,对于我无法控制的事情,找到答案太困难了。 - fluffybunny

23

最后,我找到了一个可能的解决方案。我发布此文章是因为这可能对其他人有用,而我浪费了很多时间。关键在于操作网格和几何体的概念:

var ballGeo = new THREE.SphereGeometry(10,35,35);
var material = new THREE.MeshPhongMaterial({color: 0xF7FE2E}); 
var ball = new THREE.Mesh(ballGeo, material);

var pendulumGeo = new THREE.CylinderGeometry(1, 1, 50, 16);
ball.updateMatrix();
pendulumGeo.merge(ball.geometry, ball.matrix);

var pendulum = new THREE.Mesh(pendulumGeo, material);
scene.add(pendulum);

10
“Tyler Crompton在生命中的所作所为一个也不是无用的。” - Lady Galadriel - LaughingMan
这是一个非常有用的解决方案,甚至允许mesh2围绕mesh1旋转,如果mesh1.position.set(xx, 0, 0); 如果设置为(0, 0, 0),它将居中于或环绕mesh1,取决于大小。 - user6337390
1
谢谢分享!我用它来合并新的网格/几何体到现有的网格中。function AddToGeometry(mainObject, objectToAdd) { objectToAdd.updateMatrix(); mainObject.geometry.merge(objectToAdd.geometry, objectToAdd.matrix); return mainObject; } - unx
为什么我们需要网格?根据我理解文档,您可以在不创建网格的情况下合并几何图形,我错了吗?另外,即使您确实需要网格,在合并后将不再使用的前两个清除,这难道不是更好的选择吗? - e-motiv

6
这个错误信息是正确的,CylinderGeometry不是Object3D,而Mesh是。Mesh由一个Geometry和一个Material构成。Mesh可以添加到场景中,而Geometry则不行。
在最新版本的three.js中,Geometry有两个merge方法:mergemergeMesh
  1. merge需要一个必填参数geometry,和两个可选参数matrixmaterialIndexOffset
  2. geom.mergeMesh(mesh)基本上是一个shorthand,相当于geom.merge(mesh.geometry, mesh.matrix),就像其他答案中使用的那样。('geom'和'mesh'是任意名称,分别表示Geometry和Mesh。)Mesh的材质会被忽略。

合并网格赢了。这样更简单。 - Rob Kwasowski

5
这是我的终极紧凑版,只需四 (或五) 行代码即可实现合并网格,(只要材质已在其他地方定义)。使用mergeMesh方法:

var geom = new THREE.Geometry();
geom.mergeMesh(new THREE.Mesh(new THREE.BoxGeometry(2,20,2)));
geom.mergeMesh(new THREE.Mesh(new THREE.BoxGeometry(5,5,5)));
geom.mergeVertices(); // optional
scene.add(new THREE.Mesh(geom, material));

编辑:添加了可选的额外行来删除重复顶点,这应该有助于提高性能。

编辑2:我正在使用最新版本94。


1
请告诉我们您正在使用的当前版本号? - Darius Miliauskas
看起来不错。它也适用于使用VertexColorBufferGeometry吗? - Michael
1
.Geometry()现在是BufferGeometry了吗?版本130。 - pery mimon
1
在版本131中,执行'merge'操作时,无法找到mergeMesh和mergeVertices,虽然不会报错,但它无法正常工作。 - Master James
https://threejs.org/docs/#examples/en/utils/BufferGeometryUtils.mergeGeometries - handle

0

我在这里看到的答案和代码都不起作用,因为合并方法的第二个参数是一个整数,而不是一个矩阵。就我所知,合并方法并没有以有用的方式发挥作用。因此,我使用了以下方法制作了一个带尖锥的简单火箭。

import * as BufferGeometryUtils from '../three.js/examples/jsm/utils/BufferGeometryUtils.js'

lengthSegments = 2
radius = 5
radialSegments = 32
const bodyLength = dParamWithUnits['launchVehicleBodyLength'].value
const noseConeLength = dParamWithUnits['launchVehicleNoseConeLength'].value

// Create the vehicle's body
const launchVehicleBodyGeometry = new THREE.CylinderGeometry(radius, radius, bodyLength, radialSegments, lengthSegments, false)
launchVehicleBodyGeometry.name = "body"
// Create the nose cone
const launchVehicleNoseConeGeometry = new THREE.CylinderGeometry(0, radius,  noseConeLength, radialSegments, lengthSegments, false)
launchVehicleNoseConeGeometry.name = "noseCone"
launchVehicleNoseConeGeometry.translate(0, (bodyLength+noseConeLength)/2, 0)
// Merge the nosecone into the body
const launchVehicleGeometry = BufferGeometryUtils.mergeBufferGeometries([launchVehicleBodyGeometry, launchVehicleNoseConeGeometry])
// Rotate the vehicle to horizontal
launchVehicleGeometry.rotateX(-Math.PI/2)
const launchVehicleMaterial = new THREE.MeshPhongMaterial( {color: 0x7f3f00})
const launchVehicleMesh = new THREE.Mesh(launchVehicleGeometry, launchVehicleMaterial)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接