11

I´m trying to load some STL files using Three.js. The models are loaded correctly, but there are too many triangles that I would like to merge/smooth.

I had successfully applied smooth loading terrains in other 3D formats, but I can´t do it with the BufferGeometry that results from loading an STL file with the STLLoader.

enter image description here _

var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
                object.computeBoundingBox();
                object.computeBoundingSphere();
                object.computeFaceNormals();
                object.computeVertexNormals();
                object.normalizeNormals();
                object.center();

                // Apply smooth
                var modifier = new THREE.SubdivisionModifier( 1);
                var smooth = smooth = object.clone();
                smooth.mergeVertices();
                smooth.computeFaceNormals();
                smooth.computeVertexNormals();
                modifier.modify( smooth );
                scene.add( smooth );
});

This is what I tried, it throws an error: Uncaught TypeError: smooth.mergeVertices is not a function

If I comment the "mergeVertices()" line, what I get is a different error: Uncaught TypeError: Cannot read property 'length' of undefined in SubdivisionsModifier, line 156.

It seems that the sample codes I´m trying are outdated (this is happenning a lot recently due to the massive changes in the Three.JS library). Or maybe I´m forgetting something. The fact is that the vertices seems to be null..?

Thanks in advance!

273K
  • 29,503
  • 10
  • 41
  • 64
spacorum
  • 495
  • 6
  • 16

4 Answers4

9

It seems I was looking in the wrong direction: smoothing the triangles has nothing to do with the SubdivisionsModifier... What I needed was easier than that, just compute the vertex BEFORE applying the material, so it can use SmoothShading instead of FlatShading (did I got it right?).

The problem here was that the BufferGeometry returned by the STLLoader has not calculated vertices/vertex, so I had to do it manually. After that, apply mergeVertices() just before computeVertexNormals() and voilà! The triangles dissappear and everything is smooth:

var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {                
                object.computeBoundingBox();
                object.computeVertexNormals();
                object.center();
                ///////////////////////////////////////////////////////////////

                var attrib = object.getAttribute('position');
                if(attrib === undefined) {
                    throw new Error('a given BufferGeometry object must have a position attribute.');
                }
                var positions = attrib.array;
                var vertices = [];
                for(var i = 0, n = positions.length; i < n; i += 3) {
                    var x = positions[i];
                    var y = positions[i + 1];
                    var z = positions[i + 2];
                    vertices.push(new THREE.Vector3(x, y, z));
                }
                var faces = [];
                for(var i = 0, n = vertices.length; i < n; i += 3) {
                    faces.push(new THREE.Face3(i, i + 1, i + 2));
                }

                var geometry = new THREE.Geometry();
                geometry.vertices = vertices;
                geometry.faces = faces;
                geometry.computeFaceNormals();              
                geometry.mergeVertices()
                geometry.computeVertexNormals();

                ///////////////////////////////////////////////////////////////
                var mesh = new THREE.Mesh(geometry, material);

                scene.add( mesh );
});

enter image description here

spacorum
  • 495
  • 6
  • 16
  • Please someone correct me on this: so if I want to simply make the mesh smooth just visually, I need to mergeVertices() + computeVertexNormals() before applying the material (better performance). On the other hand, the SubdivisionsModifiers allows to modify the mesh itself, making more and more triangles to smooth phisically (low performance on large models). Is this concept right? – spacorum Feb 01 '16 at 18:13
  • I think you need to replace "object." by "object.geometry" (times 4 locations). (With that edit) your solution solved my problem of trying to get smooth shading from an OBJ source file, so many thanks :-). – steveOw Dec 03 '16 at 01:52
4

Than, you can convert it back to BufferGeometry, because it's more GPU/CPU efficient for more complex models:

var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
var buffer_g = new THREE.BufferGeometry();
buffer_g.fromGeometry(geometry);
var mesh = new THREE.Mesh(buffer_g, material);
scene.add( mesh )
Zydnar
  • 1,472
  • 18
  • 27
1

Happened this issue for me while loading an obj file. If you have a 3d software like 3dsmax:

  1. Open the obj file,
  2. Go to polygons selection mode and select all polygons.
  3. Under the Surface properties panel, click 'Auto Smooth' button.
  4. Export the model back to obj format

Now you won't have to call the functions geometry.mergeVertices() and geometry.computeVertexNormals();. Just load the obj and add to the scene, mesh will be smooth.

EDIT: My obj files had meshphongmaterial by default and on changing the shading property to value 2 the mesh became smooth.

child.material.shading = 2
coderSree
  • 329
  • 1
  • 12
0

STL does not support vertex index. That is reason it has duplicated vertex of all triangles. Each vertex has its normal as triangle normal. As a result, at same position( multiple very closed vertices), there is multiple normal value. This leads to non-smooth surface of geometry when using Normal for lighting calculation.

Karoshi
  • 29
  • 1
  • 7