我正在尝试使用ThreeCSG库,并尝试将球体或普通几何体替换为自定义Shape,在本例中是3js示例中的心形。
不幸的是,我在面对镜头的一侧得到了一个奇怪的结果。这是一个代码示例:
(function onLoad() {
var container, camera, scene, renderer;
var grey = 0xD3D3D3;
init();
animate();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting()
addRenderer();
addOrbitControls();
createHeartMesh();
createDieCutHandle();
// Creates a CSG cut on the cube based on the passed mesh
createCSGDiecutHandle();
}
function createDieCutHandle() {
var cubeGeo = new THREE.CubeGeometry(250, 250, 250);
var mesh = new THREE.Mesh(cubeGeo)
mesh.position.x = -500;
scene.add(mesh);
}
function createCSGDiecutHandle() {
var cubeGeo = new THREE.CubeGeometry(250, 250, 250);
var cubeMesh = new THREE.Mesh(cubeGeo);
var cubeBSP = new ThreeBSP(cubeMesh);
// Need geometry of the mesh to cut
var heartShape = getHeartShape();
var heartGeo = getHeartGeometry(heartShape);
var meshToCut = new THREE.Mesh( heartGeo );
meshToCut.scale.set(10,10,10);
meshToCut.position.z -= 80;
var meshToCutBsp = new ThreeBSP(meshToCut)
// var resultBsp = cubeBSP.union(meshToCutBsp);
var resultBsp = cubeBSP.subtract(meshToCutBsp);
// var resultBsp = cubeBSP.intersect(meshToCutBsp);
var resultMesh = resultBsp.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
resultMesh.geometry.computeVertexNormals();
scene.add(resultMesh);
}
function getHeartShape() {
var x = 5, y = 10;
var heartShape = new THREE.Shape();
heartShape.moveTo(x - 5, y - 5);
heartShape.bezierCurveTo(x - 5, y - 5, x - 4, y, x, y);
heartShape.bezierCurveTo(x + 6, y, x + 6, y - 7, x + 6, y - 7);
heartShape.bezierCurveTo(x + 6, y - 11, x + 3, y - 15.4, x - 5, y - 19);
heartShape.bezierCurveTo(x - 12, y - 15.4, x - 16, y - 11, x - 16, y - 7);
heartShape.bezierCurveTo(x - 16, y - 7, x - 16, y, x - 10, y);
heartShape.bezierCurveTo(x - 7, y, x - 5, y - 5, x - 5, y - 5);
return heartShape;
}
function getHeartGeometry(heartShape) {
var extrudeSettings = {
steps: 2,
amount: 16,
bevelEnabled: true,
bevelThickness: 5,
bevelSize: 1,
bevelSegments: 1
};
return new THREE.ExtrudeBufferGeometry( heartShape, extrudeSettings );
}
function createHeartMesh() {
var heartShape = getHeartShape();
var geometry = getHeartGeometry(heartShape);
var material = new THREE.MeshNormalMaterial();
var mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -500;
mesh.position.z = -80;
mesh.scale.set(10, 10, 10);
scene.add(mesh);
}
/**** Helper functions ****/
function computeBoundingBox(mesh) {
var box = new THREE.Box3().setFromObject(mesh);
var size = box.getSize();
return {
width: size.x,
height: size.y,
size: size
}
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(349.11334070460066, 405.44010726325604, 359.3111192889029);
scene.add(camera);
}
function addGridHelper() {
var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
planeGeometry.rotateX(-Math.PI / 2);
var planeMaterial = new THREE.ShadowMaterial({
opacity: 0.2
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.y = -200;
plane.receiveShadow = true;
scene.add(plane);
var helper = new THREE.GridHelper(2000, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function addLighting() {
addHemisphereLight();
}
function addSpotLighting() {
var light = new THREE.SpotLight(0xffffff, 1.5);
light.position.set(0, 1500, 200);
light.castShadow = true;
light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(70, 1, 200, 2000));
light.shadow.bias = -0.000222;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
scene.add(light);
}
function addAmbientLight() {
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
}
function addHemisphereLight() {
var hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(hemisphereLight);
}
function addRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
}
function addOrbitControls() {
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
})();
body {
background: transparent;
padding: 0;
margin: 0;
font-family: sans-serif;
}
#canvas {
margin: 10px auto;
width: 800px;
height: 350px;
margin-top: -44px;
}
<body>
<div id="container"></div>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/Wilt/ThreeCSG/develop/ThreeCSG.js"></script>
<script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
</body>
左边的立方体只是从立方体内部突出的挤压心脏几何体 - 它们没有连接,它们只是共享相同的位置。
右边的那个是左边的CSG版本,但是我试图从立方体中减去心脏。面对镜头的一面是错误的,因为网眼的那一面似乎在渗透,而如果你仔细观察,背面的一面则非常好。
导致此行为的原因是什么?我尝试添加属性side: THREE.DoubleSide
,但它没有解决问题。
我还查看了这个问题here并尝试在心脏网格上调用updateMatrix()
,但它没有任何影响。
由于
答案 0 :(得分:1)
ThreeCSG无法正确减去凹面网格。但是有一种解决方法,因为心脏或凸起的两半。
你可以从长方体中减去心脏的右半部分:
function getHeartShapeRight() {
var x = 5, y = 10;
var heartShape = new THREE.Shape();
heartShape.moveTo(x - 5, y - 5);
heartShape.bezierCurveTo(x - 5, y - 5, x - 4, y, x, y);
heartShape.bezierCurveTo(x + 6, y, x + 6, y - 7, x + 6, y - 7);
heartShape.bezierCurveTo(x + 6, y - 11, x + 3, y - 15.4, x - 5, y - 19);
return heartShape;
}
// right cuboid
var cubeGeoR = new THREE.CubeGeometry(125, 250, 250);
var cubeMeshR = new THREE.Mesh(cubeGeoR);
cubeMeshR.position.x += 62.5;
var cubeBSPR = new ThreeBSP(cubeMeshR);
// right part of the heart
var heartShapeR = getHeartShapeRight();
var heartGeoR = getHeartGeometry(heartShapeR);
var meshToCutR = new THREE.Mesh( heartGeoR );
meshToCutR.scale.set(10,10,10);
meshToCutR.position.z -= 80;
// right subtract
var meshToCutBspR = new ThreeBSP(meshToCutR);
var resultBspR = cubeBSPR.subtract(meshToCutBspR);
var resultMeshR = resultBspR.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
你可以从长方体中减去心脏的左半部分:
function getHeartShapeLeft() {
var x = 5, y = 10;
var heartShape = new THREE.Shape();
heartShape.moveTo(x - 5, y - 19);
heartShape.bezierCurveTo(x - 12, y - 15.4, x - 16, y - 11, x - 16, y - 7);
heartShape.bezierCurveTo(x - 16, y - 7, x - 16, y, x - 10, y);
heartShape.bezierCurveTo(x - 7, y, x - 5, y - 5, x - 5, y - 5);
return heartShape;
}
// left cuboid
var cubeGeoL = new THREE.CubeGeometry(125, 250, 250);
var cubeMeshL = new THREE.Mesh(cubeGeoL);
cubeMeshL.position.x -= 62.5;
var cubeBSPL = new ThreeBSP(cubeMeshL);
// left part of the heart
var heartShapeL = getHeartShapeLeft();
var heartGeoL = getHeartGeometry(heartShapeL);
var meshToCutL = new THREE.Mesh( heartGeoL );
meshToCutL.scale.set(10,10,10);
meshToCutL.position.z -= 80;
var meshToCutBspL = new ThreeBSP(meshToCutL);
// left subtract
var meshToCutBspL = new ThreeBSP(meshToCutL);
var resultBspL = cubeBSPL.subtract(meshToCutBspL);
var resultMeshL = resultBspL.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
最后,你可以创建两半的联合:
// union of left an right half
var unionBsp = resultBspL.union(resultBspR);
var unionMesh = unionBsp.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
scene.add(unionMesh);
请参阅代码段:
(function onLoad() {
var container, camera, scene, renderer;
var grey = 0xD3D3D3;
init();
animate();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting()
addRenderer();
addOrbitControls();
// Creates a CSG cut on the cube based on the passed mesh
createCSGDiecutHandle();
}
function createCSGDiecutHandle() {
// left cuboid
var cubeGeoL = new THREE.CubeGeometry(125, 250, 250);
var cubeMeshL = new THREE.Mesh(cubeGeoL);
cubeMeshL.position.x -= 62.5;
var cubeBSPL = new ThreeBSP(cubeMeshL);
// left part of the heart
var heartShapeL = getHeartShapeLeft();
var heartGeoL = getHeartGeometry(heartShapeL);
var meshToCutL = new THREE.Mesh( heartGeoL );
meshToCutL.scale.set(10,10,10);
meshToCutL.position.z -= 80;
var meshToCutBspL = new ThreeBSP(meshToCutL);
// left subtract
var meshToCutBspL = new ThreeBSP(meshToCutL);
var resultBspL = cubeBSPL.subtract(meshToCutBspL);
var resultMeshL = resultBspL.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
// right cuboid
var cubeGeoR = new THREE.CubeGeometry(125, 250, 250);
var cubeMeshR = new THREE.Mesh(cubeGeoR);
cubeMeshR.position.x += 62.5;
var cubeBSPR = new ThreeBSP(cubeMeshR);
// right part of the heart
var heartShapeR = getHeartShapeRight();
var heartGeoR = getHeartGeometry(heartShapeR);
var meshToCutR = new THREE.Mesh( heartGeoR );
meshToCutR.scale.set(10,10,10);
meshToCutR.position.z -= 80;
// right subtract
var meshToCutBspR = new ThreeBSP(meshToCutR);
var resultBspR = cubeBSPR.subtract(meshToCutBspR);
var resultMeshR = resultBspR.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
// union of left an right half
var unionBsp = resultBspL.union(resultBspR);
var unionMesh = unionBsp.toMesh(new THREE.MeshLambertMaterial({flatShading: true}));
scene.add(unionMesh);
}
function getHeartShapeRight() {
var x = 5, y = 10;
var heartShape = new THREE.Shape();
heartShape.moveTo(x - 5, y - 5);
heartShape.bezierCurveTo(x - 5, y - 5, x - 4, y, x, y);
heartShape.bezierCurveTo(x + 6, y, x + 6, y - 7, x + 6, y - 7);
heartShape.bezierCurveTo(x + 6, y - 11, x + 3, y - 15.4, x - 5, y - 19);
return heartShape;
}
function getHeartShapeLeft() {
var x = 5, y = 10;
var heartShape = new THREE.Shape();
heartShape.moveTo(x - 5, y - 19);
heartShape.bezierCurveTo(x - 12, y - 15.4, x - 16, y - 11, x - 16, y - 7);
heartShape.bezierCurveTo(x - 16, y - 7, x - 16, y, x - 10, y);
heartShape.bezierCurveTo(x - 7, y, x - 5, y - 5, x - 5, y - 5);
return heartShape;
}
function getHeartGeometry(heartShape) {
var extrudeSettings = {
steps: 2,
amount: 16,
bevelEnabled: true,
bevelThickness: 5,
bevelSize: 1,
bevelSegments: 1
};
return new THREE.ExtrudeBufferGeometry( heartShape, extrudeSettings );
}
/**** Helper functions ****/
function computeBoundingBox(mesh) {
var box = new THREE.Box3().setFromObject(mesh);
var size = box.getSize();
return {
width: size.x,
height: size.y,
size: size
}
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(349.11334070460066, 405.44010726325604, 359.3111192889029);
scene.add(camera);
}
function addGridHelper() {
var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
planeGeometry.rotateX(-Math.PI / 2);
var planeMaterial = new THREE.ShadowMaterial({
opacity: 0.2
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.y = -200;
plane.receiveShadow = true;
scene.add(plane);
var helper = new THREE.GridHelper(2000, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function addLighting() {
addHemisphereLight();
}
function addSpotLighting() {
var light = new THREE.SpotLight(0xffffff, 1.5);
light.position.set(0, 1500, 200);
light.castShadow = true;
light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(70, 1, 200, 2000));
light.shadow.bias = -0.000222;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
scene.add(light);
}
function addAmbientLight() {
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
}
function addHemisphereLight() {
var hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(hemisphereLight);
}
function addRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
window.onresize = resize;
}
function addOrbitControls() {
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
})();
&#13;
body {
background: transparent;
padding: 0;
margin: 0;
font-family: sans-serif;
}
#canvas {
margin: 10px auto;
width: 800px;
height: 350px;
margin-top: -44px;
}
&#13;
<div id="container"></div>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/Wilt/ThreeCSG/develop/ThreeCSG.js"></script>
<script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
&#13;