[SOLVED] How can we smooth model loaded with OBJLoader (i.e. not flat shading)?

I’m loading a model, then once the model is loaded, I’m not able to make it have smooth shading. In my OBJLoader callback I’ve tried:

                model.traverse(n => {
                    if (n.material) {
                        n.material.flatShading = false
                        n.material.wireframe = false
                        n.material.needsUpdate = true
                
                        n.geometry.computeFaceNormals();
                        n.geometry.computeVertexNormals(true);
                    }
                })

but no luck, the model is still flat shaded. Is there something else I should do?

OBJLoader returns geometries of type BufferGeometry. Calling .computeFaceNormals() on a BufferGeometry has not effect. The method only exists because of backwards compatibility.

Besides, .computeVertexNormals() has no parameters. Hence, calling the method with true as first parameter has no effect.

In general, it’s best to export your model with smooth normals from a DCC tool.

1 Like

@prisoner849 @Mugen87 Ah, okay, there were a bunch of Google results showing n.geometry.computeVertexNormals()/n.geometry.computeVertexNormals(true), but I guess both are outdated.

I just want to download OBJ files I may find on the net and use them, with no DCC tool.

So how do we do it?

It does only work with an indexed BufferGeometry and if duplicate vertices are actually merged/eliminated. three.js does not provide a method that transforms a non-indexed to an indexed geometry right now.

If you have an indexed geometry, you can then call BufferGeometry.computeVertexNormals().

BTW: Material.flatShading is set to false by default. So three.js always performs smooth shading. You can force flat shading by set the respective material property of if you provide flat vertex normals.

1 Like

So basically if I call BufferGeometry.computeVertexNormals() and don’t see a difference, then I can assume it’s not indexed? So in this case I’d have no choice but to use a DCC to correct it?

What about the old Geometry stuff? Can I opt to use that somehow, and if so would that work (disregarding performance loss)? Or did the older Geometry classes have the same problem?

It’s best to check if geometry.index is null. If so, using Blender or a similar tool is the easiest solution.

No, since Geometry does have a method .mergeVertices(). I think sooner or later BufferGeometry needs a similar functionality. For now try this:

var tempGeometry = new THREE.Geometry().fromBufferGeometry( geometry );
tempGeometry.mergeVertices();
tempGeometry.computeVertexNormals();
geometry = new THREE.BufferGeometry().fromGeometry( tempGeometry );
2 Likes

Thanks, that works great!

Here it is put into a helper function:

setSmoothGeometry(modelLoadedFromOBJLoader)

function setSmoothGeometry(obj) {
    obj.traverse(node => {
        if ('geometry' in node) {
            const tempGeometry = new THREE.Geometry().fromBufferGeometry( node.geometry );
            tempGeometry.mergeVertices();
            tempGeometry.computeVertexNormals();
            node.geometry = new THREE.BufferGeometry().fromGeometry( tempGeometry );
        }
    })
}
1 Like

For v125 and later see How to smooth an OBJ with threejs? - #11 by firstlaser

1 Like

I have the same problem. I tried to use your function but it returns this error: “Uncaught TypeError: THREE.Geometry is not a constructor”.
Can you help me? How can I solve it? Thanks!!

You probably use a newer release which does not include THREE.Geometry in the core anymore. Read the following topic for more information: THREE.Geometry will be removed from core with r125

Use the approach mentioned: in How to smooth an OBJ with threejs? - #12 by Laubeee

1 Like