Skip to content

Clarify tangent space for flat normals #2480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
lexaknyazev opened this issue Mar 17, 2025 · 6 comments
Open

Clarify tangent space for flat normals #2480

lexaknyazev opened this issue Mar 17, 2025 · 6 comments
Milestone

Comments

@lexaknyazev
Copy link
Member

The spec has these statements:

When tangents are not specified, client implementations SHOULD calculate tangents using default MikkTSpace algorithms with the specified vertex positions, normals, and texture coordinates associated with the normal texture.

When normals are not specified, client implementations MUST calculate flat normals and the provided tangents (if present) MUST be ignored.

Consider a mesh that has neither normals, nor tangents and uses a material with a normal texture. The second quoted statement requires generating flat normals but it does not mention anything about generating tangents for them, which would be needed for that mesh.

One could say that implementations should generate tangents using MikkTSpace with the generated flat normals but the second statement does not mention that and the first statement says "specified normals", which semantically excludes "generated normals".

/cc @bghgary @emackey @javagl

@lexaknyazev lexaknyazev added this to the 2.0.x milestone Mar 17, 2025
@javagl
Copy link
Contributor

javagl commented Mar 17, 2025

I cannot say much about the conditions and the actual computation (i.e. I have not yet used or implemented MikkTSpace).

One could refer to some inherent ambiguity of natural language, and claim that the statement is

the specified vertex positions, normals, and texture coordinates

and not

the specified vertex positions, the specificed normals, and the specified texture coordinates

But besides that weaseling:

How realistic is the case that someone wants to provide tangents but no normals, and rely on the computation of the flat normals? (The point is that this is made impossible by the "MUST be ignored" part, of which I'm currently not sure what its intention is...)

@lexaknyazev
Copy link
Member Author

How realistic is the case that someone wants to provide tangents but no normals, and rely on the computation of the flat normals? (The point is that this is made impossible by the "MUST be ignored" part, of which I'm currently not sure what its intention is...)

Generating flat normals may require vertex duplication and it's unclear how to duplicate tangents in that case.

@emackey
Copy link
Member

emackey commented Apr 7, 2025

Generating flat normals may require vertex duplication and it's unclear how to duplicate tangents in that case.

Once one duplicates vertices (as needed for flat shading) and generates new flat normals, could one use MikkTSpace on the duplicated verts and generated normals to compute new tangents? It sounds doable but I'm not familiar with the internals of that algorithm.

I do know that texture coordinates would need to be present on the mesh, of course. If there's flat shading (no normals) and also no texture coordinates, then there's no TBN basis at all, and you can't have normal maps or anisotropy or anything that needs TBN.

@lexaknyazev
Copy link
Member Author

Once one duplicates vertices (as needed for flat shading) and generates new flat normals, could one use MikkTSpace on the duplicated verts and generated normals to compute new tangents?

Yes. That's why asset-supplied tangents are ignored if normals are generated at runtime.

The spec needs to be clarified because the current language could be interpreted either way:

  • When normals are missing, implementations must duplicate vertices and generate flat normals. If applicable, they should generate MikkTSpace tangents using those flat normals.
  • When normals are missing, implementations must duplicate vertices and generate flat normals. Tangent space remains undefined for such meshes and thus normal / anisotropy textures cannot be used.

@emackey
Copy link
Member

emackey commented Apr 7, 2025

In general I wouldn't expect missing / flat normals to be a common occurrence on surfaces that feature normal maps and anisotropy. But I would expect screen-space approximated tangents to continue to work regardless if normals were supplied or generated. So if such a surface did exist, it would probably continue to work in renderers that use screen-space tangent generation.

So, capability-wise, I think I would lean towards the first of your two bullet points. But I can think of a caveat: textureless anisotropy doesn't require any texture coordinates, so long as tangents are supplied. If one does not supply tangents and uses anisotropy, then texture coordinates must be supplied to provide the TBN basis.

For general spec cleanliness and simplicity, your second bullet point sounds better. If you're using normal maps and expecting tangents and stuff, surely you should at least be supplying some basic normals for the mesh. Supplying a normal map without any normals sounds foolish. We could probably cut out some edge cases by disallowing it officially.

@bghgary
Copy link
Contributor

bghgary commented Apr 8, 2025

When normals are missing, implementations must duplicate vertices and generate flat normals

I don't know how we ended up with this statement. Flat normals can be calculated in the fragment shader using derivatives (e.g. here in Babylon.js) which doesn't require duplicating vertices. I don't think we should require the runtime to duplicate vertices as this will be a slow operation.

IIRC, the original intention is that assets that require tangents should have already processed its geometry with MikkTSpace algorithms which should in turn do whatever is necessary to the vertices (e.g., duplication) beforehand. Removing the tangents afterwards can save space such that the runtime can regenerate them. I don't think we considered removing normals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants