VertexData.ComputeNormals in right handed system

I would like to use a custom mesh in a useRIghtHandedSystem scene:

https://playground.babylonjs.com/#VKBJN#2215

I know the indices of the cube are consistent with outward facing normals when outwards means a counterclockwise winding order which is often the default in right handed systems.

So I was surprised to find a few things:

  • switching on useRightHandedSystem seems to change the default material sideOrientation from counterclockwise to clockwise. Why ?
  • setting it then explicitly to counterclockwise leads to correctly facing normals with VertexData.ComputeNormals(): good
  • but then only the diffuse lighting is correct, the specular highlight is completely off. ?.
  • the specular highlight is mostly fixed when using the option to ComputeNormals {useRightHandedSystem: true}. (It still wobbles).
  • the debug visualization of normals in the Inspector seems off (or the normals are still off).

Why is there an useRightHandedSystem option to ComputeNormals, in addition to the scene setting ?

Any insights or history much appreciated.

PS: Is there a way to automatically split vertices to get flat face normals ? I think convertToFlatShadedMesh() does something like that. What does it do ?

1 Like

If you build your cube geometry as in a right handed system, then you will have some special things to do to make it work in Babylon (which is fundamentally left handed at its core):

  1. add a root node to your cube with a transformation that transforms a right to a left handed system => only needed if you plan to set scene.useRightHandedSystem = false at some point. If you will always have scene.useRightHandedSystem = true you can skip this step
  2. override the sideOrientation property of material so that Babylon does not tamper with it (use mesh.overrideMaterialSideOrientation for that)

Regarding 2, Babylon is expecting that the geometry is described in a left-handed way. So, if you set scene.useRightHandedSystem = true;, the projection matrix will be changed in a way that will invert the winding: Babylon needs to flip the side orientation of the materials to account for this, and it does this automatically. To disable this feature, you must use the mesh.overrideMaterialSideOrientation property: if it is non-null, Babylon will use it in all circumstances, whatever the value of scene.useRightHandedSystem.

Regarding 1, you can do what the glTF loader does, as the geometry in the .gltf/.glb files is described in a right handed system way. This loader is doing this to handle the two points above:

  1. create a root node with rotation.y = 180° and scaling.z = -1
  2. set mesh.overrideMaterialSideOrientation = scene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;

So, you can do the same so that your code will work whatever the value of scene.useRightHandedSystem is:

What you were doing (mat.sideOrientation = BABYLON.Material.CounterClockWiseSideOrientation) is essentially ok if you don’t plan to change the value of scene.useRightHandedSystem afterwards, as it achieves the same thing than mesh.overrideMaterialSideOrientation = Material.CounterClockWiseSideOrientation.

Note that the positions and indices should be arrays of numbers, not strings => the bounding info computation fails when using array of strings. That’s why I have added some map((s) => parseFloat(s)) in the PG above.

the debug visualization of normals in the Inspector seems off (or the normals are still off).

This was because of the arrays not being arrays of numbers.

Yes, it’s what convertToFlatShadedMesh is for, it will make sure that all faces have 3 unique vertices, not shared by other faces. Then it will set the normal of each vertex as being the normal of the face. At render time you will get a flat shaded rendering because the vertices are not shared by faces anymore, so the normals are not blended.

2 Likes

Super helpful and very clear, thanks. Luckily, I think I can rely on scene.useRightHandedSystem = true in general. That will avoid most of the contortions. Understanding that Babylon fundamentally expects geometry to be described left-handed is key (but hard to explain in docs for right handedness).

Apologies for overlooking the float conversion. I should have seen that.

mesh.overrideMaterialSideOrientation should be very helpful since I want to deal with winding order on the mesh, not the material.

Any insights on the additional useRightHandedSystem: true option for ComputeNormals ? Should it default to true if scene.useRightHandedSystem = true ? (If back compat. would not prevent that).

In other words, what is the use case for the option to be true in left handed scenes, and false in right handed scenes ? There may have been strange situations which required such an option, or it may predate scene handedness.

It’s used by the AbstractMesh.createNormals method:

public createNormals(updatable: boolean): AbstractMesh {
    var positions = this.getVerticesData(VertexBuffer.PositionKind);
    var indices = this.getIndices();
    var normals: FloatArray;

    if (this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
        normals = <FloatArray>this.getVerticesData(VertexBuffer.NormalKind);
    } else {
        normals = [];
    }

    VertexData.ComputeNormals(positions, indices, normals, { useRightHandedSystem: this.getScene().useRightHandedSystem });
    this.setVerticesData(VertexBuffer.NormalKind, normals, updatable);
    return this;
}
1 Like

Ah, so it does default to the handedness of the scene when internally used. backgroundMaterial.ts uses the method and pbrBaseMaterial.ts . So it seems it is expected that the option correspond to the scene handedness. I think that could be mentioned in the documentation on creating custom meshes.

For completeness https://playground.babylonjs.com/#VKBJN#2218

shows how to use mesh.createNormals() instead of VertexData.computeNormals()

They are not exactly equivalent. I think I will prefer createNormals since it deals with the handedness.

2 Likes