I am creating a simple mesh from 3d coordinates at runtime. I can build the mesh fine, but I need to calculate the mesh tangents in order to use materials with normal maps on the mesh I build. According to the Mesh.tangents documentation, mesh tangents are an array of Vector4s So, if I have a mesh composed of four vertices and two triangles (a square or rectangle, for instance), would my tangents array have four Vector4s (one for each vertex in the mesh)? Would each Vector4 be composed of the 3d coordinates for the appropriate vertex and then w? The documentation says that w is either 1 or -1. How do I know which to use? Thanks for your help. -- Anthony
http://answers.unity3d.com/questions/271/calculating-tangents-vector4 This answer by Aras is the closest I could get, when researching the same problem, but one thing springs to mind: If the tangents are calculated using normals and uv's, then provided these are in place, why not have a simple MakeTangents function in the mesh class taking the normals specific UVs as arguments :?: I mean it must be there somewhere under the hood as it's available on import of meshes, and in general Unity is doing a really good job in NOT being rocket science :wink:
Yes, it's there somewhere for the editor, but it's probably not something available at runtime (there are many editor-only functions). I definitely agree that a RecalculateTangents() function, which would work along the same lines as RecalculateNormals() does, would be nice. In the meantime, one of the links Aras provided ( http://www.terathon.com/code/tangent.html ) has code which can be converted to Javascript or C#. I managed to find that on my own a while ago so I'm glad to hear he thinks it's a good method; I used it in my ObjReader utility. --Eric
@ Eric Thanks for the insight! If I ever get that far I'll post the code in this thread for comments on optimizing, but in this case I would guess the 50% "script inpact" on performance is a bitch compared to a direct call or?? Any threads hinting on how I would go around implementing the other link by Aras (NVMeshMender) would it have any effect on performance compared to the scripted version what so ever?? Thx.
With that kind of code I doubt it's as low as 50%, and unless your object has 10 million vertices I also doubt you'd notice any speed difference. Considering the 65K vertex per object limit it's a moot point anyway. Not sure, haven't looked at it, but considering the scripted equivalent of RecalculateTangents is only going to run once, does it really matter if it's .002 seconds instead of .001 seconds? --Eric
@ Eric NOPE.. not at all.. If this is the amount of time we are talking about on a 65K mesh it's completely irrelevant, and just a matter of convenience.. Thanks again for your respons!
Hi again.. So far I think I'm closing in on a plausable javascript translation I would love to share BUT: When I try testing it I run into the following problems that may be due my misunderstanding of how Unityscript works. I have a gameobject with a meshfilter meshrender component. I attach my SolverScript another script that calls it. I'm using the Mesh as argument and this causes problems at runtime ( compiles fine ) : "ApplicationException: Argument is not enumerable" pointing at the mesh as an argument? Code for calling: Code (csharp): function Start () { var mesh : Mesh = GetComponent(MeshFilter).mesh; var tangentssolver = GetComponent("SolverScript"); tangentssolver.BuildTangents(mesh); } function in SolverScript script ( #pragma strict ): Code (csharp): function BuildTangents(theMesh : Mesh){ /*solve the tangents an write to mesh*/ } Hopefully I'm making an obvious mistake, but the following questions come to mind: Is there a restriction on what type of arguments I can use? Is the SolverScript placed wrong. Is it possible to call the SolverScript without attaching it to a gameobject? THX...
Nothing wrong I can see; the error suggests something about trying to use a coroutine when you shouldn't, possibly. --Eric
I was iterating something that couldn't be ( Learning by doing )... Here is the finished script thanks for the feedback on this one Eric! ATM it solves a 65K mesh at 30 FPS @ i7 920 2.67. If this can be optimized further or there is any "worse practice coding" please comment. If not, I'll append it to the UnityAnswers thread above. Code (csharp): /* Derived from Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. [url]http://www.terathon.com/code/tangent.html[/url] noontz 2010 */ #pragma strict class TangentSolver { function TangentSolver(theMesh : Mesh) { var vertexCount : int = theMesh.vertexCount; var vertices : Vector3[] = theMesh.vertices; var normals : Vector3[] = theMesh.normals; var texcoords : Vector2[] = theMesh.uv; var triangles : int[] = theMesh.triangles; var triangleCount : int = triangles.length/3; var tangents : Vector4[] = new Vector4[vertexCount]; var tan1 : Vector3[] = new Vector3[vertexCount]; var tan2 : Vector3[] = new Vector3[vertexCount]; var tri : int = 0; for ( i = 0; i <= (triangleCount-1); i++) { var i1 : int = triangles[tri]; var i2 : int = triangles[tri+1]; var i3 : int = triangles[tri+2]; var v1 : Vector3 = vertices[i1]; var v2 : Vector3 = vertices[i2]; var v3 : Vector3 = vertices[i3]; var w1 : Vector2 = texcoords[i1]; var w2 : Vector2 = texcoords[i2]; var w3 : Vector2 = texcoords[i3]; var x1 : float = v2.x - v1.x; var x2 : float = v3.x - v1.x; var y1 : float = v2.y - v1.y; var y2 : float = v3.y - v1.y; var z1 : float = v2.z - v1.z; var z2 : float = v3.z - v1.z; var s1 : float = w2.x - w1.x; var s2 : float = w3.x - w1.x; var t1 : float = w2.y - w1.y; var t2 : float = w3.y - w1.y; var r : float = 1.0 / (s1 * t2 - s2 * t1); var sdir : Vector3 = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); var tdir : Vector3 = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; tri += 3; } for (i = 0; i <= (vertexCount-1); i++) { var n : Vector3 = normals[i]; var t : Vector3 = tan1[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize( n, t ); tangents[i].x = t.x; tangents[i].y = t.y; tangents[i].z = t.z; // Calculate handedness var tW : int = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0 ) ? -1 : 1; tangents[i].w = tW; } theMesh.tangents = tangents; } }
Looks good, though this part: Code (csharp): var tW : int = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0 ) ? -1 : 1; tangents[i].w = tW; would be better like Code (csharp): tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0 ) ? -1.0 : 1.0; because otherwise you're spending time converting ints to floats for no real reason. Also declaring a variable you don't really need, trivial though that may be. You can take out the type declarations if you want, not that they hurt anything. (Personally I don't find that "var tan1 : Vector3[] = new Vector3[vertexCount];" adds anything except redundancy, when it's still obvious "var tan1 = new Vector3[vertexCount];" is a Vector3 array.) --Eric
Eric is right that in this case, the type mismatch makes the explicit intermediate variable less efficient, but in any case where the types match properly (such as without the explicit int typing above), any compiler worth its salt (including the one Unity uses) will use registers for intermediate values as necessary, whether they are named or not. This means that you shouldn't try to fit everything on one line if it's easier to read with named intermediate variables.
Hi.. Thanks again for the insight: I'm adjusting accordingly.. I'm performance testing the changes and I was trying Destroy(this) in the end to see if I would get some benefit, but it does not compile? As I understand javascripts inherit Destroy?? How can I implement this? Thx..
I would have thought that, except there's a measurable difference between using one line and two, even when the types match (one line is faster). It should compile (and work), but I'm not sure what benefit you'd get from that. --Eric
Eric @ It doesn't compile? I'm inserting Code (csharp): Destroy(this); just after Code (csharp): theMesh.tangents = tangents; My idea was to free memory, but please bare with my knowledgelevel :wink: Anyway for pure insight I would love to know what I'm doing wrong. Here is the (EDIT: ALMOST) final code: Code (csharp): /* Derived from Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. [url]http://www.terathon.com/code/tangent.html[/url] noontz 2010 */ #pragma strict class TangentSolver { function TangentSolver(theMesh : Mesh) { var vertexCount = theMesh.vertexCount; var vertices = theMesh.vertices; var normals = theMesh.normals; var texcoords = theMesh.uv; var triangles = theMesh.triangles; var triangleCount = triangles.length/3; var tangents = new Vector4[vertexCount]; var tan1 = new Vector3[vertexCount]; var tan2 = new Vector3[vertexCount]; var tri = 0; for ( i = 0; i <= (triangleCount-1); i++) { var i1 : int = triangles[tri]; var i2 : int = triangles[tri+1]; var i3 : int = triangles[tri+2]; var v1 : Vector3 = vertices[i1]; var v2 : Vector3 = vertices[i2]; var v3 : Vector3 = vertices[i3]; var w1 : Vector2 = texcoords[i1]; var w2 : Vector2 = texcoords[i2]; var w3 : Vector2 = texcoords[i3]; var x1 : float = v2.x - v1.x; var x2 : float = v3.x - v1.x; var y1 : float = v2.y - v1.y; var y2 : float = v3.y - v1.y; var z1 : float = v2.z - v1.z; var z2 : float = v3.z - v1.z; var s1 : float = w2.x - w1.x; var s2 : float = w3.x - w1.x; var t1 : float = w2.y - w1.y; var t2 : float = w3.y - w1.y; var r : float = 1.0 / (s1 * t2 - s2 * t1); var sdir : Vector3 = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); var tdir : Vector3 = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; tri += 3; } for (i = 0; i <= (vertexCount-1); i++) { var n = normals[i]; var t = tan1[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize( n, t ); tangents[i].x = t.x; tangents[i].y = t.y; tangents[i].z = t.z; // Calculate handedness tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0 ) ? -1 : 1; } theMesh.tangents = tangents; } }
Oh, I see...you can't Destroy a class. You could do something like Code (csharp): tangents = null; Also, Code (csharp): tangents.w = ( Vector3.Dot(Vector3.Cross(Vector3.one, Vector3.zero), Vector3.one) < 0 ) ? -1 : 1; is a little faster like Code (csharp): tangents.w = ( Vector3.Dot(Vector3.Cross(Vector3.one, Vector3.zero), Vector3.one) < 0.0 ) ? -1.0 : 1.0; That way you're not converting ints to floats unnecessarily. (Since tangents.w is a float and Vector3.Dot returns a float.) --Eric
Thanks.. Updated below. hmmm... It's not possible to destroy an instance of a class? I can compile this: Code (csharp): function Update () { var theMesh : Mesh = GetComponent(MeshFilter).mesh; var a = new TangentSolver(theMesh); Object.Destroy(a); } but it throws an error at runtime, so I guess this is the reason? EDIT: ( answers the above ) : http://answers.unity3d.com/question...erived-from-the-object-class-so-what-are-they Final code: Code (csharp): /* Derived from Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. [url]http://www.terathon.com/code/tangent.html[/url] */ class TangentSolver { function TangentSolver(theMesh : Mesh) { vertexCount = theMesh.vertexCount; vertices = theMesh.vertices; normals = theMesh.normals; texcoords = theMesh.uv; triangles = theMesh.triangles; triangleCount = triangles.length/3; tangents = new Vector4[vertexCount]; tan1 = new Vector3[vertexCount]; tan2 = new Vector3[vertexCount]; tri = 0; for ( i = 0; i < (triangleCount); i++) { i1 = triangles[tri]; i2 = triangles[tri+1]; i3 = triangles[tri+2]; v1 = vertices[i1]; v2 = vertices[i2]; v3 = vertices[i3]; w1 = texcoords[i1]; w2 = texcoords[i2]; w3 = texcoords[i3]; x1 = v2.x - v1.x; x2 = v3.x - v1.x; y1 = v2.y - v1.y; y2 = v3.y - v1.y; z1 = v2.z - v1.z; z2 = v3.z - v1.z; s1 = w2.x - w1.x; s2 = w3.x - w1.x; t1 = w2.y - w1.y; t2 = w3.y - w1.y; r = 1.0 / (s1 * t2 - s2 * t1); sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; tri += 3; } for (i = 0; i < (vertexCount); i++) { n = normals[i]; t = tan1[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize( n, t ); tangents[i].x = t.x; tangents[i].y = t.y; tangents[i].z = t.z; // Calculate handedness tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0 ) ? -1.0 : 1.0; } theMesh.tangents = tangents; } }
This is a *very* belated reply, my apologies. Noontz' final code above works perfectly for my purposes. It really should go on the wiki, as it works so nicely - just copied it straight into my meshbuilding code. It's been a while since I had a chance to work on this again - other work priorities, but I'm getting back to this project now. This code has helped us to finally be able to read some medieval music which no one has heard for the past six hundred years. Really. It's a long story that once the project is done I'll share, as it's quite interesting. Many thanks again, for the awesome work - to Eric5h5 too!
Glad to hear someone´s using it I placed the code under the relevant question so it can be searched: http://answers.unity3d.com/questions/271/calculating-tangents-vector4 and Ninjamint has contributed with a C# version same place. Sounds interesting that music project!
I found a tangent solver in J++ (sorry but I can't remember where I found it and I recoded it in c#. Seems to work. But it's pretty slow when you have big meshes to recompute. I'm tempted to try producing a c version and putting it in a dll to see if that makes a difference. Code (csharp): class TangentSolver { public static void Solve(Mesh mesh) { int triangleCount = mesh.triangles.Length / 3; int vertexCount = mesh.vertices.Length; Vector3[] tan1 = new Vector3[vertexCount]; Vector3[] tan2 = new Vector3[vertexCount]; Vector4[] tangents = new Vector4[vertexCount]; int a = 0; while(a < triangleCount) { long i1 = mesh.triangles[a++]; long i2 = mesh.triangles[a++]; long i3 = mesh.triangles[a++]; Vector3 v1 = mesh.vertices[i1]; Vector3 v2 = mesh.vertices[i2]; Vector3 v3 = mesh.vertices[i3]; Vector2 w1 = mesh.uv[i1]; Vector2 w2 = mesh.uv[i2]; Vector2 w3 = mesh.uv[i3]; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; float r = 1.0f / (s1 * t2 - s2 * t1); Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (a = 0; a < vertexCount; a++) { Vector3 n = mesh.normals[a]; Vector3 t = tan1[a]; tangents[a] = (t - n * Vector3.Dot(n, t)).normalized; tangents[a].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f; } mesh.tangents = tangents; } }
I'd recommend using the code posted by Noontz instead. It's much much faster. I suspect the main difference is that you're reading mesh.triangles, mesh.vertices etc. constantly, instead of assigning those to an array once. --Eric
Stolen from the ProcedualMesh tutorial Code (csharp): vertex = Vector3 (x+MainTerrain.TerrainPixelSize - block, pixelHeight, y+ MainTerrain.TerrainPixelSize - block); vertices[y* block + x] = Vector3.Scale(sizeScale, vertex); uv[y* block + x] = Vector2.Scale(Vector2 (x + offset.x, y + offset.y), uvScale ); vertexL = Vector3( x-1, MainTerrain.PerlinTex.GetPixel(x + offset.x -1, y).r , y ); vertexR = Vector3( x+1, MainTerrain.PerlinTex.GetPixel(y + offset.y + 1, y).r , y ); tan = Vector3.Scale( sizeScale, vertexR - vertexL ).normalized; tangents[y*block + x] = Vector4( tan.x, tan.y, tan.z, -1.0 );
Thanks Eric, wow that is much faster. about 20 times faster actually. I think the speed increase is due to the use of this function: The only significant difference I can see is the fast routine does this: // Gram-Schmidt orthogonalize Vector3.OrthoNormalize( ref n, ref t ); tangents.x = t.x; tangents.y = t.y; tangents.z = t.z; Compared to the old routine doing this: tangents = (t - n * Vector3.Dot(n, t)).normalized; Amazing! anyway it's a reasonably efficient solution for me now. It would still be really nice to have a built in function to create the tangents for a mesh though. regards, Tony
Actually no, I just tested it and while the OrthoNormalize thing is faster, the vast majority of the slowness was because you were using the mesh directly instead of copying the vertices/tris/etc. to arrays first. (When you assign any of the mesh.triangles and so on to arrays, they are actually copied; it's not a reference.) --Eric
Thanks Noontz, Fantastic code, and many thanks!!! I required it to make my ammendments to OBJ importer code complete, however struggled for a day with the c# code you posted on unity answers, as I thought it was not working due to my code flipping normals/mirroring mesh, however the correction is obvious and due to the many amalgamations, alterations and reversioning of the code:- On answers page please either replace:- Code (csharp): int triangleCount = mesh.triangles.Length / 3; with Code (csharp): int triangleCount = mesh.triangles.Length; or totally remove that line and replace:- Code (csharp): for(long a = 0; a < triangleCount; a+=3) with Code (csharp): for(long a = 0; a < mesh.triangles.Length; a+=3) Many thanks again, I just wanted to save other people any issues.
Hi guys, I'm using the TangentSolver posted before but I'm having some troubles with the result. The thing is that I'm adding this code to my create mesh code in a character customization scene where you can morph the character face, when you leave the scene I'm saving the morphed mesh to use it later. The problem shows up when I use TangentSolver I get this line in the middle of the character face... something like this: ...and also the mesh looks really dark and it doesn't have to anything to be with the light. So, I was trying to find my way around the TangentSolver code and I found that if I chance the line: Code (csharp): // Calculate handedness tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0 ) ? -1.0 : 1.0; and chance it for: Code (csharp): tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0 ) ? 1.0 : -1.0; I get something like this... but the line it's still there however the face looks a littler lighter: I guess that the direcctions of the tangents are not correct, but I really don't have a clue where to start in the TangentSolver calculations.... Please anyone can help to find out what I'm doing wrong ?
Thanks for this script! I took the liberty to translate it to C#: Code (csharp): /* Derived from Lengyel, Eric. "Computing Tangent Space Basis Vectors for an Arbitrary Mesh". Terathon Software 3D Graphics Library, 2001. [url]http://www.terathon.com/code/tangent.html[/url] */ using UnityEngine; using System.Collections; public class TangentSolver : MonoBehaviour { public void Solve(Mesh theMesh) { int vertexCount = theMesh.vertexCount; Vector3[] vertices = theMesh.vertices; Vector3[] normals = theMesh.normals; Vector2[] texcoords = theMesh.uv; int[] triangles = theMesh.triangles; int triangleCount = triangles.Length/3; Vector4[] tangents = new Vector4[vertexCount]; Vector3[] tan1 = new Vector3[vertexCount]; Vector3[] tan2 = new Vector3[vertexCount]; int tri = 0; for (int i = 0; i < (triangleCount); i++) { int i1 = triangles[tri]; int i2 = triangles[tri+1]; int i3 = triangles[tri+2]; Vector3 v1 = vertices[i1]; Vector3 v2 = vertices[i2]; Vector3 v3 = vertices[i3]; Vector2 w1 = texcoords[i1]; Vector2 w2 = texcoords[i2]; Vector2 w3 = texcoords[i3]; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; float r = 1.0f / (s1 * t2 - s2 * t1); Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; tri += 3; } for (int i = 0; i < (vertexCount); i++) { Vector3 n = normals[i]; Vector3 t = tan1[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize(ref n, ref t); tangents[i].x = t.x; tangents[i].y = t.y; tangents[i].z = t.z; // Calculate handedness tangents[i].w = ( Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f ) ? -1.0f : 1.0f; } theMesh.tangents = tangents; } }
I've confirmed that the tangents are wrong (they make an absolute mess of cubemap-based reflections) . Someone should have tested the code thoroughly first....
I wish I had a fix, since my project is at an impasse until I do. The odd thing is that bumpmapping seems to work fine, but cubemapping doesn't. The reflections vary enormously from one facet to the next (I'm testing it on a cylinder with 20 facets around the diameter). Does anyone know what the problem would be?
Please vote for the official request here if you want to have Mesh.RecalculateTangents. http://feedback.unity3d.com/suggestions/recalculatetangents
Thanks so much for this. I'm having an issue using this for an animated mesh. It works great until I add a mesh animation. Any idea how why?
I've got a problem with the above tangent calculations on a procedural cube sphere too. I am guessing it has something to do with the direction of the UVs on the 6 different spherical cube sides, some seams are very apparent. But then again it could be my shader. Or both =)
Had to change the code to make it work correctly. Tan2 worked a lot better for me. So my code is based on tan2. Only the x axis was reversed oposed to what it should have been. So I changed it's sign. Though I guess the code depends on the order in which the triangles value goes through the vertices (in other words it depends on the direction of points in of the vertexes on your model). Disclamer: I don't know what I'm doing with tangents. Just that it is working for me with the following code correction. Code (CSharp): for (int i = 0; i < (vertexCount); i++) { Vector3 n = normals[i]; Vector3 t = tan2[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize(ref n, ref t); tangents[i].x = -t.x; tangents[i].y = t.y; tangents[i].z = t.z; // Calculate handedness tangents[i].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f; }