Mesh.convertToFlatShadedMesh also UnIndexes. Why?

Why does Mesh.convertToFlatShadedMesh() (line 3213) call this._convertToUnIndexMesh(true) (line 3118) instead of this._getFlattenedNormals()?

It looks like this._getFlattenedNormals() would (properly, IMHO) set the normals according to the position array referenced by the indices array (lines 3094…). To do so, Mesh.convertToFlatShadedMesh() needs to do something like

const normals = this._getFlattenedNormals(this.getPositionData(),this.getIndices())
this.setVerticesData(VertexBuffer.NormalKind, normals)

Calling this._convertToUnIndexMesh(true) does indeed flatten the normals (by calling this._getFlattenedNormals()), but also expands the position data to the size of the indices data prior to doing so.

On another note, why does this._convertToUnIndexMesh(true) NOT remove the (now redundant) indices data? In fact, it explicitly sets the index array to an array where index[i] = i (lines 3185…). Unless needed by the GPU, it seems it could return a generated array on the fly if need for returning from Mesh.getIndices() when this.isUnIndexed is true.

I found this when looking through the resulting VerticesData after the various mesh/normal modification functions:

  • convertToFlatShadedMesh
  • forceSharedVertices
  • convertToUnIndexedMesh
  • createNormals
  • computeNormals
  • optimizeIndices
  • BABYLON.VertexData.ComputeNormals

As well as

  • simplify()
  • BABYLON.QuadraticErrorSimplification()

Edit: when I implement this, the StandardMaterial already applied to the mesh turns black. Does StandardMaterial ignore Indices?

Edit2: setVerticesData seems to enforce that normals data is limited to the length of positions data (in mesh.vertexData.ts in multiple functions that merge or validate). I can’t get around this, including trying to copy into a new VerticesData object and applyToMesh() (which also funnels through merge or validate). I’ve reviewed WebGL and it does support indexed data, it just seemes not fully comprehended/supported in BabylonJS at the moment. I’m stuck.

@Devs will confirm (or not :grin:) but to me it does make sense to convert to UnIndexed.
It’s a matter of understanding how smooth and flat shading are done using OpenGL. Let’s take the example of 2 triangles :


In a smooth shading mode, you only need 4 vertices A,B,C,D (red) and their 4 normals (blue)


Then you define 2 indexed triangles : ABC ([0,1,2]) and CBD ([2,1,3]). You only send 4 vertices, and 4 normals (using indices for faces) and the GPU will interpolate the normal along the surface of each triangle, resulting on smooth shading.


In flat shading now, even keeping these 4 vertices, actually 6 normals are needed :


By doing so, you ensure that the GPU interpolating the normals along the surface of each triangle will keep a constant normal on each triangle, resulting in a “sharp” edge in the middle.
To do so, what is sent to OpenGL is actually 3 vertices A,B,C along with 3 normals, as well as 3 vertices C,B,D along with 3 others normals. Total vertices is 6 (not 4 anymore). So using indexing (which is usefull at reducing vertice count) is not usefull anymore…


At least, that’s my understanding of the thing :smile:

++
Tricotou

2 Likes

Spot on @Tricotou !!!

1 Like

Thank you @Tricotou. I had understood that much already, but thank you for confirming. My thought was that normals could be associated with the indexed positions rather than the positions array directly. Googling that reveals that a lot of people are under the same (mis-)impression that I was. And various forum posts corroborate that. “GPU hardware doesn’t work like that.”

There may be a slight indication that later versions of OpenGL or WebGL may allow binding of normals data to indexed positions, but I’m still reading up on that. Some posts seem to indicate maybe it’s possible, but might require an additional draw call. I’m not convinced that that is any “better.”

If I find anything substantive and relevant, I’ll post again. For now, you have my appreciation, and concurrence.

Pleasure :slight_smile:

Yep. Problem is that indexed geometry is per face based. Taking back the first triangle of my example, the face indices would be [0,1,2] for all datas (vertices 3D, vertices 2D (uv), and normals). Which is not possible for normals in flat shaded mesh, since each vertice as multiple normals, depending on the face it’s being drawn from.

In my example only 2 normals per vertex for B and C, but if you consider more complex topoly such as a triangle fan, you can end up with any number (10, 15 or whatever) of normals per vertex. And in that case you cannot build a consistent index list such as [0,1,2] which works for both vertices and normals.

Thank you so much! I think I’m getting it. The thing I didn’t fully comprehend was that the offset within the normals data corresponds to an offset within the position data. And the mesh triangles can be organized by three successive rows within position data directly or by three rows within an index buffer each pointing to an offset into the position buffer.

Is that right?