Three.js Mesh With Multiple Materials Disappears? - three.js

I'm trying to load a pretty simple model with two materials (applied per-face) in Three.js exported from Blender. The issue I'm running into is that with multiple materials applied via the code below the mesh disappears entirely, but I can easily use materials[0] or materials[1] in place of materials and see a solid object. My export settings from blender along with what it should look like are below and the json of the model is visible online here.

I tried your JSON file and it displayed just like your image. Here's the code I used (three.js r83):
var mesh = new THREE.Object3D();
var jsonLoader = new THREE.JSONLoader();
jsonLoader.load('/js/models/cube.json',
function (geometry, materials) {
mesh = new THREE.Mesh(geometry,
new THREE.MultiMaterial(materials));
scene.add(mesh);
}
);
http://www.threejsworld.com/tutorials/working-with-materials-in-threejs

Related

how do i convert all materials in a loaded gltf from mesh basic to mesh phong?

I am loading a GLTF model into threejs. All the materials used on the object (and there a quite a few of them), use a mesh basic material, and we want them to be a material that can be affected by lights. Is there a way to convert all materials from a basic material to one that can receive lights, like Phong (including their existing properties?).
I can currently add a new material as follows:
glb.scene.traverse(child => {
if (child.isMesh) {
//child.material = new THREE.MeshPhongMaterial({ flatShading: true });
}
});
but everything just looks a solid gray color and doesn't contain any of the properties of the mesh basic material it replaced.
The default material in glTF is usually mapped to MeshStandardMaterial in three.js — your model must have the "unlit" glTF extension (KHR_materials_unlit) enabled if it's creating MeshBasicMaterial. Short of changing that in a modeling tool like Blender, which might be easiest, you can also convert it in three.js...
model.traverse((child) => {
if ( ! child.isMesh ) return;
var prevMaterial = child.material;
child.material = new MeshPhongMaterial();
MeshBasicMaterial.prototype.copy.call( child.material, prevMaterial );
});
Note that the reverse does not necessarily work — calling a complex material's copy() method on a simpler material (like MeshBasicMaterial) will cause it to look for properties that do not exist.

How do I access/change the material on an imported gltf-object in three.js individually?

I exported an object from Blender to a gltf-file and imported it into my three.js project. (Including materials, ive got 2 in total)
let loader = new THREE.GLTFLoader();
loader.load('Objects/MyBox.gltf', (gltf) => {
let box = gltf.scene;
box.traverse((child) => {
if (child.isMesh) child.material = boxMaterial; // a material i created in the code earlier
});
scene.add(box);
});
This code causes my imported Object (which is a folded box) to have the same material (boxMaterial) on the outside and on the inside.
But as i created it in bleder ( inside of the box has an inside material, the outside has an outside material) i want it to be in my three.js project. What I want is = the boxMaterial(created in three.js) shall only be on the outside-Material(created in blender).
My question is -> how do I tell/access the specific imported material of my imported object to have a certain material in three.js?
In easier words: My "child.material" contains my needed materials from blender.(2 in total) But how do I access only the first one?
I think I'm looking for something like box.children[0].material.. or so. The index 0 is my outside material.
Thanks in advance!

Aframe Load lightmap after loading GLTF - lightmap not showing

I'm trying to add a lightmap to some mesh after loading them from a GLTF file.
All my objects have 2UV channel.
I'm waiting 'object3dset' and here is my code :
const mesh = this.el.getObject3D('mesh');
var textureLoader = new THREE.TextureLoader();
textureLoader.load("lightmap.png", function(lmap){
mesh.traverse((node) => {
if (!node.isMesh) return;
node.material.lightMap = lmap;
lmap.flipY = node.material.map.flipY; //needed to flip the texture
node.material.needsUpdate = true;
});
});
If I replace the material with a new one and set the lightmap, it's working.
But I want to find a way without recreating all materials.
The lightmap was loaded, but not easy to see.
By default metalness from Khronos Blender Exporter converted in threejs after loading GLTF result to a level 1.0. With this configuration, the lightmap is hard to see and is not corresponding to what we see in Blender.
I hope my mistake can help someone else losing too much time.

Applying a material to an imported model [three.js]

Part of a project that I'm working on requires that I pull in an array of base64 images, then pull in imported models via THREE.JSONLoader(). If I keep these two types of assets apart, they both load without issue. However, if I try to apply the image as a material to the model, I get an error.
WARNING: Output of vertex shader 'vWorldPosition' not read by fragment shader
I realize that much of the time when you load in geometry, you need to run geometry.computeTangets(), but that isn't working. I thought perhaps there's another computation that might need to run prior to creating a mesh with the geometry and the material. Here's what I have:
var material = new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture(data.images[0]),
shininess: 4.0,
});
var loader = new THREE.JSONLoader();
loader.load("app/museum/geometry.json", function (geometry) {
geometry.computeTangents();
mesh = new THREE.Mesh(geometry, material);
mesh.scale.set(100, 100, 100);
mesh.position.y = 150;
mesh.position.x = 0;
scene.add(mesh);
});
I assume there's an additional setting or method that needs to be called, or perhaps there are attributes in the geometry.json that I need to set manually?

Loading multiple objects in Three.js from Blender

I have a quite complex shape (dressed girl) that in Blender is broken down into different objects and it's loaded into Three.js with the JSON loader (with a little hack I made, that uses zipped files instead of just JSON files, as there are a lot of vertices).
As I want to change dynamically the style of the dress from a Web page, I was wondering how I can show/hide different pieces (e.g. sleeves) in the scene.
I tried traversing the THREE.Mesh, but there are no children.
When I export from Blender with the JSON exporter, I don't see anything referring to the names of the objects in the JSON. Is the structure lost?
If you are using meshes containing lots of vertices, I would advise you to use the openCTM webGL loader instead of zip hacking. Here is a link to the loader : http://threejs.org/examples/webgl_loader_ctm.html
This mesh compression tool uses LZMA compression method and can reduce the size of your files by 93%...
Concerning the JSONLoader, using an array might help:
var meshes = [];
...
var loader = new THREE.JSONLoader();
var onGeometry = function(geom)
{
var mesh = new THREE.SceneUtils.createMultiMaterialObject(geom, [material]);
meshes.push( mesh );
...
scene.add(mesh);
};
loader.load("yourfile.js", onGeometry);
Hope this helps
It is possible to load an entire scene with several meshes from a json file exported from Blender and handle them separately!
You can follow the complete process of exporting a entire scene from Blender and the right way of handling the exported meshes on my answer of this post.
So, you can get the different meshes and manipulate them separately using the getObjectByName method. But it is important to know that the loaded object isn't a Geometry anymore. It is labeled with the Scene type by now and it must be handled in a different way.
The loading code must look like this one:
loader = new THREE.JSONLoader();
loader.load( "obj/Books.json", function ( loadedObj ) {
var surface = loadedObj.getObjectByName("Surface");
var outline = loadedObj.getObjectByName("Outline");
var mask = loadedObj.getObjectByName("Mask");
scene.add(surface);
scene.add(outline);
scene.add(mask);
} );
Besides, you can handle the multiple materials of single mesh using THREE.MeshFaceMaterial like in the following code:
var mat1 = new THREE.MeshLambertMaterial( { map: texture1 } );
var mat2 = new THREE.MeshLambertMaterial( { map: texture2 } );
var materials = [mat1, mat2];
var faceMat = new THREE.MeshFaceMaterial(materials);
mesh = new THREE.Mesh( geometry, faceMat );
scene.add( mesh );
Hope this helps :)

Resources