I'd like to write a fragment shader that stretches a tetragonal shape in a texture to a square shaped game object of mine. My problem is best described with a picture, seen below. The shader receives a texture and the four corners of the red tetragon, and I'd like to stretch the tetragon as such that that's the only thing that'll appear on my square object, nothing outside of it. In this example, the texture coordinates u and v (0, 0) would be transformed into (x1, y1), and in a similar fashion, (1, 1) would become (x4, y4).

enter image description here

I don't know how I should go about this though. I've done my research, I believe bilinear interpolation could be what I'm looking for, but from what I can see, it would only work if my red tetragon was a rectangle, in other words if its four corners where (x1, y1), (x1, x2), (x2, y1) and (x2, y2), which is clearly not the case here as all my four coordinates are different. Any help from a little hint to a complete code solution would be welcome.

Edit: I did try implementing bilinear interpolation, however it did not work. Here are the equations from the wikipedia article that I tried modifying to my specific case:

R1 R2 fxy

What R1 and R2 correspond to can be also seen on the image on wikipedia, my math notation may not entirely be correct but I tried to make it as understandable as possible. This did not give me the result I was looking for, however. I tried setting the red tetragon to be a triangle by setting (x1, y1) to (0.5, 0.5) while the remaining three coordinates went in their corresponding corners), and while I was expecting the texture to be stretched in some way, the end result still was a crystal clear, non-stretched picture (due to my testing circumstances, I can't tell the exact difference to when I set (x1, y1) to (0, 0), but the results were extremely similar).

Edit 2: I'll also include the code from my fragment shader, for good measure.

in vec4 tex;
in vec3 normal;
out vec4 fragmentColor;

uniform struct {
    sampler2D colorTexture;
    vec2 p1;
    vec2 p2;
    vec2 p3;
    vec2 p4;
} material;

void main(void) {
    float x = tex.x / tex.w;
    float y = tex.y / tex.w;
    float x1 = material.p1.x;
    float x2 = material.p2.x;
    float x3 = material.p3.x;
    float x4 = material.p4.x;
    float y1 = material.p1.y;
    float y2 = material.p2.y;
    float y3 = material.p3.y;
    float y4 = material.p4.y;
    vec2 R1 = (x3 - x) / (x3 - x1) * material.p1 + (x - x1) / (x3 - x1) * material.p3;
    vec2 R2 = (x4 - x) / (x4 - x2) * material.p2 + (x - x2) / (x4 - x2) * material.p4;

    vec2 interpolatedPoint = (R2.y - y) / (R2.y - R1.y) * R1 + (y - R1.y) / (R2.y - R1.y) * R2;

    fragmentColor = texture(material.colorTexture, interpolatedPoint);

Here's what I see when I set p1 to (0, 0), p2 to (0, 1), p3 to (1, 0) and p4 to (1, 1)


And here's the same image but with p1 set to (0.5, 0.5)


The slight differences are only there because I had to position my camera manually, the two results are identical, even though in the second case I would expect to see an image that's clearly been stretched in some way.


1 Answer 1


Assuming your x & y coordinates go from (0, 0) in the bottom-left to (1, 1) in the top-right, you should be able to simplify to something like this:

void main(void) {
    float x = tex.x / tex.w;
    float y = tex.y / tex.w;

    vec2 top = mix(material.p2, material.p4, x);
    vec2 bottom = mix(material.p1, material.p3, x);

    vec2 samplePoint = mix(bottom, top, y);

    fragmentColor = texture(material.colorTexture, interpolatedPoint);
  • \$\begingroup\$ I wish I had known of the mix function, could've made my life a lot easier. I learned something new today at least, it seems to be working now! Thank you very much:) \$\endgroup\$
    – Bobobot
    Commented Apr 22, 2020 at 18:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .