Tangent node in X3D - michaliskambi/x3d-tests GitHub Wiki

This is DONE in X3D 4.1

X3D 4.1 (draft for now) contains everything we need, following our proposal. Excellent! :)

See Tangent node in X3D 4.1 (draft) spec.

Proposition

We propose adding Tangent node to X3D:

Tangent : X3DGeometricPropertyNode {
  MFVec4f    [in,out]      vector      []
}

Along with a field to specify it:

X3DComposedGeometryNode {
  ...
  SFNode     [in,out]      tangent     NULL      
}

Note: It is always optional.

Note 2: No need for tangentPerVertex, tangentIndex etc. The Tangent assignment can follow normal vectors.

Prose for spec

Each element in the vector array provides a unit XYZ vector defining a tangent direction on the surface.

The 4th component of each vector, W, must be -1.0 or 1.0 and indicates the handedness of the tangent base (see the bitangent equation below). All vertexes of the same triangle must have the same W value for their tangent vectors.

Tangent vectors, if provided using this node, determine the tangent space in which the normalmaps (specified in the normalTexture fields of various material nodes) are specified. This is the space in which bump mapping algorithms operate. At each point of the surface, the tangent space is defined by:

  1. The tangent vector determines local (in tangent space) direction of X.
  2. The bitangent vector determines local (in tangent space) direction of Y.
  3. The normal vector determines local (in tangent space) direction of Z.

These 3 vector values are determined as follows:

  1. The value of normal is provided using the Normal node or automatically calculated following the specification of geometry nodes.

  2. The value of tangent is provided using this node, Tangent. The order and count of values specified by this node matches exactly the order and count of values as would be given by the Normal node.

    If the tangent vectors are not specified for a given mesh, the implementation should calculate them (if necessary, to apply normalTexture) for example using the MikkTSpace algorithm. Authors using normalTexture are encouraged to not rely on a particular algorithm used by the X3D browser to auto-calculate the tangent vectors, and instead provide explicit Tangent values to achieve the same rendering results as the 3D software used for creating normalmaps.

  3. The bitangent value mentioned above is always automatically calculated like this:

    bitangent = crossProduct(normal, tangent.xyz) * tangent.w

Current State

As of now, both X_ITE and Castle Game Engine have extensions to define Tangent node.

So we have invented the "Tangent" node independently in 2 implementations and, predictably, they are unfortunately similar-but-not-exactly the same. Links where you can notice a difference:

Also glTF format has them. See https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview , information about "TANGENT".

Note from Michalis: We will change Castle Game Engine's Tangent node in the future, to match better both X_ITE and glTF, thus we will have matching definitions with X_ITE. X_ITE approach is better, Tangent.vector should be 4D, just like glTF.

Why this is useful

To dispel some incorrect statements:

  • The tangent vectors are not a replacement for knowing normal vectors (nor is the other way around).

  • And just because the model provides normal vectors -- doesn't mean that tangent vectors are already precisely 100% determined.

    There are multiple similar algorithms to determine tangent vectors. The normal vector alone doesn't yet define the tangent -> because in a 3D space, if you have a normal vector -> you still have infinite possible values for a vector "orthogonal to normal". So tangent vectors are auto-calculated (when not explicitly provided) to match the normal vectors, but also to match the texture coordinates used for normal maps. There are edge-cases when it is just not obvious what should be the "perfect tangent" so various implementations (MikkTSpace is just one example) can do different things.

Ultimately, the "perfect tangent value" is the one that was used to bake the normalmaps. If the authoring tool (like Blender) that you used to make normalmaps used a bit different algorithm to calculate tangents than your renderer -> then the rendering of normalmaps will be slightly "off".

There's a nice screenshot in https://gamedev.stackexchange.com/questions/146855/how-do-you-compute-the-tangent-space-vectors-with-normals-given-in-the-mesh , scroll to "There are multiple ways to generate tangent spaces for a mesh, and not all of them agree on the result.". The screenshot there shows subtle problems that result from having authoring tool and renderer calculate a bit different tangent vectors.

So the perfect thing to do, from a renderer, is to not calculate the tangent vectors, instead take the tangent vectors as provided by the authoring tool. That exactly why glTF has tangents, and why X_ITE and Castle Game Engine support them too -- we don't just discard tangent values from glTF (only to auto-calculate them), we prefer to take tangent values recorded in the 3D model, because this makes really good rendering.

⚠️ **GitHub.com Fallback** ⚠️