texture-space to object-space

I need to go from texture space to object space.

Having a triangle with vertices p0, p1, p2 and texture coordinates t0, t1, t2, i’m constructing a 3x3 matrix ‘A’ as:

| v1.x v2.x n.x |
| v1.y v2.y n.y |
| v1.z v2.z n.z |

where v1 and v2 are the vectos lying on the 2 sides of the triangle:

v1 = p0 - p1
v2 = p2 - p1

and n is the normal:

n = |v1 x v2| (normal)

This matrix would go from “triangle-space” to object space.

Then I’m constructing another matrix (‘B’) as:

| uv1.x uv2.x 0 |
| uv1.y uv2.y 0 |
| uv1.z uv2.z 1 |

Where uv1 is t0-t1 and uv2 is t2-t1 (as before).

This matrix would go from “texture-space” to triangle-space

Finally to convert from texture-space to object-space I’m calculating the final matrix as:

m = B * A

But it doesn’t work. What am I doing wrong?

Any help will be appreciated.

As far as I see you want a tangent space basis matrix.
Instead of looking for the error in your code, I’ll provide some links with working code.

Thanks, but I want to understand what I’m doing, and not borrow someone else’s code/method.

Ok, but take in account that links provided contain the extensive explanation, not just plain working code.

As far as I see, your B matrix transforms from triangle space to tangent space (inverse of what you are saying). So, assuming that you put the vector to the left of a matrix when multiplying, you need the following matrix in the result:
m = inv(B) * A

I’ve seen the links, but after posting I realized that for what I want to do what I need is the full transformation (including translation). I need texture points (W=1) in space (and vice-versa), no just vectors (W=0) for tangent-space lighting.

Thus I expanded A to

| v1.x v2.x n.x 0 |
| v1.y v2.y n.y 0 |
| v1.z v2.z n.z 0 |
| p1.x p1.y p1.z 1 |

and B to

| uv1.x uv2.x 0 0 |
| uv1.y uv2.y 0 0 |
| uv1.z uv2.z 1 0 |
| t1.x t1.y t1.z 1 |

Even so, (also with inv(B)) I cannot make it work. I cannot grasp why. After all is two consecutive linear transformations: From texture-space to triangle-space and from triangle-space to object-space…

You are not correct.

==A==:
transform from triangle space to object space:
v’ = v.x * v1 + v.y * v2 + v.z * n + p1

So, your matrix should have p1 on the right column instead of bottom row:
| v1.x v2.x n.x p1.x |
| v1.y v2.y n.y p1.y |
| v1.z v2.z n.z p1.z |
| 0 0 0 1 |

==B==:
the same mistake, should be:
| uv1.x uv2.x 0 t1.x |
| uv1.y uv2.y 0 t1.y |
| uv1.z uv2.z 1 t1.z |
| 0 0 0 1 |

You are right. My mistake.
Is wrong in the post, but done right in the code:

c#:

Matrix4f A = new Matrix4f(v0, v1, n, p0);

public Matrix4f(Vector3f xaxis, Vector3f yaxis, Vector3f zaxis, Vector3f pos)
{
m00 = xaxis.X;
m01 = xaxis.Y;
m02 = xaxis.Z;
m03 = 0.0f;
m10 = yaxis.X;
m11 = yaxis.Y;
m12 = yaxis.Z;
m13 = 0.0f;
m20 = zaxis.X;
m21 = zaxis.Y;
m22 = zaxis.Z;
m23 = 0.0f;
m30 = pos.X;
m31 = pos.Y;
m32 = pos.Z;
m33 = 1.0f;
}

I hope you have a correct matrix * vector multiplication.
How do you test the correctness of the tangental space basis?

You should try to use the working equations for the start, just to be sure everything else works correctly.

DmitryM has the solution:

==A==:
| v1.x v2.x n.x p1.x |
| v1.y v2.y n.y p1.y |
| v1.z v2.z n.z p1.z |
| 0 0 0 1 |

==B==:
| uv1.x uv2.x 0 t1.x |
| uv1.y uv2.y 0 t1.y |
| uv1.z uv2.z 1 t1.z |
| 0 0 0 1 |

Those definitions explicitly mean:

``````
object_coord=A*Triangle_coord
Textre_coord=B*Triangle_coord

``````

so you can solve for Triangle_coord

``````
Triangle_coord=Inverse(B)*Textre_coord

``````

hence

``````
object_coord=A*Triangle_coord
=A*Inverse(B)*Textre_coord

``````

Note the order of operations though!

This is craving my brain. It’s a small test app in C# using OpenTK:

private void myGLControl1_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

``````	// Draw triangle with texture

GL.Color3(1.0f, 1.0f, 1.0f);
GL.Enable(EnableCap.Texture2D);
tex.Bind();
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);

GL.Begin(BeginMode.Triangles);

GL.TexCoord2(t0.X, t0.Y);
GL.Vertex3(p0.X, p0.Y, p0.Z);
GL.TexCoord2(t1.X, t1.Y);
GL.Vertex3(p1.X, p1.Y, p1.Z);
GL.TexCoord2(t2.X, t2.Y);
GL.Vertex3(p2.X, p2.Y, p2.Z);

GL.End();

// Draw triangle in wireframe

GL.Color3(1.0f, 1.0f, 0.0f);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
GL.LineWidth(3);
GL.Disable(EnableCap.Texture2D);

GL.Begin(BeginMode.Triangles);

GL.Vertex3(p0.X, p0.Y, p0.Z);
GL.Vertex3(p1.X, p1.Y, p1.Z);
GL.Vertex3(p2.X, p2.Y, p2.Z);

GL.End();

// Compute triangle basis

Vector3f v0 = p1 - p0;
Vector3f v1 = p2 - p0;
Vector3f n = Vector3f.CrossProduct(v0, v1);
//n.Normalize(); // not really needed

Matrix4f A = new Matrix4f(v0, v1, n, p0);

// Point to locate tangent and bitangent origin somewhere outside the triangle

Vector3f temp = A.TransformPoint(new Vector3f(-0.1f, -0.1f, 0.0f));

GL.PointSize(4);
GL.Color3(1.0f, 0.0f, 1.0f);
GL.Begin(BeginMode.Points);
GL.Vertex3(temp.X, temp.Y, temp.Z);
GL.End();

// Texture basis

Vector3f uv0 = t1 - t0;
Vector3f uv1 = t2 - t0;
Vector3f uvn = Vector3f.CrossProduct(uv0, uv1);

Matrix4f B = new Matrix4f(uv0, uv1, uvn);
B.Invert();
//Matrix4f C = A * B;
Matrix4f C = B * A;

// Draw tangent vector

Vector3f tan = C.TransformVector(new Vector3f(1.0f, 0.0f, 0.0f)) * 0.5f;

GL.Color3(1.0f, 0.0f, 0.0f);
GL.Begin(BeginMode.Lines);
GL.Vertex3(temp.X, temp.Y, temp.Z);
GL.Vertex3(temp.X+tan.X, temp.Y+tan.Y, temp.Z+tan.Z);
GL.End();

// Draw bitangent (or binormal)

Vector3f bitan = C.TransformVector(new Vector3f(0.0f, 1.0f, 0.0f)) * 0.5f;

GL.Color3(0.0f, 1.0f, 0.0f);
GL.Begin(BeginMode.Lines);
GL.Vertex3(temp.X, temp.Y, temp.Z);
GL.Vertex3(temp.X + bitan.X, temp.Y + bitan.Y, temp.Z + bitan.Z);
GL.End();

// Overimpose full texture quad over the triangle

Vector3f c0 = C.TransformPoint(new Vector3f(0, 0, 0));
Vector3f c1 = C.TransformPoint(new Vector3f(1, 0, 0));
Vector3f c2 = C.TransformPoint(new Vector3f(1, 1, 0));
Vector3f c3 = C.TransformPoint(new Vector3f(0, 1, 0));

GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.Color4(1.0f, 1.0f, 1.0f, 0.5f);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
GL.Enable(EnableCap.Texture2D);

GL.Begin(BeginMode.Quads);

GL.TexCoord2(0, 0);
GL.Vertex3(c0.X, c0.Y, c0.Z);
GL.TexCoord2(1, 0);
GL.Vertex3(c1.X, c1.Y, c1.Z);
GL.TexCoord2(1, 1);
GL.Vertex3(c2.X, c2.Y, c2.Z);
GL.TexCoord2(0, 1);
GL.Vertex3(c3.X, c3.Y, c3.Z);

GL.End();

GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.Texture2D);

myGLControl1.SwapBuffers();
}
``````

It works perfectly as long as B is identity i.e. tex coords (0,0) (1,0) (0,1)

Matrix multiplication, inversion et al had all been proved good with hierarchical transforms, camera transform (inverse of normal transform) and skinning

Got it!

Matrix4f B = new Matrix4f(uv0, uv1, uvn);

Should have been:

Matrix4f B = new Matrix4f(uv0, uv1, uvn, t0);

Thank you guys!!!

quick question, which one worked?

``````
Matrix4f C = A * Inverse(B); ?

or

Matrix4f C = Inverse(B) * A; ?

``````

Well, I it will ever depend on how matrix multiplication was written. In my case the latter. For a standard “scale, rotate, translate” I write M = S * R * T

You both where right. Thanks again.