-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add BINORMAL and TANGENT attribute semantics #812
Comments
Did you mean |
Yes, I updated the title. |
As this is supposed to affect the spec, a nitpick from me, quoting from http://mathworld.wolfram.com/BinormalVector.html :
Some people have strong opinions about this, because the surface only has one normal, but it has infinitely many tangents, and one can only compute a bitangent for any given tangent. (I know, "binormal" is so common that one might have the choice between "common terminology" and "right terminology" here. But some tutorials, e.g. http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/ are already trying to use the "right" one...) |
@mlimper what is the common PBR term? I suggest we use that. |
If we can universally figure out following questions, it should be possible to include that in the core:
|
Yes, this is similar in the GL world, where, for example, vertex normals are not used by the pipeline for backface culling since they could be anything.
No, Cesium implements a general-purpose algorithm that is pretty good for most cases, but users can often do better if they know more about their mesh, e.g., because they have an equation for it, e.g., a sphere.
I dunno. I'm tempted to say no so they can fit a variety of use cases, but I'm also tempted to say yes so users can get the best performance and then just define other semantics for custom cases. For example: Guarantee that they form a right-handed system and are orthogonal to Perhaps see what other formats and engines do for tangent space effects if someone has the bandwidth. I do not. |
In a nutshell, should spec define these attributes meaning (to some extent), or just allow new tokens and let material/shaders define actual usage? E.g., with custom (GLSL) technique, actual data type and usage of CC @mlimper |
Ah, interesting idea...perhaps it is OK to have them very loosely defined by the attribute semantic and precisely defined by the material. The downside would be different material extensions may not be swappable with the same attributes. I suspect this wouldn't be a problem in practice since (1) most extensions would probably have the same definition, and (2) swapping is unlikely to be a common use case. |
My 2 cents. Normal/displacement/bump maps are only uniquely defined if tangents are defined. So defining tangents is quite important for portability of assets in my opinion. |
Agree with both!
Interesting point. I think as part of glTFs mission, it will be fine to help spreading the "right" term here, without much risk - I suppose people are smart enough to figure out that their "binormal" would be our "bitangent", and we could also briefly note this somewhere in the spec.
Sounds like a good way to go! |
@erich666 do you know the answer to "binormal" vs. "bitangent?" I thought I read something on your blog at one point.
|
Please do use the right term, "bitangent." Write Eric Lengyel if you want convincing. From Real-Time Rendering, 3rd edition, a footnote:
See http://mathworld.wolfram.com/BitangentVector.html as a reference, written by Eric Lengyel, who's a graphics programmer for games and an expert at math for graphics. I also sent a long email thread to Patrick, letting Eric Lengyel and Spike Hughes hash it out in 2010. No great conclusion, but no one said "binormal" was good, and "bitangent" was basically the winner. |
Thanks @erich666! OK, no question at all then, go with bitangent. |
To follow up here, I was doing some reading and a normal map is often distorted so it fits the surface so the bitangent and tangent may not be perpendicular so this is even more reason to not over constraint these in the spec. |
In GPU Pro 5, there is a chapter, "Quaternions Revisited", that explains that the tangent space basis can be stored as a quaternion with a small loss in quality. I don't know enough about this to say if this would be preferred since it saves so much memory, if this should be an option to use instead of explicit vectors, or if we should just pass on it for now. @erich666 have you looked at this closely? |
Closely? No. I know that there's a small loss in quality by doing so, according to the article. I've asked the author to comment. |
Hi, |
@erich666 @PeterSikachev thanks for the prompt response. Sounds like this has great potential to reduce file size. @mlimper @lexaknyazev what do you think? Should this be optional as a replacement to storing the vectors? Or could we just use a quaternion (assuming visual quality is acceptable)? It would likely require a bit more work for an exporter/converter, but the runtime would be simple and efficient by default. |
I'd include @cedricpinson and ask for "what do people use?" I just recalled: Malyshau, Dzmitry, “A Quaternion-Based Rendering Pipeline,” in Wolfgang Engel, ed., GPU Pro3, CRC Press, pp. 265–273, 2012. He notes one additional bit you need to store: handedness. He explains how quaternions are different than matrices in this way. Malyshau also talks about how quaternions can also save on texture access and storage. It's also worth pointing out that, for good or ill, the normal workflow is TBN (tangent, bitangent, normal) in most modelers, AFAIK. |
You can convert TBN into quaternions. Nobody makes artists set quats directly, obviously. The only thing which quaternions do not allow is a skewed basis, but I would argue that for vertex basis you don't really need it. |
Good point, and that's simple enough. If quaternions are the default, we need to document how to go in both directions (or give a good reference) to make it easy for users to adopt. |
Not sure if adding quaternions implies removing NORMAL, since quaternions do imply an orthornomal basis. I would suggest no. But in that case, using 4D tangents (x,y,z,handedness) feels like a better compromised. No need to store bitangent and trivial to encode and compute with. To get the bitangent, just take the cross product. In this manner, BITANGENT may be added in case one wants a non-orthonormal basis, but they are not required. It seems storing tangents takes the same space as quaternions if normals are stored separately, and they may be encoded more efficiently using the same normal encoding tricks. Just my 2cents. |
Of course it does. In fact, xyz components of the quaternion are Normal_xyz * sin(theta/2). |
I guess my post was not clear. Even if using quaternions, I would suggest to not remove NORMAL to ease adoption. But converting on input is always an option, I guess. A curiosity. Which game engines use Quaternions directly instead of normals?. It is very common in the area of graphics where I work, but I am curious how widespread this use is. |
In Ogre we use QTangents as described by CryTek.
We have already code that converts 3 float3 into four 16-bit SNORM values. Decoding:
The definitions of xAxis and yAxis can be found here: https://bitbucket.org/sinbad/ogre/src/18ebdbed2edc61d30927869c7fb0cf3ae5697f0a/Samples/Media/Hlms/Common/GLSL/QuaternionCode_piece_all.glsl?at=v2-1&fileviewer=file-view-default Technically, RGBA10_2 could be used, reconstructing one of the components and storing sign in the alpha component, but one needs to be very careful, to not break edge cases when value is 0 or reflection is used. Just Cause 2 uses an entirely different approach, and uses 4 bytes in total to store all of the data. See http://www.humus.name/Articles/Persson_CreatingVastGameWorlds.pdf |
xAxis & yAxis have done almost all the work of zAxis...using cross instead is just adding issues. |
If you're decoding on the CPU yes. If you're decoding on the GPU, you have to decode them on the Vertex Shader; thus the cross product will very likely be done in the pixel shader so you end up sending 7 interpolants instead of 9 (and actually you may be able to send 6 if you manage to encode the sign in another interpolant). |
You don't have to decode a quaterion to TBN. You can transform a normal from a bump map directly by quaternion in the pixel shader. |
Quaternions don't interpolate well because as they may double cover. |
Quaternions DO interpolate well (and better than TBN), they just need a proper alignment which is easily doable for any correctly UV-mapped object. Please, refer to our GPU Pro 5 article for further details. |
Something I feel may be missing from the conversation here is a bit of consideration for where normal map content comes from. At the risk of saying stuff everyone already knows, here's a quick summary. Normal maps are almost always "baked". That is, they are automatically generated by casting rays from the low poly mesh onto a high poly mesh to capture the detail missing in the low poly approximation. This process is very sensitive to the way in which tangent basis vectors are set up and interpolated across the triangles. If the baking tool and the renderer do not match in every regard, you will get rather bad normal mapping artifacts. For game studios this is fairly easily solved. Either the renderer is crafted to match the tool the artists are using in-house, or custom baking tools are altered to produce renderer-friendly meshes and normal maps. Projects like glTF do not have the benefit of such integration however. Since I presume the purpose of the glTF format is to be of general use, and broadly compatible with content and workflows "in the wild", then the variety of conventions in use by artists today should be a primary, not a secondary, concern. A rough ordering of the top tools, in descending order of popularity, looks something like this: xNormal For the most part, none of these tools produce identical outputs, even though they all use TBN vectors. They are all slightly different, both in terms of tangent generation and tangent interpolation. This sucks, but it's where we are. I know this because I've recently written my own baker (Marmoset Toolbag) that supports all of the above conventions. With this in mind I hope an idea like interpolating quaternions over the triangle would be immediately rejected, even though it's probably perfectly elegant engineering-wise, because that would set us directly at odds with essentially every baking tool on the market. At least, if we want to do more with glTF than send efficiently packaged Utah Teapots to each other. So what would I recommend? Well, the closest thing to a standard in the space has been set by Morten Mikkelson (now at Unity I believe). He wrote a paper, and a C implementation of the algorithm, for generating stable tangent results on any mesh, which is I believe always orthogonal (bitangent can be generated in the shader) and requires no per-pixel renormalization or orthogonalization. It's in use in both Unity and Unreal, as well as several bakers (xNormal, Substance, Knald, Marmoset). So that would be a pretty strong vote for N3 + T4 vertex storage, and simple interpolation of the same, I think. If you really, really wanted the extra space, this could be switched to quaternion (but you shouldn't interpolate via quaternion, you'd have to use N+T for that). If you do this though, you should provide a tool for converting, since essentially all of our user's input data will come in TBN form. Personally, I wouldn't want to burden the users or the import process with any of this, and I'd just stick with tangent vectors, which everyone is using already. Hope this helps! |
No worries. After the first ~20 comments here, I was about to ask: What is this all about?. Although I still don't know the answer (really), your comment at least leaves me with the high-level answer: Stuff about normals that is so complicated that even the major game studios don't have a silver bullet for it. I hope that here, a solution may be found that is sustainable and for which an undoubted, unambiguous (CC0) reference implementation will be provided. |
My $.02 and that's all it's worth, not my long suit here: TBN is SO much easier to understand for implementors. OTOH as long as tools and viewers can easily convert and the math is documented, that would be consistent with how to do rotations... for whatever that's worth. |
The tweet for this thread had some nice comments, adding them here so, at the least, we have a single archive of this discussion:
|
I'll ask that we try to converge on this topic by this Wednesday, Feb 8, so we can get the spec and ecosystem in order. As for the best approach, I originally suggested quaternions, but @jeffdr's comments on the toolchain make me think it might create ecosystem issues. @mlimper's suggestion seems reasonable in that is is pretty efficient, easy to implement, and covers all cases:
I'll also add that glTF does not define the datatype for vertex attribute semantics (however, it does for uniform semantics) so a user could short these as Any other thoughts on this or another approach? |
What is glTF trying to solve? If it's being a interchange format (aka "Binary Collada"); then Although if this is the aim, personally I'll probably stick to OpenGEX. If the goal is being a format with the hopes of actually being used in the wild (WebGL apps, Indie/AAA games, simulations, mobile apps, etc) including VR applications, then something I learnt from VR is that it pushes EVERYTHING. From runtime performance to loading performance. That means I need a small lossy quaternion representation (or the one used by Just Cause) and assume my tris are orthonormal. Consume as little as bandwidth as possible, and as less interpolators as possible. That means I'll need these compact representations being discussed here. I could sure do the conversion during loading, right? Well then I hurt loading performance, which is also important for VR. IMHO if glTF wants to have the slightest chance of being used and not die as a format that nobody picks up it has to support compact/alternative representations. |
Good summary @pjcozzi, and @darksylinc. Two quick comments:
Thanks! The presentation is here, PDF here< which I think is worth adding to the summary. In that presentation they seem to agree with @PeterSikachev and his comment:
Slide 10 of the Crytek presentation notes:
For me it seems to come down to whether non-orthogonal tangent frames are something that are the norm and are not corrected, and are hard to correct. I know very little here about content creation: how are these corrected? Peter says it can be fixed in the exporter, but it seems like if you skew a texture on a surface, that's something the artist needs to fix up front, an exporter can't fix the tangent frame AFAIK. But I'm uninformed, so I'd love to learn. If it's easy to teach people to fix things up, "here's the code", then quaternions (in some form - there are two competing methods identified so far) give good compression (slide 17 of the QTangents says they're a win). If TBN is how most people do it (which from what I can tell is true) and there's no obvious way to clean up and move to quaternions, then TBN needs to be supported (probably storing just TB and reflection, and not the normal). If glTF is meant to be a flexible interchange format (I don't think that's a goal, but hey I'd love to have something that is...), then TBN is needed. However, two representations starts to wander into TIFF and VRML2 (and Collada, AFAIK) territory, of the file format meaning everything to everyone but no one being able to 100% interpret the other's data. |
glTF aims to be a "JPEG-for-3D", hence, runtime consumption format. If we stick to orthonormal basis, this issue becomes about implementation (quats vs different flavors of optimized TBN). Otherwise, it looks like there's no alternative to full TBN support.
Also from @PeterSikachev:
|
Here is an implementation of NORMAL + TANGENT4 in BabylonJS: BabylonJS/Babylon.js#1911 (thanks to @dewadswo) I've also updated @sbtron's test scene to use models that include TANGENT4 in the glTF. Here is a direct link: https://sbtron.github.io/BabylonJS-glTFLoader/?model=PillowPlane&environment=darkPark&pointLight=true&showTangentSpace=true |
Hey, great progress - good to see this! I worked on a small C++ tangent generator, which is public domain: I am using this code to generate tangents for export, and also for baking normal maps. It delivers useable results, at least for my use cases - two simple examples are shown on the page. Sketchfab is also able to render the assets with those maps (with its own tangents) - at least it does not seem totally broken to me: After the great explanations of @jeffdr above, I checked and tried the available implementation of Morten Mikkelson. However, I found the way it works a bit too "intrusive", as it completely rearranges your index / vertex data. My code is a bit more modular, I hope. Looking forward to feedback , comments and contributions! |
@McNopper @mlimper @AurL Just merged the tangent support into the core spec. Can you check your implementations/samples to ensure they match the spec? I believe there is currently some discrepancies on the attribute name (TANGENT4 vs. TANGENT). The name TANGENT is what we have committed to the repo. |
I am already using VEC4 for TANGENT with the correct interpretation. |
Okay, fixed it: |
Thanks for the update! Just checked: TGen is not affected (doesn't generate glTF content), and our MOPS CLI tool already uses the |
@bghgary Thanks. I will update this |
Updated in #826 |
From CesiumGS/gltf-pipeline#203:
@lexaknyazev @mlimper
The text was updated successfully, but these errors were encountered: