I am loading a .glb model into Three.js. All the materials get turned into MeshStandardMaterial. How can I convert them to MeshBasicMaterial?
The goal is to get the exact colors that I used in Blender without having to use any lights in the Three.js scene.
Attempts
(1) I have tried the following code, based on https://stackoverflow.com/a/61717988/795016, but the problem is that this approach appears not to work when going from a more complex to a less complex material.
// For each child in the group
loadedGroup.traverse((child) => {
// If this child has a mesh standard material
if (
child instanceof THREE.Mesh
&& child.material instanceof THREE.MeshStandardMaterial
) {
var prevMaterial = child.material;
child.material = new THREE.MeshBasicMaterial();
THREE.MeshStandardMaterial.prototype.copy.call(child.material, prevMaterial);
console.log("child.material", child.material);
}
});
(2) I have also tried to use the gltf-transform tool to change the materials in the model to be unlit, but Three.js still shows them as MeshStandardMaterial.
gltf-transform unlit model-in.glb model-out.glb
Related
Suppose i have one gltf model like given here: https://sketchfab.com/3d-models/box-d919737a5a5b464f809d12f7e1fad78f. and cover image like given below:
I have to cover this image on full box not each layer separately.
Below is my code to apply texture on object.
var loader = new GLTFLoader().setPath( 'models/gltf/box/' );
loader.load( 'scene.gltf', function ( gltf ) {
var model = gltf.scene;
model.traverse ( ( o ) => {
if ( o.isMesh ) {
o.material.map = texture;
}
} );
scene.add( model );
render();
} );
Results of code is like given in below image but i have to wrap single image in continues manner to all side.
Thanks in advance for any kind of help.
That's the default texture-mapping behavior of a standard Three.js cube. If you need to modify the way the texture is mapped, then you'll have to perform your own UV Mapping and import the geometry into Three.js so they follow the pattern that you desire. For example, if you want to create a 6-sided die, you'll have to edit your UVs so they follow this pattern:
I recommend you use Blender to do this because it's free, easy, and it has a built-in GLTF exporter that you can use with Three.js.
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.
I have a .fbx model which load via FBXLoader2.
When I add the model (which include 3 meshes) to the scene, it is added as a Group. Now I try to change the color of a single Mesh in the Group, but all 3 meshes are getting the color. Then i thought they might be "linked" because of the Group. So i detached them (SceneUtils.detach).
Now I have all the meshes in the scene. Still, when I change the color of a single mesh, all three of them get the color. When I console.log the mesh (in the group or detached) it shows me the correct mesh.
var loader = new THREE.FBXLoader( manager );
loader.load( 'somemodel.fbx', function( object ) {
model = object;
var modelLength = model.children.length;
for (i=0;i<modelLength;i++) {
THREE.SceneUtils.detach(model.children[0], model, scene);
}
scene.children[0].material.emissive.setHex( 0xff0000 );
}
When i try to change position or scale the mesh, it works fine.
Anybody had the problem before?
Just clone the material and replace it with original once.
scene.children[0].material = scene.children[0].material.clone();
scene.children[0].material.emissive.setHex( 0xff0000 );
I need help for getting UV Textures to be rendered correctly in three.js. I've created a model with repeating tile texture in Blender. The tile texture is applied using UV mapping, and it looks like this if it is being rendered correctly:
Render image using Blender
.However, when it is loaded using JSONLoader by three.js, the tiles are just stretched to fill each polygon, giving weird result like this:Screenshot of render using three.js
. I've tried setting THREE.RepeatWrapping in my code but nothing changed:
bodyLoader = new THREE.JSONLoader();
bodyLoader.load('./starofthesea_threejs.json', function(geometry, materials) {
mainBodyMaterials = new THREE.MeshFaceMaterial(materials);
console.log(materials);
mainBodyMaterials.wrapS = THREE.RepeatWrapping;
mainBodyMaterials.wrapT = THREE.RepeatWrapping;
mainBodyMaterials.needsUpdate = true;
mainBody = new THREE.Mesh(geometry, mainBodyMaterials);
mainBody.traverse ( function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
mainBody.scale.x = mainBody.scale.y = mainBody.scale.z = 1;
geometry.computeBoundingBox();
geometry.computeFaceNormals();
geometry.computeFlatVertexNormals();
scene.add(mainBody);
});
Is there anything wrong in my code, or workaround to get it rendered correctly? All help is deeply appreciated.
Finally I've figured out the problem by myself, where both the Blender model and JS are misconfigured. RepeatWrapping should be applied to texture but not material. I need to study the structure of THREE.MeshFaceMaterial to find the handle for the underlying textures. I need to traverse through the materials to find out all materials with image textures:
mainBodyMaterials = new THREE.MeshFaceMaterial(materials);
for(prop in mainBodyMaterials.materials) {
if(mainBodyMaterials.materials[prop].map != null) {
mainBodyMaterials.materials[prop].map.wrapS = THREE.RepeatWrapping;
mainBodyMaterials.materials[prop].map.wrapT = THREE.RepeatWrapping;
tex.push(mainBodyMaterials.materials[prop].map.clone());
tex[tex_sequence].needsUpdate = true;
tex_sequence++;
}
}
After applying wrapS and wrapT to textures correctly, one of the tile materials get rendered correctly, but 'Texture marked for update but image is undefined' error keep throwing out. I need to clone the texture to get rid of the error, according to another question: Three.js r72 - Texture marked for update but image is undefined?
As there are several materials with repeating tiles, I need to create a global array at the beginning of the JS routine, and push the modified textures one by one when looping through the materials:
var tex = new Array();
var tex_sequence = 0;
After fixing the JS calls, one of the textures are still not working. I forgot that only ONE UV slot is allowed for three.js. I need to unwrap the UVs again under the same UV slot in Blender. Everything works like a charm, and I hope my painful experience can help those who are getting mad by similar problems.
I'm experimenting with one of the examples, namely the webgl_loader_obj.html to load an .obj file exported from blender into three.js
This works and displays the model exactly as expected.
Now i was looking at the use of material.shading = THREE.SmoothShading.
For this to work, the model needs to have vertex normals.
the exported file from blender has no normals defined.
So i looked at using computeVertexNormals to calculate the required normals.
however this doesn't appear to be doing anything, the resulting view is of the unsmoothed model.
further to this, as a test, i exported the same model, with normals.
loading it straight in, it appeared smoothed.
If i then did computeFaceNormals() & computeVertexNormals() it would result in the unsmoothed mesh again.
var loader = new THREE.OBJLoader( manager );
loader.load( 'test.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = new THREE.MeshLambertMaterial( { color: 0xff6600 });
child.geometry.computeFaceNormals();
child.geometry.computeVertexNormals();
child.material.shading = THREE.SmoothShading;
}
} );
Geometry.computeVertexNormals() "smooths" the vertex-normals by computing each vertex-normal to be the average of the face-normals of all faces that share that vertex.
If each face of your geometry has unique vertices (i.e., no vertices are shared with a neighboring face), then computeVertexNormals() will result in each face's vertex-normals being the same as the face-normal, and the mesh shading will appear faceted.
three.js r.71