1

I want to map a 2d picture onto an arbitrary 2d quadrilateral, for the purpose of particle system. To do that, I split the picture into two triangles, and then linearly interpolated u,v along the triangle edges, using the following code:

struct tlerp {
  double x,i;
  double u,v;
  double ui,vi;
} tlerp;

void tlerp_init(tlerp *l
                      , double first_step, double steps
                      , double sx, double ex
                      , double su, double eu
                      , double sv, double ev) {
  l->i  = (ex - sx) / steps;
  l->ui = (eu - su) / steps;
  l->vi = (ev - sv) / steps;
  l->x = sx + first_step*l->i;
  l->u = su + first_step*l->ui;
  l->v = sv + first_step*l->vi;
}

void tlerp_advance(tlerp *l) {
  l->x += l->i;
  l->u += l->ui;
  l->v += l->vi;
}

But the result is garbled. My guess is that linear interpolation can't be used here, because the space inside quadrilateral is obviously non-linear (on the attached picture it gets more compressed the closer it gets to the top). Is there some way to make it linear again?

As always, I'm not using an OpenGL/DirectX, so can't use any built-in solution.

Problem

1 Answers1

1

I've solved the problem by following DMGregory's suggestion to introduce this imaginary Z, which I calculated based on distances between quad vertices. Here is a basic C code, because many people seems struggle getting it right, it is like most common pitfall of 2d and 3d graphics:

//usual euclidean distance
float distance(int ax, int ay, int bx, int by) {
  int x = ax-bx;
  int y = ay-by;
  return sqrtf((float)(x*x + y*y));
}

void gfx_quad(gfx_t *dst //destination texture, we are rendering into
             ,gfx_t *src //source texture
             ,int *quad  // quadrilateral vertices
             )
{
  int *v = quad; //quad vertices
  float z = 20.0;
  float top = distance(v[0],v[1],v[2],v[3]); //top
  float bot = distance(v[4],v[5],v[6],v[7]); //bottom
  float lft = distance(v[0],v[1],v[4],v[5]); //left
  float rgt = distance(v[2],v[3],v[6],v[7]); //right

  // By default all vertices lie on the screen plane
  float az = 1.0;
  float bz = 1.0;
  float cz = 1.0;
  float dz = 1.0;

  // Move Z from screen, if based on distance ratios.
  if (top<bot) {
    az *= top/bot;
    bz *= top/bot;
  } else {
    cz *= bot/top;
    dz *= bot/top;
  }

  if (lft<rgt) {
    az *= lft/rgt;
    cz *= lft/rgt;
  } else {
    bz *= rgt/lft;
    dz *= rgt/lft;
  }

  // draw our quad as two textured triangles
  gfx_textured(dst, src
              , v[0],v[1],az, v[2],v[3],bz, v[4],v[5],cz
              , 0.0,0.0,      1.0,0.0,      0.0,1.0);
  gfx_textured(dst, src
              , v[2],v[3],bz, v[4],v[5],cz, v[6],v[7],dz
              , 1.0,0.0,      0.0,1.0,      1.0,1.0);
}