Indexed BufferGeometry front and back side assignment - normals or order of indices?

Hey guys,

I am trying to understand what controls which side of a BufferedGeometry polygon is visible and which isn’t (i.e. which side is considered ‘front’ and which one is ‘back’). Please consider the following simple indexed BufferGeometry that creates a simple square made of 2 triangles.

        const geometry = new THREE.BufferGeometry();
        const vertices = [
            { pos: [-1, -1, 1], norm: [0, 0, 1], uv: [0, 0], },
            { pos: [1, -1, 1], norm: [0, 0, 1], uv: [1, 0], },
            { pos: [-1, 1, 1], norm: [0, 0, 1], uv: [0, 1], },
            { pos: [1, 1, 1], norm: [0, 0, 1], uv: [1, 1], },
        ];

        const positionNumComponents = 3;
        const normalNumComponents = 3;
        const uvNumComponents = 2;

        const positions = new Float32Array(vertices.length * positionNumComponents);
        const normals = new Float32Array(vertices.length * normalNumComponents);
        const uvs = new Float32Array(vertices.length * uvNumComponents);


        let posIdx = 0;
        let nrmIdx = 0;
        let uvIdx = 0;
        for (const vertex of vertices) {
            positions.set(vertex.pos, posIdx);
            normals.set(vertex.norm, nrmIdx);
            uvs.set(vertex.uv, uvIdx);
            posIdx += positionNumComponents;
            nrmIdx += normalNumComponents;
            uvIdx += uvNumComponents;
        }

        geometry.setAttribute('position', new THREE.BufferAttribute(positions, positionNumComponents));
        geometry.setAttribute('normal', new THREE.BufferAttribute(normals, normalNumComponents));
        geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, uvNumComponents));


        geometry.setIndex([
            0, 1, 2,  // first triangle
            2, 1, 3   // second triangle
        ]);

Now, it was my understanding that the vertex normals are used to interpolate the face normals and those face normals are then used when drawing the polygons on screen to determine which side should be drawn. So I expected that if I flip the normals from (0,0,1) to (0.0,-1), the polygon would face the opposite direction and I will not see it (since I have the side set to THREE.FrontSide) . Turns out that is not the case and I can remove the normals attribute altogether without any visible effect or error.

Then I tried flipping the vertex indices and I realized that if I created the second triangle counter-clockwise like so

        geometry.setIndex([
            0, 1, 2,  // first triangle
            2, 3, 1   // instead of 2,1,3
        ]);

this will actually flip the face so that the camera would now be facing the back side of the second triangle.

Could someone please tell me if changing the vertex index order is what should be used to control which polygon side is “front” and which side is “back” or is there a different/recommended way to achieve this?

Thanks in advance for the help!

Front/back face culling is determined by the vertex order, yes. The normals come into play later when the triangle is shaded. There’s also material.side available.

In this example CircleDynamicallyFormable ( from the Collection of examples from discourse.threejs.org ) I had made a mistake, and since I had used side: THREE.DoubleSide, I had not noticed it. Since then I make sure to test only front and back. The note about the error is still in the code:

line 103

    const setFace =  ( ) => {
    
        faceIndices[ idxCount     ] = a;
        //faceIndices[ idxCount + 1 ] = b;
        //faceIndices[ idxCount + 2 ] = c;
        faceIndices[ idxCount + 1 ] = c; // swapped here c and b
        faceIndices[ idxCount + 2 ] = b; // otherwise, the geometry is backsided
        idxCount += 3;
        
    }

You can test the wrong variant. :slightly_smiling_face:

Thanks for the reply! And do you know what logic/math is used to determine why triangle with vertex indices [2,1,3] is facing towards the negative Z-axis and [2,3,1] is facing towards the positive Z-axis?

With 3D geometries, it’s all about the view of them. Let’s assume we have a cube. If I look at one side from the outside, the order of the indices must be counterclockwise. Then the front side is outward and the back side is inward.

UPDATE: Take a look at these simple examples.

BufferGeometryNormals

BufferGeometryIndexed

Thanks for your replies as well! In this case I have a custom BufferGeometry which happens to be a single square plane that is made up of 2 triangles which looks like this:

but if I change the indices order of the second triangle from 2, 1, 3 to 2, 3, 1 and that change will flip the face direction so one half of the square looks toward the camera and the other half looks in the opposite direction

So my question is if anybody knows what is the math/logic that decides that this particular order should flip the face direction.

Front-faces have counter-clockwise winding order. If you’re looking at the triangle from the side where 2, 1, 3 are in CCW order, you are viewing from the front. You could also calculate that direction using Triangle.getNormal.

See update previous post. Simple examples.

Here you can see the numbering of the official geometries.
NumberingHelperExamples

The official source code PlaneGeometry about it three.js/PlaneGeometry.js at 8921bfaee413e809cb2d27f0aec917408d690a0f · mrdoob/three.js · GitHub

Excerpt

	for ( let iy = 0; iy < gridY; iy ++ ) {

		for ( let ix = 0; ix < gridX; ix ++ ) {

			const a = ix + gridX1 * iy;
			const b = ix + gridX1 * ( iy + 1 );
			const c = ( ix + 1 ) + gridX1 * ( iy + 1 );
			const d = ( ix + 1 ) + gridX1 * iy;

			indices.push( a, b, d );
			indices.push( b, c, d );

		}

	}

Which way the polygon is facing has nothing to do with vertex index order.

It’s all about vertex relative position and the winding direction [ CCW is positive ].

WebGL uses xyz, right hand rule.
X runs from left to right.
Y runs from bottom to top.
Z runs from far to near.

Thanks for the help guys!