How to get the normal of a face/triangle? (BufferGeometry)

I am migrating my geometry to buffergeometry, but I have came to something preventing me to make progress. I can’t find a way to retrieve the normal of a triangle/face. I need to determine the direction a polygon is pointing… How can I accomplish this?

With geometry I was able to do

geometry.faces[f].normal

but I’m lost now with buffergeometry. How do I calculate the direction of a triangle?

1 Like
			var posAttribute = this.face.geometry.attributes.position;
			var indexAttribute = this.face.geometry.index;
			
			for(var f=0; f<2; f++){	
                    var triangle = new THREE.Triangle(	new THREE.Vector3(posAttribute.getX(indexAttribute[0+(f*3)]),posAttribute.getY(indexAttribute[0+(f*3)]),posAttribute.getZ(indexAttribute[0+(f*3)])),
														new THREE.Vector3(posAttribute.getX(indexAttribute[1+(f*3)]),posAttribute.getY(indexAttribute[1+(f*3)]),posAttribute.getZ(indexAttribute[1+(f*3)])),
														new THREE.Vector3(posAttribute.getX(indexAttribute[2+(f*3)]),posAttribute.getY(indexAttribute[2+(f*3)]),posAttribute.getZ(indexAttribute[2+(f*3)])), );
					triangle.getNormal( copytoavector3 );
                    //otherstuff
		    }

I think I can do something like this… Let me know if there’s a better way.

Sometimes a geometry already has pre-computed normals, you can find them at: geometry.attributes.normal (they are per vertex, of course).

Or, if you create a custom geometry, you can make your life easier, calling .computeVertexNormals().

I need the face normal, not vertex normal.

Okay, there’s something, that can be simplified/optimized:

let pos = this.face.geometry.attributes.position;
let idx = this.face.geometry.index;

let tri = new THREE.Triangle(); // for re-use
let a = new THREE.Vector3(), 
    b = new THREE.Vector3(), 
    c = new THREE.Vector3(); // for re-use

for( let f = 0; f < 2; f++ ){
    let idxBase = f * 3;
    a.fromBufferAttribute( pos, idx.getX( idxBase + 0 ) );
    b.fromBufferAttribute( pos, idx.getX( idxBase + 1 ) );
    c.fromBufferAttribute( pos, idx.getX( idxBase + 2 ) );
    tri.set( a, b, c );
    tri.getNormal( copytoavector3 );
    //otherstuff
}
4 Likes

Thanks, that looks great! I didn’t know that you could set a vector from a buffer. Cool!

Hi guys, I was trying to re-write the threejs-slice-geometry - npm library to be able to slice with v125 and above of ThreeJs.

I was stack at this point:

geometry.faces.forEach(function(face, faceIndex) {
}

How I can translate that to the bufferGeometry? I have tried the next code, but i got undefined “idx”

let pos = this.sourceGeometry.geometry.attributes.position;

    let idx = this.sourceGeometry.geometry.index;

    let tri = new THREE.Triangle(); 

    let a = new THREE.Vector3(),

        b = new THREE.Vector3(),

        c = new THREE.Vector3();

    for (let f = 0; f < 2; f++) {

        let idxBase = f * 3;

        a.fromBufferAttribute(pos, idx.getX(idxBase + 0));

        b.fromBufferAttribute(pos, idx.getX(idxBase + 1));

        c.fromBufferAttribute(pos, idx.getX(idxBase + 2));

        tri.set(a, b, c);

    }

Really appreciate your help!

Thanks!

It means, that your model has a non-indexed buffer geometry, as it has no .index property. They call it “triangle soup”, when faces defined with triplets of consequential vertices. So, there’s no need to use .index, work with geometry.attributes.position;.

2 Likes

Triangle has a setFromAttributeAndIndices method to simplify things:

const tri = new THREE.Triangle(); // for re-use
const indices = new THREE.Vector3(); // for re-use
const outNormal = new THREE.Vector3(); // this is the output normal you need

indices.fromArray(geometry.index.array, faceIndex * 3);
tri.setFromAttributeAndIndices(geometry.attributes.position,
    indices.x,
    indices.y,
    indices.z);
tri.getNormal(outNormal);
3 Likes

That looks a lot simpler, thanks!

1 Like

Can @prominent or anyone post the working code? I’m trying to achiveve the same question but none of the code above is working. I want to draw normals of geometry faces’ center of gravity…

I could be able to draw a triangle, and could be able to draw a face normal from center of gravity but i want to do the same for the BoxGeomety faces.

What exactly isn’t working in your code? Maybe you can post the code for us.
It should be fairly straightforward- you’d just have to cycle through the faces in the box.

Hi @prominent, actually i’m stuck with the same thing with you. I made some research and found out that geometry.faces is deprecated because THREE.js is now using BufferGeometries now. I couldnt figure out how to iterate a triange on geometry’s faces. I tried erasta’s solution at the top but its giving some errors like, “faceIndex is not defined”, “geometry.index is possibly null” (I’m using typescript btw)

try something like this…


const tri = new THREE.Triangle(); // for re-use
const indices = new THREE.Vector3(); // for re-use
const outNormal = new THREE.Vector3(); // this is the output normal you need

for(let f=0; f<geometry.index.count; f++){
  indices.fromArray(geometry.index.array, f * 3);
  tri.setFromAttributeAndIndices(geometry.attributes.position,
      indices.x,
      indices.y,
      indices.z);
  tri.getNormal(outNormal);

  //do something with the normal for this face
}

You’ll need to iterate through the faces. That’s what the faceIndex variable was for- to specify which index in the array. The attributes put the data into a 1 dimensional array, so that’s why it is multiplied by 3, because each position takes 3 values (x, y, and z).
You can read more about it here in the bufferAttributes section: three.js docs

Thank you @prominent for helping out. I could be able to create something working.
Out normals are looking to midpoints of square faces which is weird. i added a video


Here is my working code:

const boxMesh: THREE.Mesh = new THREE.Mesh(boxGeometry, boxMaterial)
scene.add(boxMesh)
boxMesh.position.x = 3

const tri = new THREE.Triangle(); // for re-use
const indices = new THREE.Vector3(); // for re-use
const outNormal = new THREE.Vector3(); // this is the output normal you need
const midPoint = new THREE.Vector3()

function getTriangles() {
    for(let f = 0; f < boxGeometry.index!.count; f++){
        
        indices.fromArray(boxGeometry.index!.array, f * 3)
        tri.setFromAttributeAndIndices(boxGeometry.attributes.position,indices.x,indices.y,indices.z)

        console.log(tri.getNormal(outNormal), " << OutNormal")
        console.log(tri.getMidpoint(midPoint), " << Midpoint")

        if(isNaN(midPoint.x) || isNaN(midPoint.y) || isNaN(midPoint.z)) return

        const lineMaterial = new THREE.LineBasicMaterial( { color: 0x00ff00 } )
        const linePoints = []
        linePoints.push( tri.getMidpoint(midPoint) )
        linePoints.push( tri.getNormal(outNormal) )
        const lineGeometry = new THREE.BufferGeometry().setFromPoints( linePoints )
        const line = new THREE.Line( lineGeometry, lineMaterial )
        boxMesh.add(line)
    }
        
}

getTriangles()

Your outNormal is a normalised vector3 meaning for example your top two out normals are (0,1,0) your line geometry is therefore going from your midPoint which is correct, to 0,1,0 in world space. Try making your second point in your line geometry outNormal + midPoint using vector3.add()

@Lawrence3DPK thanks!

linePoints.push( tri.getNormal(outNormal).add(midPoint) )

this code worked. i learned! :slight_smile:

1 Like