Java-Gaming.org Hi !
 Featured games (84) games approved by the League of Dukes Games in Showcase (565) Games in Android Showcase (151) games submitted by our members Games in WIP (606) games currently in development
 News: Read the Java Gaming Resources, or peek at the official Java tutorials
Pages: [1]
 ignore  |  Print
 SOLVED Calculating Tangent from Normal  (Read 4722 times) 0 Members and 1 Guest are viewing this topic.
bogieman987
 « Posted 2014-05-21 22:23:23 »

Hi, everyone

First, is this the appropriate place to post this, as I wasn't too sure.

I have googled this, however a lot of the answers were either too complicated due to the Math terminologies used and/or lack of easily understood example, or I wasn't sure how I would implement the answer myself.

So I'm asking here. So I can hopefully understand and implement what I need.

From what I understand, the tangent is perpendicular from the normal.

So here's a square, where two corners are raised slightly.

 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16 `v -1.000000 0.516746 1.000000v 1.000000 0.000000 1.000000v -1.000000 0.000000 -1.000000v 1.000000 0.504169 -1.000000vt 0.943613 0.001434vt 0.999903 0.941847vt 0.057807 0.941847vt 0.000097 0.000097vn 0.003003 0.999991 -0.003003vn -0.237447 0.941933 0.237447vn 0.242680 0.939262 -0.242680f 2/1/1 4/2/2 3/3/1f 1/4/3 2/1/1 3/3/1`

So with the first triangle
 1  2  3  4  5  6  7  8  9  10  11  12 `// Positionv 1.000000 0.000000 1.000000v 1.000000 0.504169 -1.000000v -1.000000 0.000000 -1.000000// UVvt 0.943613 0.001434vt 0.999903 0.941847vt 0.057807 0.941847// Normalvn 0.003003 0.999991 -0.003003vn -0.237447 0.941933 0.237447vn 0.242680 0.939262 -0.242680`

How would I calculate the tangent?

Also, when implementing normal mapping, do I need, one tangent per triangle, or one tangent per vertex.

Thanks in advance for any help.

The Lion King
 « Reply #1 - Posted 2014-05-21 22:52:04 »

lets call the tangent vector Vt and the normal vector Vn

Vt dot Vn = Magnitude(Vt) * Magnitude(Vn) * cos(angle)

if they are tangent the angle is 90 degrees. Since angle is 90 degrees cos(90) = 0. This turns the right hand side to 0.

so

Vt dot Vn = 0.

The dot product means multiply similar components and add them. Ex(X1, Y1) dot (X2, Y2) = X1 * X2 + Y1 * Y2

so

VtX * VnX + VtY * VnY + VtZ * VtZ = 0

this becomes the equation of a plane. Any vector on this plane is tangent to point.

-simpler way using the face-

If you just want a vector that is "tangent" to the triangle, just take any two points on the triangle and subtract one from the other.

EDIT:
if you have a high poly model (a model with lots of polygons) use the 2nd way otherwise you'll probably want to use the first way.

"You have to want it more than you want to breath, then you will be successful"
bogieman987
 « Reply #2 - Posted 2014-05-22 12:42:08 »

I'm having difficulty understanding, it's just not clicking in. Stupid brain

So at the beginning, is Vt known? Where as Vn is. (In my case anyways)

If not, will following what you wrote get the value of Vt?
If it is, how do I get the value?

And for the simpler way, would that just be as simple as Vt1 - Vt2 or some other combination?
Or would it be all the data in the vertices taken away from each other? (Position, normal etc)

I know it may seem lazy on my end, but it would help me understand, could you perhaps solve it for the first triangle, so I get an idea of
it in practice.

Riven
« League of Dukes »

« JGO Overlord »

Medals: 949
Projects: 4
Exp: 16 years

 « Reply #3 - Posted 2014-05-22 12:51:43 »

Imagine your normal pointing up, along the Z axis:

There are infinite tangent vectors for this normal, all on the XY plane.

In conclusion: solely a normal (or a triangle) is not enough to determine a single tangent vector.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
bogieman987
 « Reply #4 - Posted 2014-05-22 14:33:03 »

Ah, ok, so what information do I need in order to calculate a single tangent vector?

Bearded Cow

Senior Devvie

Medals: 2
Projects: 1
Exp: 1 year

¬..¬

 « Reply #5 - Posted 2014-05-22 15:37:38 »

A point along that vector.

What are you trying to create, draw a picture in paint if you have to.
bogieman987
 « Reply #6 - Posted 2014-05-22 16:37:35 »

I'm trying to get a tangent vector so I can implement normal mapping, I know there's object or world space based methods, but I would rather use tangent.

Unfortunately, the Wavefront file format doesn't have a tangent, so I need to create one with what ever information I can.

I figured I would need to use the normal vectors in order to try and create a tangent vector, but I unsure on how I go about doing this.

Bearded Cow

Senior Devvie

Medals: 2
Projects: 1
Exp: 1 year

¬..¬

 « Reply #7 - Posted 2014-05-22 17:13:35 »

Do you understand what normal and tangents are??
bogieman987
 « Reply #8 - Posted 2014-05-22 17:17:19 »

I know what they are, but I don't fully understand them, other-wise I wouldn't be asking for help.

Bearded Cow

Senior Devvie

Medals: 2
Projects: 1
Exp: 1 year

¬..¬

 « Reply #9 - Posted 2014-05-22 18:14:17 »

I drew you this.

Both a tangent and normal require a given point along a circle or line. A tangent means that it is parallel to the circle and only touches/crosses the circle once. A normal is as you say a perpendicular line. So for example you can have a tangent to a circle/curve and then also figure out the normal to that tangent when it touches the circle.

Could you post pictures or code of what your trying to achieve, sorry if I'm miss understanding something.

Also look here, http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/#Computing_the_tangents_and_bitangents

I am guessing this is what you are trying to do.
Roquen
 « Reply #10 - Posted 2014-05-22 18:47:51 »

bogieman987
 « Reply #11 - Posted 2014-05-22 19:56:24 »

Bearded Cow, yes, I think that link you posted is what I need. I'll get back to say if it works out or not.

All I was after was a method to calculate the tangent when I have limited information.

As for a picture to help, I don't really know what I could show, an algorithm, what kind of picture would you use to show that you're trying to find the method of calculating a tangent from, as I said before, limited information.
I have position, normal, and uv vectors, what do I do with them?
How can I manipulate them to come out with a tangent use values are easier to use, since I learned from Riven, that there are an infinite number of tangents.

Of course there are probably numerous ways I could explain myself better, but every now and then, my brain fails me

But, with that said, I think that link is sufficient.

bogieman987
 « Reply #12 - Posted 2014-05-25 14:45:19 »

Well, I managed to get the tangents, I'm pretty sure they're right, created a geometry shader so I could visualize them. And they all appear to be perpendicular to the normal.

But now there's the issue of making use of the values, when I try and implement the same method as shown in Opengl Super Bible 6th Edition, everything is black. Though after messing around, I think I accidentally made it so it uses the texture co-ordinates like Roquen suggested.

Should I stick with what I have, since it appears to work, or make it use the calculated tangents.

Roquen
 « Reply #13 - Posted 2014-05-25 14:55:35 »

You can compute tangents without extra information, but since as Riven pointed out, there are an infinite number of them to choose from you must use some "extra" information to make the coherent with each other.  Since you're almost insured to have texture coordinates available that's one possible choice.
Danny02
 « Reply #14 - Posted 2014-05-25 16:01:27 »

As roquen pointed out, you have to use the gradient of the texture coordinates to identify the direction of the tangent. You need to do this if you want your normal maps to work.

I dug out some code I used to do this task:
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19 `//the three position vectors creating a triangle        Vector3 pos1,  pos2, pos3;//the three texture coordinate vectors of the same three vertices        Vector2 uv1,  uv2, uv3;//the normal of the vector we want to calculate the tangent for (pos1, uv1 int this case)        Vector3 n;        Vector3 v2v1 = pos2.sub(pos1);        Vector3 v3v1 = pos3.sub(pos1);        float c2c1b = uv2.getY() - uv1.getY();        float c3c1b = uv3.getY() - uv1.getY();        Vector3 t = v2v1.mult(c3c1b).sub(v3v1.mult(c2c1b));        Vector3 b = n.cross(t);//the final tangent        Vector3 smoothTangent = b.cross(n).normalize();      `
bogieman987
 « Reply #15 - Posted 2014-05-26 16:36:03 »

Ok, so I think I've got the tangent calculation down. (Or not)

But upon rendering, well it doesn't look right.
It could be down to the tangents, or the shaders, I don't know.
On top of that, any books you guys could recommend for understanding glsl and lighting techniques and the math behind it?

Here's some images:
(Note the actual vector values for the light position, doesn't change)

(With normal texture coords scaled several times and small changes)

As you can see, not quite right, also, how can I smooth out the lighting, I assume it's because I have a tangent per vertex, and not per polygon.

Vertex
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59 `#version 430 corein vec4 in_Position;in vec2 in_TexCoord;in vec3 in_Normal;in vec3 in_Tangent;out VS_OUT {   vec2 pass_TexCoord;   vec3 eyeDir;   vec3 lightDir;} vs_out;uniform mat4 proj_matrix;uniform mat4 view_matrix;uniform mat4 model_matrix;uniform vec3 lightPos = vec3(5.0, 0.0, 10.0);void main(void) {      //  EDITED // "mv_matrix" is used in the book, I assume this is how it's calculated       // Didn't know that the order of matrix multiplication would change things, reversing the order, made       // the specular stop moving, one step closer to it looking better.   mat4 mv_matrix = view_matrix * model_matrix;   // Calculate vertex position in view space   vec4 P = mv_matrix * in_Position;      // Calculate normal (N) and tangent (T) vectors in view space from incoming   // object space vectors   vec3 N = normalize(mat3(mv_matrix) * in_Normal);   vec3 T = normalize(mat3(mv_matrix) * in_Tangent);   // Calculate the bitangent vector (B) from the normal and tangent vectors   vec3 B = cross(N, T);      // I think it was a little mistake, in the book, this part was calculate just above the   // "eyeDir" calculation   vec3 V = -P.xyz;      // The light vector (L) is the vector from the point of interest to the light.   // Calculate that and multiply it by the TBM matrix   vec3 L = lightPos - P.xyz;   vs_out.lightDir = normalize(vec3(dot(V, T), dot(V, B), dot(V, N)));      // The view vector is the vector from the point of interest to the viewer,   // which in view space is simply the negative of the position.   // Calculate that and multiply it by the TBN matrix   vs_out.eyeDir = normalize(vec3(dot(V, T), dot(V, B), dot(V, N)));      // Pass the texture coordinate through unmodified so that the fragment shader   // can fetch from the normal and color maps   vs_out.pass_TexCoord = in_TexCoord;      // Calculate clip coordinates by multiplying our view position by   // the projection matrix   //gl_Position = proj_matrix * P;      // The above "gl_Position" just brings back the refresh color without any geometry   gl_Position = proj_matrix * view_matrix * model_matrix * in_Position;}`

Fragment
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51 `#version 430 core// Texturesuniform sampler2D albedo_Tex;uniform sampler2D normal_Tex;uniform sampler2D reflect_Tex;uniform sampler2D micro_Tex;// Environment Mapuniform sampler2D envMap_Tex;in VS_OUT {   vec2 pass_TexCoord;   vec3 eyeDir;   vec3 lightDir;} fs_in;out vec4 out_Albedo;out vec4 out_Normal;out vec4 out_Reflect;out vec4 out_Micro;void main(void) {      // Normalize our incoming view and light direction vectors   vec3 V = normalize(fs_in.eyeDir);   vec3 L = normalize(fs_in.lightDir);   // EDITED // Read the normal from the normal map and normalize it        // Scaling the texture coords made the base normal map much easier to see, as it is a         // 2048x2048 normal map with 4 squares, and a crease in between each square.        // It now looks perfect on the cube, but not the sphere due to the noticeable triangles.        // How do I go about smoothing it out? Would I have to "smooth" it before the         // data is sent to opengl? Or can I do that with the shaders?   vec3 N = normalize(texture2D(normal_Tex, fs_in.pass_TexCoord * 4.0).rgb * 2.0 - vec3(1.0));   // Calculate R ready for use in Phong lighting   vec3 R = reflect(-L, N);      // Fetch the diffuse albedo from the texture   vec3 diffuse_albedo = texture2D(albedo_Tex, fs_in.pass_TexCoord).rgb;   // Calculate diffuse color with the simple N dot L   vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo;   // Uncomment this to turn off diffuse shading   // diffuse = vec3(0.0);      // Assume that specular albedo is white - it could also come from a texture   vec3 specular_albedo = vec3(1.0);   // Calculate Phong specular highlight   vec3 specular = max(pow(dot(R, V), 5.0), 0.0) * specular_albedo;   // Uncomment this to turn off specular highlights   // specular = vec3(0.0;      // Finale color is diffuse + specular   out_Albedo = vec4(diffuse + specular, 1.0);}`

This is pretty much what's in the book itself
Also, when implementing basic Phong shading using the books code, it looks the way it's supposed to, the sphere and dragon are smooth where as the Suzanne and the cube are not.

bogieman987
 « Reply #16 - Posted 2014-05-29 18:55:05 »

I've made some progress fixing the above issue.
Turned out there was just a minor type in my interpretation of Dannys snippet.

 1  2 `float c2c1b = uv2.getY() - uv1.getY();        float c3c1b = uv3.getY() - uv1.getY();`

Written as:
 1  2 `float c2c1b = uv2.getY() - uv1.getX();        float c3c1b = uv3.getY() - uv1.getY();`

So I fixed that and it looks much better but not perfect, where as the cube does.
Which I can kinda understand why. Only need to get it better on the rest.

So is there anything I'm missing, that could cause the sharpness seen?

Also, here's interpretation, just in case the fault is there.
 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39 `for(int i = 0; i < getVerts().length; i += 3) {         Vector3f v0 = getVerts()[i].getVectorXYZ();         Vector3f v1 = getVerts()[i + 1].getVectorXYZ();         Vector3f v2 = getVerts()[i + 2].getVectorXYZ();                  Vector2f uv0 = getVerts()[i].getVectorUV();         Vector2f uv1 = getVerts()[i + 1].getVectorUV();         Vector2f uv2 = getVerts()[i + 2].getVectorUV();                  Vector3f n = getVerts()[i].getVectorNXYZ();                  Vector3f deltaPos1 = new Vector3f();         Vector3f.sub(v1, v0, deltaPos1);         Vector3f deltaPos2 = new Vector3f();         Vector3f.sub(v2, v0, deltaPos2);                  float deltaUv1 = uv1.getY() - uv0.getY();         float deltaUv2 = uv2.getY() - uv0.getY();                  // Tangent         float tx = (deltaPos1.getX() * deltaUv2) - (deltaPos2.getX() * deltaUv1);         float ty = (deltaPos1.getY() * deltaUv2) - (deltaPos2.getY() * deltaUv1);         float tz = (deltaPos1.getZ() * deltaUv2) - (deltaPos2.getZ() * deltaUv1);         Vector3f t = new Vector3f(tx, ty, tz);                  // Bitangent         Vector3f b = new Vector3f();         Vector3f.cross(n, t, b);                  // Final tangent         Vector3f smoothTangent = new Vector3f();         Vector3f.cross(b, n, smoothTangent);         smoothTangent.normalise();                                // These are where I store all the vertex data, so it's one tangent per vertex.         getVerts()[i].setTXYZ(smoothTangent);         getVerts()[i + 1].setTXYZ(smoothTangent);         getVerts()[i + 2].setTXYZ(smoothTangent);      }`

Roquen
 « Reply #17 - Posted 2014-05-30 08:55:00 »

Just to be the monkey tossing wrenches around:
http://www.thetenthplanet.de/archives/1180#more-1180
Danny02
 « Reply #18 - Posted 2014-05-30 12:02:03 »

you only calculate one tangent per triangle, but you have to calculate it for each vertex of every triangle.

lets say that my code is a function which takes three vertices or indices into your vertex array. you would have to call it three times for each trinalge.

 1  2  3  4  5  6 `Vector3f calcTangent(int v1, int v2, int v3);for(int i = 0; i < getVerts().length; i += 3) {getVerts()[i].setTXYZ(calcTangent(i, i + 1, i +2);getVerts()[i+1].setTXYZ(calcTangent(i+1, i + 2, i);getVerts()[i+2].setTXYZ(calcTangent(i+2, i, i +1);}`
bogieman987
 « Reply #19 - Posted 2014-05-30 12:43:01 »

lol I'm confused.

So let me get this straight.

I don't need to pre-calculate on launch, I can do it in the shaders by "perturbing the normal" using the "co-tangent frame"?
So essentially is that getting the tangent from using uvs? Like what you stated before.

And from what I can tell by the shaders, I just need to feed the shader a normal, view vector and texture co-ordinates in order to get the tangent, right?

Danny02
 « Reply #20 - Posted 2014-05-30 13:03:35 »

Yes you can do all the work in the shader, but as you can see this requires a lot of math and will slow down the shader considerably.
You will have to decide for yourself which way is easier for you to support and use.
Roquen
 « Reply #21 - Posted 2014-05-30 13:19:45 »

Sorry.  I probably shouldn't have done that.  Trade off:  GPU time vs. moving memory around (well and engineering time as well).
bogieman987
 « Reply #22 - Posted 2014-05-30 13:43:19 »

Ah right, ok, I think Ill pre-compute it instead of calculating it on the GPU.

bogieman987
 « Reply #23 - Posted 2014-05-30 20:29:11 »

Thanks guys, got it working.
I'll definitely keep in mind doing the calculation on the GPU, but overall, I went with pre-computing it first.

Pages: [1]
 ignore  |  Print

You cannot reply to this message, because it is very, very old.

 theagentd (13 views) 2015-03-27 23:08:20 wxwsk8er (54 views) 2015-03-20 15:39:46 Fairy Tailz (47 views) 2015-03-15 21:52:20 Olo (29 views) 2015-03-13 17:51:59 Olo (31 views) 2015-03-13 17:50:51 Olo (39 views) 2015-03-13 17:50:16 Olo (44 views) 2015-03-13 17:47:07 ClaasJG (57 views) 2015-03-10 11:36:42 ClaasJG (42 views) 2015-03-10 11:33:01 Pippogeek (50 views) 2015-03-05 14:36:23
 BurntPizza 21x LiquidNitrogen 21x basil_ 19x EgonOlsen 17x theagentd 17x KevinWorkman 16x Roquen 16x wessles 11x 65K 11x Varkas 10x Riven 10x princec 9x phu004 8x SHC 8x Ashedragon 8x nsigma 7x
 How to: JGO Wikiby Mac702015-02-17 20:56:162D Dynamic Lighting2015-01-01 20:25:42How do I start Java Game Development?by gouessej2014-12-27 19:41:21Resources for WIP gamesby kpars2014-12-18 10:26:14Understanding relations between setOrigin, setScale and setPosition in libGdx2014-10-09 22:35:00Definite guide to supporting multiple device resolutions on Android (2014)2014-10-02 22:36:02List of Learning Resources2014-08-16 10:40:00List of Learning Resources2014-08-05 19:33:27
 java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org