问题描述
我正在尝试创建一个适合管道领域的 T 恤。它由合并在一起的 2 个管组成,并具有 3 个开口,如 this 图片所示。
我在threejs中编写了一些代码,我试图创建一个管mesh1和另一个管mesh2,然后尝试使用库@enable3d/three-graphics/jsm/csg将它们合并到mesh3中——感谢@Marquizzo。在使用 CSG.union 函数并将网格添加到场景中后,我可以看到我得到了一个 T 恤,但它也在几何体 1 中创建了一个洞,这是意料之外的。您可以在此处查看正确孔(绿色)和错误创建的孔(红色)的图片:
它应该看起来像这样并且是一个几何体。
谁能告诉我 CSG 是如何工作的,以及为什么我在第一个几何体的背面有一个额外的孔?
import React,{ Component } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSG } from '@enable3d/three-graphics/jsm/csg';
export default class TubeViewer extends Component {
componentDidMount() {
//Add Scene
this.scene = new THREE.Scene();
//Add Renderer
this.renderer = new THREE.Webglrenderer({ antialias: true });
this.renderer.setClearColor('#808080');
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth,window.innerHeight);
this.mount.appendChild(this.renderer.domElement);
//Add Camera
const fov = 60;
const aspect = window.innerWidth / window.innerHeight;
const near = 1.0;
const far = 1000.0;
this.camera = new THREE.PerspectiveCamera(fov,aspect,near,far);
this.camera.position.set(1,1,1000);
//Tee-piece
const curve1 = new THREE.LineCurve(new THREE.Vector3(2,0),new THREE.Vector3(2,0.1));
const curve11 = new THREE.LineCurve(new THREE.Vector3(2.0,0.05),new THREE.Vector3(2.05,0.05));
const geometry1 = new THREE.TubeGeometry(curve1,20,0.025,8,false);
const geometry2 = new THREE.TubeGeometry(curve2,false);
const material = new THREE.MeshBasicMaterial({ color: '#C0C0C0' });
const mesh1 = new THREE.Mesh(geometry1,material);
const mesh2 = new THREE.Mesh(geometry2,material);
const mesh3 = CSG.union(mesh1,mesh2);
this.scene.add(mesh3);
//Add raycaster to for interactivity
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.renderer.domElement.addEventListener('click',onClick.bind(this),false);
function onClick(event) {
event.preventDefault();
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse,this.camera);
var intersects = this.raycaster.intersectObjects(this.scene.children,true);
if (intersects.length > 0) {
console.log('Intersection:',intersects[0]);
//console.log(intersects[0].object.uuid);
// console.log(`GUID: ${intersects[0]}`);
let object = intersects[0].object;
object.material.color.set(Math.random() * 0xffffff);
}
}
//Settings
//Add Camera Controls
const controls = new OrbitControls(this.camera,this.renderer.domElement);
controls.addEventListener('change',this.render); // use if there is no animation loop
controls.mindistance = 2;
controls.maxdistance = 10;
controls.target.set(0,-0.2);
controls.update();
///Add AMBIENT LIGHT
let light = new THREE.DirectionalLight(0xffffff,1.0);
light.position.set(20,100,10);
light.target.position.set(0,0);
light.castShadow = true;
light.shadow.bias = -0.001;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 500.0;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500.0;
light.shadow.camera.left = 100;
light.shadow.camera.right = -100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
this.scene.add(light);
light = new THREE.AmbientLight(0xffffff,0.7);
this.scene.add(light);
//Start animation
this.start();
}
//Unmount when animation has stopped
componentwillUnmount() {
this.stop();
this.mount.removeChild(this.renderer.domElement);
}
//Function to start animation
start = () => {
//Rotate Models
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);
}
};
//Function to stop animation
stop = () => {
cancelAnimationFrame(this.frameId);
};
//Animate models here
animate = () => {
//ReDraw scene with camera and scene object
if (this.cubeMesh) this.cubeMesh.rotation.y += 0.01;
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};
//Render the scene
renderScene = () => {
if (this.renderer) this.renderer.render(this.scene,this.camera);
};
render() {
return (
<div
style={{ width: '800px',height: '800px' }}
ref={(mount) => {
this.mount = mount;
}}
/>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter code here
?
解决方法
对于 CSG,您需要实体。这些管子是开着的。
我创建了一个使用圆柱体的示例(管子用于盖帽),因此您可以对其进行测试。
这些气缸是开放式的,因此它们的失效方式与您的管子相同。 https://codepen.io/flatworldstudio/pen/bGBjmrP
const geometry1 = new THREE.CylinderGeometry(0.1,0.1,0.5,20,1,true);
这些都已关闭,CSG 按预期工作。 https://codepen.io/flatworldstudio/pen/VwmBRoL
const geometry1 = new THREE.CylinderGeometry(0.1,false);
(我使用的是不同版本的 CSG,但它们似乎都建立在相同的代码上)