<p>I am working on a small rendering engine for a personal project and I am having issues with the texture mapping part of it.</p>

<p>It seems to work for some cases, but not for others. For example, when one of the vertices is behind the camera, the texture is stretched.</p>

<p><em>Seemingly</em> <strong>Correct case</strong>
<a href="https://i.sstatic.net/IJtuU.png" rel="noreferrer"><img src="https://i.sstatic.net/IJtuU.png" alt="Seemingly correct case"></a>
<strong>Incorrect case</strong>
<a href="https://i.sstatic.net/5M70C.png" rel="noreferrer"><img src="https://i.sstatic.net/5M70C.png" alt="Incorrect case"></a></p>

<p>I am guessing that it has something to do with the texture mapping not being perspective-correct. I have tried various changes mainly involving the z-distance to the camera, but I could not find any quick fix to my code.</p>

<p>Here is my code for the perspective projection:</p>

<pre><code>public double[] project(double x, double y, double z) {
    double tx = x - camera.x;
    double ty = z - camera.z;
    double tz = y - camera.y;

    double cx = Math.cos(camera.pitch);
    double cy = Math.cos(camera.yaw);
    double cz = Math.cos(camera.roll);

    double sx = Math.sin(camera.pitch);
    double sy = Math.sin(camera.yaw);
    double sz = Math.sin(camera.roll);

    double dx = cy * (sz * ty + cz * tx) - sy * tz;
    double dy = sx * (cy * tz + sy * (sz * ty + cz * tx)) + cx * (cz * ty - sz * tx);
    double dz = cx * (cy * tz + sy * (sz * ty + cz * tx)) - sx * (cz * ty - sz * tx);

    double ez = 1.0 / Math.tan(FOV / 2.0);

    double bx = ez / dz * dx;
    double by = ez / dz * dy;

    if (dz &lt; 0.0) {
        bx = -bx;
        by = -by;
    }

    int px = (int) (width + bx * height) / 2;
    int py = (int) (height + by * height) / 2;

    return new double[] { px, py, dz };
}
</code></pre>

<p>and here my code for the texture mapping:</p>

<pre><code>public double[] map(double x, double y, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) {
    double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
    double B = ((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3) + (x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2)) / 2.0;
    double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);

    double det = A - 2.0 * B + C;

    double u;
    if (det == 0.0) {
        u = A / (A - C);
        if (Double.isNaN(u) || u &lt; 0.0 || u &gt; 1.0)
            return null;
    } else {
        double u1 = ((A - B) + Math.sqrt(B * B - A * C)) / det;
        boolean u1valid = !Double.isNaN(u1) &amp;&amp; u1 &gt;= 0.0 &amp;&amp; 1.0 &gt;= u1;

        double u2 = ((A - B) - Math.sqrt(B * B - A * C)) / det;
        boolean u2valid = !Double.isNaN(u2) &amp;&amp; u2 &gt;= 0.0 &amp;&amp; 1.0 &gt;= u2;

        if (u1valid &amp;&amp; u2valid)
            u = u1 &lt; u2 ? u2 : u1;
        else if (u1valid)
            u = u1;
        else if (u2valid)
            u = u2;
        else
            return null;
    }

    double v1 = ((1.0 - u) * (x0 - x) + u * (x1 - x)) / ((1.0 - u) * (x0 - x2) + u * (x1 - x3));
    boolean v1valid = !Double.isNaN(v1) &amp;&amp; v1 &gt;= 0.0 &amp;&amp; 1.0 &gt;= v1;

    double v2 = ((1.0 - u) * (y0 - y) + u * (y1 - y)) / ((1.0 - u) * (y0 - y2) + u * (y1 - y3));
    boolean v2valid = !Double.isNaN(v2) &amp;&amp; v2 &gt;= 0.0 &amp;&amp; 1.0 &gt;= v2;

    double v;
    if (v1valid &amp;&amp; v2valid)
        v = v1 &lt; v2 ? v2 : v1;
    else if (v1valid)
        v = v1;
    else if (v2valid)
        v = v2;
    else
        return null;

    return new double[] { u, v };
}
</code></pre>

<p>and here is my quad drawing code:</p>

<pre><code>public void renderFace(Screen screen, int x0, int y0, int z0, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
    boolean render = true;

    double[] p0 = screen.project(x0, y0, z0);
    int px0 = (int) p0[0], py0 = (int) p0[1];
    render |= p0[2] &gt;= ZCLIP &amp;&amp; px0 &gt;= 0 &amp;&amp; px0 &lt; screen.width &amp;&amp; py0 &gt;= 0 &amp;&amp; py0 &lt; screen.height;

    double[] p1 = screen.project(x1, y1, z1);
    int px1 = (int) p1[0], py1 = (int) p1[1];
    render |= p1[2] &gt;= ZCLIP &amp;&amp; px1 &gt;= 0 &amp;&amp; px1 &lt; screen.width &amp;&amp; py1 &gt;= 0 &amp;&amp; py1 &lt; screen.height;

    double[] p2 = screen.project(x2, y2, z2);
    int px2 = (int) p2[0], py2 = (int) p2[1];
    render |= p2[2] &gt;= ZCLIP &amp;&amp; px2 &gt;= 0 &amp;&amp; px2 &lt; screen.width &amp;&amp; py2 &gt;= 0 &amp;&amp; py2 &lt; screen.height;

    double[] p3 = screen.project(x3, y3, z3);
    int px3 = (int) p3[0], py3 = (int) p3[1];
    render |= p3[2] &gt;= ZCLIP &amp;&amp; px3 &gt;= 0 &amp;&amp; px3 &lt; screen.width &amp;&amp; py3 &gt;= 0 &amp;&amp; py3 &lt; screen.height;

    if (!render)
        return;

    int minX = Math.min(Math.min(px0, px1), Math.min(px2, px3));
    if (minX &lt; 0)
        minX = 0;
    if (minX &gt; screen.width)
        minX = screen.width;

    int minY = Math.min(Math.min(py0, py1), Math.min(py2, py3));
    if (minY &lt; 0)
        minY = 0;
    if (minY &gt; screen.height)
        minY = screen.height;

    int maxX = Math.max(Math.max(px0, px1), Math.max(px2, px3));
    if (maxX &lt; 0)
        maxX = 0;
    if (maxX &gt; screen.width)
        maxX = screen.width;

    int maxY = Math.max(Math.max(py0, py1), Math.max(py2, py3));
    if (maxY &lt; 0)
        maxY = 0;
    if (maxY &gt; screen.height)
        maxY = screen.height;

    if (minX == maxX || minY == maxY)
        return;

    for (int py = minY; py &lt; maxY; ++py)
        for (int px = minX; px &lt; maxX; ++px) {
            double[] uv = screen.map(px + 0.5, py + 0.5, px0, py0, px1, py1, px2, py2, px3, py3);
            if (uv == null)
                continue;
            double u = uv[0], v = uv[1];

            double pz = (1 - u) * ((1 - v) * p0[2] + v * p2[2]) + u * ((1 - v) * p1[2] + v * p3[2]);
            if (pz &lt; ZCLIP)
                continue;

            int texX = 15 - Math.min(15, (int) (16 * u));
            int texY = 15 - Math.min(15, (int) (16 * v));
            screen.setPixel(px, py, pz, Art.WALLS.getPixel(texX, texY) * BRICKS);
        }
}
</code></pre>

<p>Can anyone point out what I am doing wrong? I am not very experienced as this is my first try at implementing a game engine.</p>

<p>Thank you for any insight.</p>

CC BY-SA 3.0
2016-05-10T15:47:29.307Z
I didn't read your code because I saw this:

*"For example, when one of the vertices is behind the camera, the texture is stretched."*

You *have* to clip your polygons to a Z=constant plane in front of the camera or else all sorts of chaos will ensue :-)
Simon F
CC BY-SA 3.0
2016-05-10T16:32:41.590Z
@SimonF I am already clipping on the Z axis, see _ZCLIP_ in the 3rd code.
neat
CC BY-SA 3.0
2016-05-10T16:55:31.523Z
My apologies! I saw that opening paragraph and it "raised a red flag".    So given *"I am guessing that it has something to do with the texture mapping not being perspective-correct."* .. I tried to understand your "map" function but got lost. For example, I don't understand why you need a square root.  IIRC there's an initial set up from triangle coords to create 9 params, a,b,c d e f  p q r, then @ pixel x,y,  U= (a x +by + c)/(px+qy+r)  and V = (d x +ey + f)/(px+qy+r).   To determine the abc..pqr requires adjoint of a simplified "3x3" matrix (i.e. inverse w/out det) and mat muls.
Simon F
CC BY-SA 3.0
2016-05-10T17:49:48.373Z
@SimonF My *map()* function is adapted from [this StackoverFlow answer](http://stackoverflow.com/questions/808441/inverse-bilinear-interpolation). I thought it was perspective-correct, but I guess it isn't. Does all perspective-correct texture mapping techniques require to break down the polygon into triangles? Is there any formula that is not in a matricial form? Thanks!
neat
CC BY-SA 3.0
2016-05-11T10:22:36.163Z
Why on earth do people want to avoid the matrix calculations. Triangles make it easy to transform the coordinates.
joojaa
CC BY-SA 3.0
2016-05-11T10:38:23.343Z
@joojaa My reason is that creating a matrix object, calling and updating members is going to take longer to the JVM than simply executing some bytecode. Okay, I understand. Does it work w/o breaking it down into triangles though?
neat
CC BY-SA 3.0
2016-05-12T14:01:57.077Z
*"Does it work w/o breaking it down into triangles though"*. If your surface is planar and the texturing, in world space, is linear, then "yes". You just need 3 non-colinear points on that surface along with their matching UVs.
Simon F
<texture><perspective><uv-mapping>
Perspective-Correct Texture Mapping
neat
# Perspective-Correct Texture Mapping

(Asked by: username_0 on 2016-05-10)

I am working on a small rendering engine for a personal project and I am having issues with the texture mapping part of it.

It seems to work for some cases, but not for others. For example, when one of the vertices is behind the camera, the texture is stretched.

*Seemingly* **Correct case**
[![Seemingly correct case](https://i.sstatic.net/IJtuU.png)](https://i.sstatic.net/IJtuU.png)
**Incorrect case**
[![Incorrect case](https://i.sstatic.net/5M70C.png)](https://i.sstatic.net/5M70C.png)

I am guessing that it has something to do with the texture mapping not being perspective-correct. I have tried various changes mainly involving the z-distance to the camera, but I could not find any quick fix to my code.

Here is my code for the perspective projection:

```
public double[] project(double x, double y, double z) {
    double tx = x - camera.x;
    double ty = z - camera.z;
    double tz = y - camera.y;

    double cx = Math.cos(camera.pitch);
    double cy = Math.cos(camera.yaw);
    double cz = Math.cos(camera.roll);

    double sx = Math.sin(camera.pitch);
    double sy = Math.sin(camera.yaw);
    double sz = Math.sin(camera.roll);

    double dx = cy * (sz * ty + cz * tx) - sy * tz;
    double dy = sx * (cy * tz + sy * (sz * ty + cz * tx)) + cx * (cz * ty - sz * tx);
    double dz = cx * (cy * tz + sy * (sz * ty + cz * tx)) - sx * (cz * ty - sz * tx);

    double ez = 1.0 / Math.tan(FOV / 2.0);

    double bx = ez / dz * dx;
    double by = ez / dz * dy;

    if (dz < 0.0) {
        bx = -bx;
        by = -by;
    }

    int px = (int) (width + bx * height) / 2;
    int py = (int) (height + by * height) / 2;

    return new double[] { px, py, dz };
}

```

and here my code for the texture mapping:

```
public double[] map(double x, double y, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) {
    double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
    double B = ((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3) + (x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2)) / 2.0;
    double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);

    double det = A - 2.0 * B + C;

    double u;
    if (det == 0.0) {
        u = A / (A - C);
        if (Double.isNaN(u) || u < 0.0 || u > 1.0)
            return null;
    } else {
        double u1 = ((A - B) + Math.sqrt(B * B - A * C)) / det;
        boolean u1valid = !Double.isNaN(u1) && u1 >= 0.0 && 1.0 >= u1;

        double u2 = ((A - B) - Math.sqrt(B * B - A * C)) / det;
        boolean u2valid = !Double.isNaN(u2) && u2 >= 0.0 && 1.0 >= u2;

        if (u1valid && u2valid)
            u = u1 < u2 ? u2 : u1;
        else if (u1valid)
            u = u1;
        else if (u2valid)
            u = u2;
        else
            return null;
    }

    double v1 = ((1.0 - u) * (x0 - x) + u * (x1 - x)) / ((1.0 - u) * (x0 - x2) + u * (x1 - x3));
    boolean v1valid = !Double.isNaN(v1) && v1 >= 0.0 && 1.0 >= v1;

    double v2 = ((1.0 - u) * (y0 - y) + u * (y1 - y)) / ((1.0 - u) * (y0 - y2) + u * (y1 - y3));
    boolean v2valid = !Double.isNaN(v2) && v2 >= 0.0 && 1.0 >= v2;

    double v;
    if (v1valid && v2valid)
        v = v1 < v2 ? v2 : v1;
    else if (v1valid)
        v = v1;
    else if (v2valid)
        v = v2;
    else
        return null;

    return new double[] { u, v };
}

```

and here is my quad drawing code:

```
public void renderFace(Screen screen, int x0, int y0, int z0, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
    boolean render = true;

    double[] p0 = screen.project(x0, y0, z0);
    int px0 = (int) p0[0], py0 = (int) p0[1];
    render |= p0[2] >= ZCLIP && px0 >= 0 && px0 < screen.width && py0 >= 0 && py0 < screen.height;

    double[] p1 = screen.project(x1, y1, z1);
    int px1 = (int) p1[0], py1 = (int) p1[1];
    render |= p1[2] >= ZCLIP && px1 >= 0 && px1 < screen.width && py1 >= 0 && py1 < screen.height;

    double[] p2 = screen.project(x2, y2, z2);
    int px2 = (int) p2[0], py2 = (int) p2[1];
    render |= p2[2] >= ZCLIP && px2 >= 0 && px2 < screen.width && py2 >= 0 && py2 < screen.height;

    double[] p3 = screen.project(x3, y3, z3);
    int px3 = (int) p3[0], py3 = (int) p3[1];
    render |= p3[2] >= ZCLIP && px3 >= 0 && px3 < screen.width && py3 >= 0 && py3 < screen.height;

    if (!render)
        return;

    int minX = Math.min(Math.min(px0, px1), Math.min(px2, px3));
    if (minX < 0)
        minX = 0;
    if (minX > screen.width)
        minX = screen.width;

    int minY = Math.min(Math.min(py0, py1), Math.min(py2, py3));
    if (minY < 0)
        minY = 0;
    if (minY > screen.height)
        minY = screen.height;

    int maxX = Math.max(Math.max(px0, px1), Math.max(px2, px3));
    if (maxX < 0)
        maxX = 0;
    if (maxX > screen.width)
        maxX = screen.width;

    int maxY = Math.max(Math.max(py0, py1), Math.max(py2, py3));
    if (maxY < 0)
        maxY = 0;
    if (maxY > screen.height)
        maxY = screen.height;

    if (minX == maxX || minY == maxY)
        return;

    for (int py = minY; py < maxY; ++py)
        for (int px = minX; px < maxX; ++px) {
            double[] uv = screen.map(px + 0.5, py + 0.5, px0, py0, px1, py1, px2, py2, px3, py3);
            if (uv == null)
                continue;
            double u = uv[0], v = uv[1];

            double pz = (1 - u) * ((1 - v) * p0[2] + v * p2[2]) + u * ((1 - v) * p1[2] + v * p3[2]);
            if (pz < ZCLIP)
                continue;

            int texX = 15 - Math.min(15, (int) (16 * u));
            int texY = 15 - Math.min(15, (int) (16 * v));
            screen.setPixel(px, py, pz, Art.WALLS.getPixel(texX, texY) * BRICKS);
        }
}

```

Can anyone point out what I am doing wrong? I am not very experienced as this is my first try at implementing a game engine.

Thank you for any insight.

**Tags:** texture, perspective, uv-mapping

> **Comments:**
> **username_1**: I didn't read your code because I saw this:  *"For example, when one of the vertices is behind the camera, the texture is stretched."*  You *have* to clip your polygons to a Z=constant plane in front of the camera or else all sorts of chaos will ensue :-)  
> **username_0**: @username_1 I am already clipping on the Z axis, see _ZCLIP_ in the 3rd code.  
> **username_1**: My apologies! I saw that opening paragraph and it "raised a red flag".    So given *"I am guessing that it has something to do with the texture mapping not being perspective-correct."* .. I tried to understand your "map" function but got lost. For example, I don't understand why you need a square root.  IIRC there's an initial set up from triangle coords to create 9 params, a,b,c d e f  p q r, then @ pixel x,y,  U= (a x +by + c)/(px+qy+r)  and V = (d x +ey + f)/(px+qy+r).   To determine the abc..pqr requires adjoint of a simplified "3x3" matrix (i.e. inverse w/out det) and mat muls.  
> **username_0**: @username_1 My *map()* function is adapted from [this StackoverFlow answer](http://stackoverflow.com/questions/808441/inverse-bilinear-interpolation). I thought it was perspective-correct, but I guess it isn't. Does all perspective-correct texture mapping techniques require to break down the polygon into triangles? Is there any formula that is not in a matricial form? Thanks!  
> **username_2**: Why on earth do people want to avoid the matrix calculations. Triangles make it easy to transform the coordinates.  
> **username_0**: @username_2 My reason is that creating a matrix object, calling and updating members is going to take longer to the JVM than simply executing some bytecode. Okay, I understand. Does it work w/o breaking it down into triangles though?  
> **username_1**: *"Does it work w/o breaking it down into triangles though"*. If your surface is planar and the texturing, in world space, is linear, then "yes". You just need 3 non-colinear points on that surface along with their matching UVs.  

---

## 0 answers

