2

I have a triangle, each point of which is defined by a position (X,Y,Z) and a UV coordinate (U,V):

struct Vertex
{
    Vector mPos;
    Point mUV;
    inline Vector& ToVector() {return mPos;}
    inline Vector& ToUV() {return mUV;}
};

With this function, I am able to get the UV coordinate at a specific XYZ position:

Point Math3D::TriangleXYZToUV(Vector thePos, Vertex* theTriangle)
{
    Vector aTr1=theTriangle->ToVector()-(theTriangle+1)->ToVector();
    Vector aTr2=theTriangle->ToVector()-(theTriangle+2)->ToVector();
    Vector aF1 = theTriangle->ToVector()-thePos;
    Vector aF2 = (theTriangle+1)->ToVector()-thePos;
    Vector aF3 = (theTriangle+2)->ToVector()-thePos;
    float aA=aTr1.Cross(aTr2).Length();
    float aA1=aF2.Cross(aF3).Length()/aA;
    float aA2=aF3.Cross(aF1).Length()/aA;
    float aA3=aF1.Cross(aF2).Length()/aA;
    Point aUV=(theTriangle->ToUV()*aA1)+((theTriangle+1)->ToUV()*aA2)+((theTriangle+2)->ToUV()*aA3);
    return aUV;
}

I attempted to reverse-engineer this to make a function that gets the XYZ coordinate from a specific UV position:

Vector Math3D::TriangleUVToXYZ(Point theUV, Vertex* theTriangle)
{
    Point aTr1=theTriangle->ToUV()-(theTriangle+1)->ToUV();
    Point aTr2=theTriangle->ToUV()-(theTriangle+2)->ToUV();
    Point aF1 = theTriangle->ToUV()-theUV;
    Point aF2 = (theTriangle+1)->ToUV()-theUV;
    Point aF3 = (theTriangle+2)->ToUV()-theUV;
    float aA=gMath.Abs(aTr1.Cross(aTr2)); // NOTE: Point::Cross looks like this: const float Cross(const Point &thePoint) const {return mX*thePoint.mY-mY*thePoint.mX;}
    float aA1=aF2.Cross(aF3)/aA;
    float aA2=aF3.Cross(aF1)/aA;
    float aA3=aF1.Cross(aF2)/aA;
    Vector aXYZ=(theTriangle->ToVector()*aA1)+((theTriangle+1)->ToVector()*aA2)+((theTriangle+2)->ToVector()*aA3);
    return aXYZ;
}

This works MOST of the time. However, it seems to exponentially "approach" the right-angled corner of the triangle-- or something. I'm not really sure what's going on except that the result gets wildly inaccurate the closer it gets to the right-angle.

What do I need to do to this TriangleUVtoXYZ function to make it return accurate results?

KiraHoneybee
  • 495
  • 3
  • 12
  • Are you sure the 2nd snippet of code is what youre running? The cross product of 2 vectors should be another vector but you assign it to a float. Is it missing .Length()? I'm also curious why the extra gMath.Abs there (everything else seems to be exactly the first snipper with ToVector swapped for ToUV) – Borgleader May 12 '22 at 01:30
  • My point class (which I got from another programmer) returns a cross product that is a float. Since I've never actually used it before this on a point (as opposed to a vector), I've never thought about it twice. – KiraHoneybee May 12 '22 at 01:35
  • ...After quickly converting it to a vector with z=0 and taking the cross.length there, I get the same result. It looks like my point class just returns the length instead of a cross vector for whatever reason the guy who wrote it decided. – KiraHoneybee May 12 '22 at 01:39
  • If you still have questions, please [edit] and update `Vector` and `Point`'s implementation to prevent confusion. – Louis Go May 12 '22 at 01:45
  • @LouisGo Clarified that the Point class returns the magnitude of the cross product, not the cross product itself. – KiraHoneybee May 12 '22 at 01:49
  • You're not actually inverting the function, you just swapped some `toVector()` for `toUV()`. Look at it like this - a delta of UV space along the U direction maps to a delta of the triangle from the right corner to point 1. In the V direction from the right corner to point 2. Translate the right corner to zero, project the U and V coordinates independently onto the (0 -> (point 1 - point 0)) and (0 -> (point 2 - point 0)) vectors independently, then just add the two (XYZ-space) vectors together (adding back point 0 after, of course). It's way easier than the first function from XYZ to UV. – BadZen May 12 '22 at 03:28
  • `xyz = point0 + (point1-point0) *u + (point2-point0) *v` (perhaps similar to BadZen comment) – MBo May 12 '22 at 04:21

1 Answers1

1

I haven't tested your implementation, but you only need to compute two parametric coordinates - the third being redundant since they should sum to 1.

Vector Math3D::TriangleUVToXYZ(Point theUV, Vertex* theTriangle)
{
    // T2-T1, T3-T1, P-T1
    Point aTr12 = theTriangle[1].ToUV() - theTriangle[0].ToUV();
    Point aTr13 = theTriangle[2].ToUV() - theTriangle[0].ToUV();
    Point aP1 = theUV - theTriangle[0].ToUV();
    
    // don't need Abs() for the denominator
    float aA23 = aTr12.Cross(aTr13);
    
    // parametric coordinates [s,t]
    // s = (P-T1)x(T2-T1) / (T3-T1)x(T2-T1)
    // t = (P-T1)x(T3-T1) / (T2-T1)x(T3-T1)
    float aA12 = aP1.Cross(aTr12) / -aA23;
    float aA13 = aP1.Cross(aTr13) /  aA23;
    
    // XYZ = V1 + s(V2-V1) + t(V3-V1)
    return theTriangle[0].ToVector()
       + aA12 * (theTriangle[1].ToVector() - theTriangle[0].ToVector())
       + aA13 * (theTriangle[2].ToVector() - theTriangle[0].ToVector());
}
meowgoesthedog
  • 14,670
  • 4
  • 27
  • 40