Home Technical Talk

Need clarification: Tangents and Binormals, Tangent Space, Tangent Space per Fragment.....

fattkid
polycounter lvl 15
Offline / Send Message
fattkid polycounter lvl 15

"Tangents and Binormals, Tangent Space, Tangent Space per Fragment"

Hey folks - curious if anyone could offer up some explanations of what those things exactly are, how they might be related, and how they should be considered in one's workflow etc.

I'm pretty up to speed on normal baking etc. I understand the idea of a synced baking workflow, etc. 

I've recently switched to baking in Painter from years of dear sweet Xnormal, so I'm trying to get my process dialed in.

When I am exporting .FBX from Maya, I have the option of exporting the "Tangents and Binormals" with the FBX. What exactly are the Tangents and Binormals? And is the "Tangents" the same as a "Tangent Space" that one would bake in? Or is there some difference, and maybe they are related and work together - As in - Mikkt tangent space is the math used to compute the tangents on the exported FBX model? Or, are the tangents being exported with FBX the actual tangent space itself, that would in turn override the default tangent space in whatever the baking app is?
That make sense?

Also, in Painter, it has an option to "Compute tangent space per fragment". What does that mean? How is that related to the Tangents being exported with the FBX, and the Tangent Space (Mikkt) being used for baking or display?

So yeah, if anybody could provide me with some info or links or whatever, that would be super cool and most appreciated. (Or any other info that you think might be helpful or relevant to figuring this all out and dialing in my process.)

Thanks.

-Trevor




Replies

  • JedTheKrampus
    Options
    Offline / Send Message
    JedTheKrampus polycounter lvl 8
    Tangents are vectors in object space that are usually perpendicular to the normals. Binormals (which are also sometimes called bitangents) are usually perpendicular to both the tangents and the normals. In all common tangent spaces used today, tangent vectors correspond to pointing to the right on your UV map in 2D space. Bitangent vectors correspond to pointing up or down on your UV map in 2D space. In 3ds Max, Cryengine, Source 2, and UE4, the bitangents point down in UV space, and in Unity, Blender, Maya, Modo, XSI, and Toolbag (unless you set Toolbag to use 3ds Max tangents) the bitangents point up in UV space. The renderer uses these three vectors to transform your normal map from tangent space to world space so that it can be used for shading, and if the mesh deforms, it transforms these three vectors along with the vertices so that the tangent space, and therefore the shading remains consistent during deformation.

    When you export tangents and binormals to your FBX from Maya, you are including those vectors as Maya computed them so that your renderer can use the same vectors. But, since you're baking in Substance Painter and (I assume) going to UE4 or Unity which both support MikkTSpace variants, there's usually no need for the Maya tangents since the baker and the renderer both know how to compute the same tangent space given the same object and UVs. If you're freelancing, you should ask your client whether you should bake in Maya tangent space or MikkTSpace and export tangents accordingly. (I'm assuming here that Substance Painter can use the exported tangents and binormals if you exported them from Maya. If it can't, then you would need to bake normal maps in Maya and export the tangents and binormals to your renderer if you needed to bake maps in Maya tangent space. I haven't baked with Substance Painter so I'm not confident one way or the other.)

    Some renderers pass all three vectors to the GPU as per-vertex data, or vertex attributes. Cryengine and Unity do this. However, some other renderers pass just the normal and the tangent vectors to the GPU as per-vertex data, and then in the pixel shader they use the interpolated value of the normal and tangent vectors to compute the bitangent. The bitangent is just the cross product, or maybe the opposite of the cross product, of these two vectors in this case. Doing this saves a bit of memory and vertex processing time, but costs a bit more processing time in the pixel shader, so it's less useful for weak GPUs and more useful if there are a lot of vertices. Doing things this way also yields (in most cases) subtle differences in the way that the normal map looks. So to get a correct result, you have to make sure that the baker is informed of which way the renderer is getting the bitangent vector. Again, Unity and Cryengine (and, as it happens, xNormal's 3D preview) pass the bitangent as vertex data so it's interpolated as such, and almost every other renderer that I know of computes the bitangent in the pixel shader. So if you're using Unity, you should tell Substance Painter to not compute tangent space per fragment, and if you're using UE4, you should tell Substance Painter to compute tangent space per fragment.
  • fattkid
    Options
    Offline / Send Message
    fattkid polycounter lvl 15
    Awesome. Thank you Jed. That makes a lot of sense. Much appreciated. I owe you a beer if we ever cross paths.

    To clarify - The "Tangent" is vertex data, and the "Tangent Space" is the specific math used to calculate the geometry's tangent, correct? For example, using Mikkt to calculate the tangents on a model?

    FYI - I forgot to mention, we are on a proprietary engine, so I'll have to find out the above specifics on binormal/bittangent calculations.

    Also, we are not using a synced workflow, but our engine and Maya do match pretty close. Less than ideal, I know, but it's beyond my control.
  • JedTheKrampus
    Options
    Offline / Send Message
    JedTheKrampus polycounter lvl 8
    The "tangent" is vertex data. The "tangent space" is the basis formed by the normal, tangent, and bitangent vectors in which the normal map can pretend to always be pointing towards +Z. MikkTSpace is an algorithm that calculates a pretty good, standardized tangent space basis in a particular way, completely independent of vertex ordering which can sometimes be important for mirroring normal maps across a seam. Other algorithms calculate the tangent space differently. In most cases the tangent and bitangent are rotated somewhat from what they would be in MikkTSpace. What matters the most is consistency between your baker and your renderer.

    If you're using a proprietary engine, there are two good ways to get a synced tangent space working if your testing has shown some discrepancies. You could integrate MikkTSpace, which is open source with an extremely permissive license that allows its source code to be integrated with proprietary projects. See here: https://github.com/dfelinto/blender/tree/master/intern/mikktspace  and here: http://gamedev.stackexchange.com/questions/128023/how-does-mikktspace-work-for-calculating-the-tangent-space-during-normal-mapping for more information on how to do this. Depending on how fucked your code is (if it's less fucked than Cryengine you should be able to do it without too much fuss) this should be pretty easy to do because of the clean, simple design of the MikkTSpace API. Just make sure you understand whether your engine calculates bitangents in the pixel shader, or uploads them in a vertex attribute. MikkTSpace bakers can handle either case.

    The other easy way that you could do it is to export the tangents and binormals with your FBX file and have the engine read them in and upload them to the GPU as-is. If memory serves, Maya bakes normal maps assuming that your engine calculates bitangents in the pixel shader, but I'm not sure about this so you would have to test it. I haven't implemented this way of doing things myself.

    The third way to do it, which I wouldn't recommend because it's more difficult than the other two ways, would be to look for documentation on how Maya's tangent space works and check your code to make sure it's exactly the same.

    The fourth way to do it would be to write an engine utility that imports world-space normal maps and uses the tangent basis that your engine calculates to convert them to tangent space. This is what I would recommend if every other technique has been investigated and deemed unworkable.

    I would personally strongly recommend option 1 because if your company hires some freelance artists they can bake normal maps using any baker that can output MikkTSpace normal maps, which includes xNormal, Substance, Toolbag 3 I assume, Handplane Baker, Mightybake, Modo, and Blender at least, and probably a couple of others. If you use option 2 they'll have to bake normal maps in Maya, Mightybake, or Handplane Baker, or be sure to use a baker that reads tangents from .fbx files, which all are good options but easier to mess up.

    Before you do any of these things, you should do a tangent space test to make sure that your renderer isn't already synced to something. For this purpose I favor a completely smooth-edged 6-face cube with one UV island, with a beveled cube baked down to a normal map for it. This setup is a worst case in which most renderers that are correctly synced should be expected to still behave correctly. If the faces of the cube appear completely flat and smooth in-engine (with perhaps some tiny bumps from compression or bit-depth artifacts) then your tangent space should be synced. By default, you need to export your tangents and binormals to Substance Painter for it to use Maya's tangent space for baking, so given that you said your renderer is kind of like Maya's tangent space I would recommend exporting tangents and binormals with your lowpoly model. If your engine were synced to MikkTSpace you would want to avoid exporting tangents and binormals with your lowpoly model so that Substance Painter calculates a MikkTSpace-compatible tangent space for your lowpoly model. It's the same way that xNormal did it: if you exported tangents, it uses them, and if you didn't export tangents, it calculates them.
  • fattkid
    Options
    Offline / Send Message
    fattkid polycounter lvl 15
    Thank you very much Jed. I now owe you a pitcher ;)
  • fattkid
    Options
    Offline / Send Message
    fattkid polycounter lvl 15
    It's me again.... I almost got this -

     So, if I export tangents into Substance from Maya, via FBX, I will be baking in Maya's native tangent space, instead of Mikkt? 

     you need to export your tangents and binormals to Substance Painter for it to use Maya's tangent space for baking

    I thought that if I exported from Maya FBX with Tangents and Binormals, into Painter, and baked - I would still be baking in Mikkt Tangent Space. It would just be with the Tangents on the verts from Maya, instead of the Tangents Mikkt came up with.

    So a cube with all soft edges, baked in Painter, with Tangents exported from Maya, would look perfect in our (theoretical) Maya tangent space based engine?

    In which case, if our engine was truly synced to Maya tangent space, it would be a synced workflow? Baking in Painter with exported Tangents?

    I was under the impression "Tangents" and "Tangent Space" were two different, but related things. "Tangent Space" was the algorithm to compute the normal map, based off of the Tangents, Binormals etc of the geometry.

    Thanks again Jed.

    -Trevor

  • JedTheKrampus
    Options
    Offline / Send Message
    JedTheKrampus polycounter lvl 8
    So a cube with all soft edges, baked in Painter, with Tangents exported from Maya, would look perfect in our (theoretical) Maya tangent space based engine?
    It's not a guarantee. There are some other factors like bitangents being computed per fragment or things getting interpolated wrong that might mess things up, and so on, but it should at least look kind of close. Triangulation algorithms being the same in your engine and your baker are another factor that could mess things up if your workflow doesn't involve locking normals and triangulation before baking. If I were baking normal maps for a Maya-synced engine I would probably bake them in Maya, just to be sure. Here's my test of such a situation. I've got a smoothed-normals cube exported from Maya with tangents and bitangents, and a corresponding high-poly cube exported from Blender. The lowpoly cube has one UV island and in Substance Painter I've set it to compute tangent space per fragment and OpenGL orientation. In Substance Painter the cube's faces look as flat as you would expect after baking the high poly:



    I export the texture set and set up the material using it in Toolbag 2, which is synced with Maya if you set the imported mesh to use Maya's tangent space. (I could also view it back in Maya's viewport.) This is what it looks like:



    Obviously this isn't flat. This is what an unsynced tangent space looks like. Trying again with Substance Painter NOT computing tangent space per-fragment, here's the result I get: 



    This obviously isn't synced to Maya tangent space either, and neither is it synced to MikkTSpace (switching the mesh to xNormal/Mikk in Toolbag verifies this. You can get Mikkt-synced normal maps for Toolbag 2 from SP2 but you have to check the box for interpolation in the pixel shader.) Which of these two is better is really a matter of taste. Neither is correct, and neither is "synced" to anything in particular. Finally, a normal map that I've baked in Maya with Transfer Maps (you'd want to use Turtle and get a 16-bit output of some kind instead for a production workflow, probably):



    This is what it should look like. So, I wasn't able to get something synced from SP2 for some reason. It would be a miracle if you could get something synced to your proprietary engine from SP2, in my opinion. You could bake object-space normals in SP2 and convert them in Handplane and that would be correctly synced to Maya's tangent space, but perhaps not to your proprietary engine.

    After some further fiddling around, even my Maya-baked normal map has ended up unsynced in the Maya viewport somehow. I am unsure what went wrong and perhaps I'll try again tomorrow when I'm more awake and less drunk.

    "Tangent" is one of three vectors that make up the tangent basis. "Tangent basis" means that you can add those three vectors each multiplied by a number supplied in your normal map and get a vector in world space. "Tangent space" refers to a particular group of these bases that varies in a defined, predictable way across the entire mesh, and tangent spaces must be defined with the same values at all places on the mesh's faces in the baker and the renderer for the result to be correct. When SP2 imports tangents and binormals for baking, that only guarantees that the tangent space is the same at vertices, and everywhere else it will be slightly incorrect because somehow the tangents are interpolated differently between vertices.
Sign In or Register to comment.