Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (481)
Games in Android Showcase (110)
games submitted by our members
Games in WIP (548)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  SOLVED Calculating Tangent from Normal  (Read 3966 times)
0 Members and 1 Guest are viewing this topic.
Offline 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.000000
v 1.000000 0.000000 1.000000
v -1.000000 0.000000 -1.000000
v 1.000000 0.504169 -1.000000

vt 0.943613 0.001434
vt 0.999903 0.941847
vt 0.057807 0.941847
vt 0.000097 0.000097

vn 0.003003 0.999991 -0.003003
vn -0.237447 0.941933 0.237447
vn 0.242680 0.939262 -0.242680

f 2/1/1 4/2/2 3/3/1
f 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  
// Position
v 1.000000 0.000000 1.000000
v 1.000000 0.504169 -1.000000
v -1.000000 0.000000 -1.000000
// UV
vt 0.943613 0.001434
vt 0.999903 0.941847
vt 0.057807 0.941847
// Normal
vn 0.003003 0.999991 -0.003003
vn -0.237447 0.941933 0.237447
vn 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.

Offline 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"
Offline 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.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online Riven
« League of Dukes »

JGO Overlord


Medals: 781
Projects: 4
Exp: 16 years


Hand over your head.


« 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
Offline 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?

Offline Bearded Cow

Senior Member


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.
Offline 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.


Offline Bearded Cow

Senior Member


Medals: 2
Projects: 1
Exp: 1 year


¬..¬


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

Do you understand what normal and tangents are??
Offline 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.

Offline Bearded Cow

Senior Member


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.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Online Roquen
« Reply #10 - Posted 2014-05-22 18:47:51 »

You use your texture coordinates.
Offline 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  Smiley

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

Offline 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.


Online 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.
Offline 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();      
Offline 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.

The shaders:
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 core

in 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
// Textures
uniform sampler2D albedo_Tex;
uniform sampler2D normal_Tex;
uniform sampler2D reflect_Tex;
uniform sampler2D micro_Tex;
// Environment Map
uniform 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.

Offline 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.

As I had this:
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);
      }

Online 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
Offline 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);
}
Offline bogieman987
« Reply #19 - Posted 2014-05-30 12:43:01 »

lol I'm confused.

So let me get this straight.

From that link. http://www.thetenthplanet.de/archives/1180#more-1180
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?

Offline 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.
Online 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).
Offline 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.

Offline 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  
 
 

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

atombrot (27 views)
2014-08-19 09:29:53

Tekkerue (25 views)
2014-08-16 06:45:27

Tekkerue (23 views)
2014-08-16 06:22:17

Tekkerue (15 views)
2014-08-16 06:20:21

Tekkerue (22 views)
2014-08-16 06:12:11

Rayexar (61 views)
2014-08-11 02:49:23

BurntPizza (39 views)
2014-08-09 21:09:32

BurntPizza (31 views)
2014-08-08 02:01:56

Norakomi (37 views)
2014-08-06 19:49:38

BurntPizza (67 views)
2014-08-03 02:57:17
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!