Three.js Calculating Vertex Normals - three.js

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

Related

Convert MeshStandardMaterial to MeshBasicMaterial for glb model

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

Threejs cover single image on each box face without repeat

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.

Three.js FBXLoader2, change color of single Mesh (in Group or detached)

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 );

explodeModifier not working for imported model in three.js

I am trying to do a face explode effect as seen in this example: http://threejs.org/examples/#webgl_modifier_tessellation
However the example uses THREE.TextGeometry and I am using an imported model using
THREE.JSONLoader();
loader.load( "models/animated/Brain-New_2154_ft.js", function ( geometry ) {
var material = new THREE.MeshLambertMaterial( {
color: 0x6249a3,
vertexColors: THREE.FaceColors,
morphTargets: true,
overdraw: 0.5
} );
How can I use an imported model for this effect? Do I have to convert the model to something before it's faces, vertices can be affected with the explodeModifier and TessellateModifier libraries? thanks!
You need to use the THREE.ExplodeModifier() which is not included in the core, you have to explicitly import it in (see line 140 in your example).
The ExplodeModifier just loops through the vertices array in your geometry so it should work with any model type that can be successfully imported.
var geometry = new THREE.PlaneGeometry(10,10,10,10);
var explodeModifier = new THREE.ExplodeModifier();
explodeModifier.modify( geometry );
This gives you an array of faces you can manipulate the same way you do the vertices

Computing Bounding Sphere after static .OBJ loading with Three.js

I got the following code for loading a simple, non-animated .OBJ with a .MTL into Three.js. It's very simple indeed and works well, but when I add the line with computeBoundingSphere(), it fails with a "TypeError: object.computeBoundingSphere is not a function" :
var callbackIrali1 = function ( event ) {
var object = event.content;
object.computeBoundingSphere();
scene.add( object );
};
var loaderIrali1 = new THREE.OBJMTLLoader();
loaderIrali1.addEventListener( 'load', callbackIrali1);
loaderIrali1.load( 'models/obj/irali/irali.obj', 'models/obj/irali/irali.mtl' );
The issue is, I can't find what type of object might be this event.content returned by the callback, and so I can't find how to apply the computeBoundingSphere() function to it.
Finally found it out by myself :
- The object returned by the Loader is an Object3D,
- Thus, it has 2 children : mesh and materials,
- So you have to find the first child, then extract its geometry, and then compute the bounding sphere.
Which gives the following line :
object.children[0].geometry.computeBoundingSphere();

Resources