user3160302
user3160302

Reputation: 237

How can I calculate normals of closed shape in three.js?

I am trying to write my own mesh importer for my own file format. In my file format there is no normal data. So I am trying to calculate normals of a close shape and apply those normals to mesh.

My solution is calculating a normal vector for every face of geometry and create a raycast from middle of these faces in the direction of normal vector. If that raycast hit something (another plane) it means this direction is inside. In that case I flip normal, If it does not hit something I leave it that way.

While I wrote a function with this logic, normals don't change at all.

        function calculateNormals(object){

            for (var i = 0; i < object.geometry.faces.length; i++) {
                var vertices= object.geometry.vertices;
                var face=object.geometry.faces[i];



                var a=vertices[face.a];
                var b=vertices[face.b];
                var c=vertices[face.c];

                console.log(face.a+" "+face.b+" "+face.c+" "+face.normal.z);
                console.log(face);
                console.log(face[4]);

                var edge0=new THREE.Vector3(0,0,0);
                edge0.subVectors(a,b);

                var edge1=new THREE.Vector3(0,0,0);
                edge1.subVectors(b,c);

                var planeNormal=new THREE.Vector3(0,0,0)
                planeNormal.crossVectors(edge0,edge1);

                // console.log(planeNormal);

                //Raycast from middle point towards plane nrmal direction
                //If it hits anything it means normal direction is wrong
                var midPoint=calculateMiddlePoint([a,b,c]);
                var raycaster = new THREE.Raycaster(midPoint,planeNormal);

                var intersects = raycaster.intersectObjects([object]);
                if(intersects.length==0){
                    console.log("Normal is true");
                    face.normal=planeNormal;
                }else{
                    console.log("Normal is wrong, you should flip normal direction, length: "+intersects.length);

                    console.log("Old face");
                    console.log(face.normal);

                    var newNormal=new THREE.Vector3(-1*planeNormal.x,-1*planeNormal.y,-1*planeNormal.z);
                    console.log(newNormal);
                    face.normal=newNormal;



                    console.log("new face");
                    console.log(face.normal);
                    console.log(face);
                }

                object.geometry.faces[i]=face;


                // console.log(object.geometry.faces);

            };


            return object;


        }

Upvotes: 1

Views: 3668

Answers (1)

TheJim01
TheJim01

Reputation: 8866

Matey makes a good point. The winding order is what determines the face normal, meaning which side is considered "front." Vertex normals are used for shading (with MeshPhongMaterial for example). If your vertex normals point in the opposite direction from your face normal, you'll end up with unintended results (anything from bad shading to a totally black face).

All that said, Geometry has helper functions for calculating normals.

Geometry.computeFaceNormals (based on winding order)

Geometry.computeFlatVertexNormals (sets a vertex normal to be the same as the associated face normal)

Geometry.computeVertexNormals (sets a vertex normal to the average of its surrounding face normals)

Once you've computed the normals, you could make a second pass to try and correct them, either by re-ordering the vertices (to correct the face normal), or by re-calculating the vertex normals yourself.

Upvotes: 1

Related Questions