4
votes

I'm trying establish why I can't smooth shade geometry loaded with OBJLoader.

var loader = new THREE.OBJLoader(manager);
loader.load('/manmodel/js/man.obj', function (object, materials) {
    console.log(object);
    console.log(materials);
    man = object;
    man.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.geometry.computeFaceNormals();
            child.geometry.computeVertexNormals( true );
            child.material = new THREE.MeshPhongMaterial({
                color: 'white',
                shading: THREE.SmoothShading // <-----------  THIS IS THE PROBLEM
            });
        }
    });
    man.position.y = -10;
    scene.add(man);
});

This is the result:

apparently Flat Shading

If I remove the line computeFaceNormals() the model renders the same. If I remove computeVertexNormals( true ) the object renders with no lighting (black) - so I know this is doing something.

If I change the color attribute of the MeshPhongMaterial in this code, the color changes, so I also know that this is working.

I have tried to use the vertex and normal helpers to establish what the problem is, but they fail because with BufferGeometry the faces and verticices are not stored as arrays.

I have also tried modifying the man.obj file to change the 's' values from 'off' to 1. This did nothing.

As I will be loading several .obj files for different human figures, generated in Blender, and each is currently around 2MB, I prefer to do the shading on the browser than to 'bake' it into the file if this would increase file size.

THE QUESTION(s): Am I missing something here? OR, Is there a way to load the .obj file as standard Geometry, compute the normals, apply the shading, and then save as BufferGeometry?

ps. I may also need the normals for ray tracing further down the line.

2

2 Answers

8
votes

The most recent version of ObjLoader parses the .obj to a BufferGeometry for performance reasons. If you look back through the history on GitHub you can find the previous version which parses to Geometry:

https://github.com/mrdoob/three.js/blob/a321ba05f02ae3a1586a4060a53f5ad63b90729b/examples/js/loaders/OBJLoader.js

Load your .obj using this and you can then manipulate the Geometry until you have it as you need it, then create a new BufferGeometry and load the Geometry into it using BufferGeometry.fromGeometry(geometry) in order to get the performance. I have this working nicely.

8
votes

If we want to use the most recent loader (r73), we can also convert the BufferGeometry to a Geometry, and then back!

The only caveat is that I had to merge the vertices before computing the vertex normals. I'm guessing that converting from the buffers messes with the triangles, so we gotta merge them before anything.

 var geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
 geometry.computeFaceNormals();
 geometry.mergeVertices();
 geometry.computeVertexNormals();
 child.geometry = new THREE.BufferGeometry().fromGeometry( geometry );