Question

How do you calculate UV coordinates for points on a plane?

I have a polygon - 3 or 4 or more points - that is on a plane - that is to say, all the points are on a plane. But it can be at any angle in space.

One side of this polygon - two points - are to be mapped to two corresponding 2D points in a texture - I know these two points in advance. I also know the x and y scale for the texture, and that no points fall outside the texture extent or other 'edge cases'.

Here's an image where the up-most textured quad is distorted:

enter image description here

I outlined a bad quad in yellow. Imagine that I know the UV coordinates of those two bottom-most corners on that quad, and want to calculate the proper UV coordinates of the other two points...

How do you calculate the UV coordinates of all the other points in the plane relative to these two points?

Imagine my texture is a piece of paper in real life, and I want to texture your (flat) car door. I place two dots on my paper, which I line up with two dots on your car door. How do I calculate where the other locations on the car door are under the paper?

Can you use trilateration? What would the pseudo-code look like for two known points in 2D space?


Success using brainjam's code:

def set_texture(self,texture,a_ofs,a,b):
    self.texture = texture
    self.colour = (1,1,1)
    self.texture_coords = tx = []
    A, B = self.m[a_ofs:a_ofs+2]
    for P in self.m:
        if P == A:
            tx.append(a)
        elif P == B:
            tx.append(b)
        else:
            scale = P.distance(A)/B.distance(A)
            theta = (P-A).dot((B-A)/(P.distance(A)*B.distance(A)))
            theta = math.acos(theta)
            x, y = b[0]-a[0], b[1]-a[1]
            x, y = x*math.cos(theta) - y*math.sin(theta), \
                x*math.sin(theta) + y*math.cos(theta)
            x, y = a[0]+ x*scale, a[1]+ y*scale
            tx.append((x,y))

enter image description here

Was it helpful?

Solution

Label the vertices of your 3D polygon in counter-clockwise order, starting with the two vertices whose UV coordinates are known. Call these labels A, B, C, D. The labels of the corresponding vertices in UV space are a, b, c, d, where a and b are known.

The problem you've stated is, for a point P in the original polygon, to determine the corresponding UV coordinate p. (I believe that you only care about calculating the UV coordinates c and d for the points C and D, but the general solution for P is the same.)

First, calculate the angle θ between P-A and B-A. This is easily done using the dot product of the normalized vectors, and acos.

α = (P-A)⋅(B-A)/(|P-A||B-A|)

θ = acos(α)

We also calculate the ratio of the lengths:

σ = |P-A|/|B-A|

Now to calculate p in UV space, we simply rotate the vector b-a by angle θ (keeping a fixed) and scale by σ.

Let R, the matrix for a rotation by angle θ, be

| +cos(θ) -sin(θ) |
| +sin(θ) +cos(θ) |

Then p = a + σR( b-a).

And you're done.

OTHER TIPS

You have to express the others points in terms of two chosen vectors and an origin.

I would do something like this :

Choose 3 3D points with corresponding UV points :

  • A(x,y,z,u,v)
  • B(x,y,z,u,v)
  • C(x,y,z,u,v)

Then using the x,y,z coords, we want to express a given 3D point D as :

D = A + alpha ( B - A ) + beta ( C - A ) + gamma ( B - A ) X ( C - A )

We have 3 equations for x,y,z, X is cross product, and alpha,beta,gamma are unknown. We want this to create a linear relation between uv and xyz.

Compute W = ( B - A ) X ( C - A ), we need to solve :

Dx - Ax = alpha.(Bx-Ax) + beta.(Cx-Ax) + gamma.Wx

Dy - Ay = alpha.(By-Ay) + beta.(Cy-Ay) + gamma.Wy

Dz - Az = alpha.(Bz-Az) + beta.(Cz-Az) + gamma.Wz

Compute the inverse matrix of matrix M with this method :

       | (Bx-Ax) , Cx-Ax , Wx | 
   M = | (By-Ay) , Cy-Ay , Wy | 
       | (Bz-Az) , Cz-Az , Wz | 

We call the result matrix N, note it does not depend on D.

Then compute alpha,beta,gamma for D by :

(alpha,beta,gamma) = N.(D-A)

Then compute u,v for D by :

Du = Au + alpha( Bu - Au ) + beta( Cu - Au )

Dv = Av + alpha( Bv - Av ) + beta( Cv - Av )

gamma is not used as it's a distance between D and the (A,B,C) 3D plane.

U and V are numbers between 0 and 1.

So, say in your situation the size of the larger edge is 10, and the smaller edge is 5, each "gap" is 2.5. This is then normalized to give you a digit that is needed.

so some example pseudo code:

bottomLeftVector(0,0,0)
bottomLeftTexture(0,0)
topLeftVector(2.5, 5, 0)
topLeftTexture(0.25, 0)
topRightVector(7.5, 5, 0)
topRightTexture(0, 0.75)
bottomRightVector(10, 0, 0)
bottomRightTexture(1,1)

Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top