Reputation: 23
I want to merge two mesh with different materials.
This is what i 'v done, but render incorrect, it just like render one side of the object.
Here is my code on codesandbox:
https://codesandbox.io/s/summer-dawn-5nq05?fontsize=14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script>
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
let scene = new THREE.Scene();
scene.background = new THREE.Color(0xcce0ff);
// Camera
let camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
1000
);
camera.position.set(30, 0, 100);
let controls = new THREE.OrbitControls(camera);
for (let i = 0; i < 1; i++) {
// Geometry
let cube1Geometry = new THREE.PlaneGeometry(10, 10, 10);
let cube2Geometry = new THREE.PlaneGeometry(10, 15, 5);
// Material
let cube1Material = new THREE.MeshStandardMaterial({
color: 0x7c3c3c,
roughness: 1
});
let cube2Material = new THREE.MeshStandardMaterial({
color: 0x01b8ba,
roughness: 0.1
});
// Mesh
let cube1 = new THREE.Mesh(cube1Geometry, cube1Material);
let cube2 = new THREE.Mesh(cube2Geometry, cube2Material);
// Combine
let singleGeometry = combine([cube1, cube2]);
let single = new THREE.Mesh(singleGeometry, [
cube1Material,
cube2Material
]);
// scene.add(cube1);
// scene.add(cube2);
scene.add(single);
single.position.set(
Math.random() * 5,
Math.random() * 10,
Math.random() * 20
);
}
function combine(meshes) {
let mergeGeometry = new THREE.Geometry();
for (let i = 0; i < meshes.length; i++) {
meshes[i].updateMatrix();
// update materialIndex
for (let j = 0; j < meshes[i].geometry.faces.length; j++) {
meshes[i].geometry.faces[j].materialIndex = 0;
}
mergeGeometry.merge(meshes[i].geometry, meshes[i].matrix, i);
}
return mergeGeometry;
}
// LIGHT
let light1 = new THREE.AmbientLight(0x666666);
scene.add(light1);
let light = new THREE.SpotLight(0xdfebff, 1);
light.position.set(50, 200, 100);
scene.add(light);
requestAnimationFrame(function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
});
</script>
</body>
</html>
Upvotes: 0
Views: 144
Reputation: 1735
ANSWER OUTDATED
Threejs' API has changed and no longer support the old Geometry
interface, now you need to deal directly with BufferGeometries and their attributes. You can find more merging utilities on DOCS: BufferGeometryUtils.
This behavior happens because BoxGeometry
(source) stipulates a different MaterialIndex
for each of its faces. This, in turn, controls which material will be used for that face, when utilizing an array of Materials.
To make sure that every geometry only uses the specified material in the array, you need to reset MaterialIndex
to 0
for each face of the geometry. That way, geometry.merge()
will correctly adjust each geometry to use the correct material.
I've modified your combine
function to do this on the fly. I'm not sure if there's a better way of resetting MaterialIndex
, at least I couldn't find any.
function combine(meshes) {
let mergeGeometry = new THREE.Geometry();
for (let i = 0; i < meshes.length; i++) {
meshes[i].updateMatrix();
// update materialIndex
for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
meshes[i].geometry.faces[j].materialIndex = 0;
}
mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
}
return mergeGeometry;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<style>
body {
margin: 0;
position: fixed;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
</style>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script>
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
let scene = new THREE.Scene();
scene.background = new THREE.Color(0xcce0ff);
// Camera
let camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
1000
);
camera.position.set(30, 0, 50);
let controls = new THREE.OrbitControls(camera);
// Geometry
let cube1Geometry = new THREE.BoxGeometry(10, 10, 10);
let cube2Geometry = new THREE.BoxGeometry(10, 15, 5);
// Material
let cube1Material = new THREE.MeshStandardMaterial({
color: 0x7c3c3c,
roughness: 1
});
let cube2Material = new THREE.MeshStandardMaterial({
color: 0x01b8ba,
roughness: 0.1
});
// Mesh
let cube1 = new THREE.Mesh(cube1Geometry, cube1Material);
let cube2 = new THREE.Mesh(cube2Geometry, cube2Material);
// Combine
let singleGeometry = combine([cube1, cube2]);
let single = new THREE.Mesh(singleGeometry, [
cube1Material,
cube2Material
]);
scene.add(single);
function combine(meshes) {
let mergeGeometry = new THREE.Geometry();
for (let i = 0; i < meshes.length; i++) {
meshes[i].updateMatrix();
// update materialIndex
for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
meshes[i].geometry.faces[j].materialIndex = 0;
}
mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
}
return mergeGeometry;
}
// LIGHT
let light1 = new THREE.AmbientLight(0x666666);
scene.add(light1);
let light = new THREE.SpotLight(0xdfebff, 1);
light.position.set(50, 200, 100);
scene.add(light);
requestAnimationFrame(function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
});
</script>
</body>
</html>
Upvotes: 1