This follows onto this question I asked a few days ago which turned out like the following:
(function onLoad() {
var canvasElement;
var width, height;
var scene, camera;
var renderer;
var controls;
var pivot;
var bagMesh;
var planeMesh;
const objLoader = new THREE.OBJLoader2();
const fileLoader = new THREE.FileLoader();
const textureLoader = new THREE.TextureLoader();
init();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting();
addRenderer();
addOrbitControls();
loadCubeObj();
// Logic
var update = function() {};
// Draw scene
var render = function() {
renderer.render(scene, camera);
};
// Run game logic (update, render, repeat)
var gameLoop = function() {
requestAnimationFrame(gameLoop);
update();
render();
};
gameLoop();
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xd3d3d3);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(3, 3, 3);
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();
scene.add(axis);
}
// *********** Lighting settings **********************
function addLighting() {
var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
scene.add(light);
}
//************** End of materials ***************
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 addPivot() {
var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
var cubeMat = new THREE.MeshBasicMaterial();
pivot = new THREE.Mesh(cubeGeo, cubeMat);
bagMesh.position.x -= 15;
bagMesh.position.z -= 55;
pivot.add(bagMesh);
pivot.add(handle);
scene.add(pivot);
}
function loadCubeObj() {
loadObjWithMtl('LetterBox',
'https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/models/cube/letter-block.obj',
'https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/models/cube/letter-block.mtl')
.then(function(mesh) {
mesh.traverse(function(child) {
if (child instanceof THREE.Mesh) {
var materials = [
new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/img/cube-diffuse-texture.png')
})
];
child.material = materials[0];
}
});
scene.add(mesh);
});
}
function loadObjWithMtl(modelName, modelUrl, mtlUrl, modificationFunc) {
return new Promise(function(resolve, reject) {
var callbackOnLoad = function(event) {
var mesh = event.detail.loaderRootNode;
if (modificationFunc) {
modificationFunc(mesh);
resolve(mesh);
} else {
resolve(mesh);
}
};
var onLoadMtl = function(materials) {
objLoader.setModelName(modelName);
objLoader.setMaterials(materials);
objLoader.getLogger().setDebug(true);
objLoader.load(modelUrl, callbackOnLoad, null, null, null, false);
};
objLoader.loadMtl(mtlUrl, null, onLoadMtl);
});
}
})();
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>
<img src="https://github.com/Katana24/threejs-experimentation/blob/master/img/cube-diffuse-texture.png">
<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://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>
As you can see I'm adding a single material with a texture onto the cube - great. I have several questions regarding this.
- How do I apply a different material to each of the outside sides? When building the model in Blender do I need to unwrap each side individually and assign a seperate material for each?
- Why does only a single material work?
- How do I apply a different material to each side, including the inside?
I'll ask a different SO question about number three as I've made a another unwrapped cube (I duplicated the sides and flipped the normals and applied different materials to the inside and outside (using Nodes) for this.), unless someone can answer it here.
I tried the following for 1:
mesh.traverse( function (child) {
if( child instanceof THREE.Mesh ) {
var materials = [
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/1.jpg')}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/2.jpg')}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/3.jpg')}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/4.jpg')}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/5.jpg')}),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load('img/6.jpg')})
];
child.material = materials;
}
});
_this.scene.add(mesh);
But nothing appears at all. I also tried assigning each material the THREE.DoubleSide
property but it had the same result.
EDIT
To answer my own questions:
To place a different material on each side I needed to (in Blender):
- unwrap the cube by marking the seams
- export each individual face as a "UV Layout"
- open the face in GIMP and fill with a colour to differentiate each face
- go back to Blender, choose a side and create a new material
- set the renderer type to "Cycles Renderer"
- create a new material and call it "front" for the front side
- select "Assign" to set it to the front
- drag open the "Node Editor" screen and click "Use Nodes"
- add a "Image Texture" node
- open the "front" image we created in GIMP
- finally in the 3D view set the view to "Texture" to see it reflected
- repeat for all sides
This resulted in this:
(function onLoad() {
var canvasElement;
var width, height;
var scene, camera;
var renderer;
var controls;
var pivot;
var bagMesh;
var planeMesh;
const objLoader = new THREE.OBJLoader2();
const fileLoader = new THREE.FileLoader();
const textureLoader = new THREE.TextureLoader();
init();
function init() {
container = document.getElementById('container');
initScene();
addGridHelper();
addCamera();
addLighting();
addRenderer();
addOrbitControls();
loadCubeObj();
// Logic
var update = function() {};
// Draw scene
var render = function() {
renderer.render(scene, camera);
};
// Run game logic (update, render, repeat)
var gameLoop = function() {
requestAnimationFrame(gameLoop);
update();
render();
};
gameLoop();
}
/**** Basic Scene Setup ****/
function initScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0xd3d3d3);
var axis = new THREE.AxesHelper();
scene.add(axis);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(3, 3, 3);
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();
scene.add(axis);
}
// *********** Lighting settings **********************
function addLighting() {
var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
scene.add(light);
}
//************** End of materials ***************
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 addPivot() {
var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
var cubeMat = new THREE.MeshBasicMaterial();
pivot = new THREE.Mesh(cubeGeo, cubeMat);
bagMesh.position.x -= 15;
bagMesh.position.z -= 55;
pivot.add(bagMesh);
pivot.add(handle);
scene.add(pivot);
}
function loadCubeObj() {
loadObjWithMtl('LetterBox',
'https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/models/cube/cube-1.obj',
'https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/models/cube/cube-1.mtl')
.then(function(mesh) {
mesh.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://raw.githubusercontent.com/Katana24/threejs-experimentation/master/models/cube/imgs/cube-1-all.png'), side: THREE.DoubleSide
});
}
});
scene.add(mesh);
});
}
function loadObjWithMtl(modelName, modelUrl, mtlUrl, modificationFunc) {
return new Promise(function(resolve, reject) {
var callbackOnLoad = function(event) {
var mesh = event.detail.loaderRootNode;
if (modificationFunc) {
modificationFunc(mesh);
resolve(mesh);
} else {
resolve(mesh);
}
};
var onLoadMtl = function(materials) {
objLoader.setModelName(modelName);
objLoader.setMaterials(materials);
objLoader.getLogger().setDebug(true);
objLoader.load(modelUrl, callbackOnLoad, null, null, null, false);
};
objLoader.loadMtl(mtlUrl, null, onLoadMtl);
});
}
})();
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>
<img src="https://github.com/Katana24/threejs-experimentation/blob/master/img/cube-diffuse-texture.png">
<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://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>
- A single material will work only if your exported model defines a single material in its construction. In my first code snippet the object had a single material exported, whereas in the second there are 5 materials that each describe a single position for that side which allows each to be changed.
3.To apply a different material to each side including the inside is the same process as described in 1 but you have to duplicate all the sides and flip the normals before assigning materials.
EDIT 2
In my latest code snippet above, if I change from the image that I exported from GIMP that covers everything and just load some random images they don't appear on the cube correctly:
How do I map the texture correctly to appear on each side without part of the image moving off to one side or the other?
Thanks
THREE.BufferGeometry()
and how it works, for example, forTHREE.BoxBufferGeometry()
– prisoner849