I'm currently writing a software renderer. I managed to implement texture mapping and it works fine. Only issue is, that the edges between the pixels are sometimes jagged in x direction as you can see here:


Could this be caused by a lack of precision? If so, how can I improve it? What I'm doing is simply stepping along the edges of a triangle and interpolating the UVs for each scanline.

EDIT: Here's my code for edge stepping and drawing of scanlines. The Edge class has a method step which increments the interpolants for each scanline.


Edge(const Vertex& minY, const Vertex& maxY){
    float dy = maxY.position.y - minY.position.y;

    yStart = static_cast<int>(ceil(minY.position.y));
    yEnd = static_cast<int>(ceil(maxY.position.y));

    float prestep = yStart - minY.position.y;

    xStep = (maxY.position.x - minY.position.x) / dy;
    x = minY.position.x + xStep * prestep;

    wStep = (1.0f / maxY.position.w - 1.0f / minY.position.w) / dy;
    w = 1.0f / minY.position.w + wStep * prestep;

    texCoordsStep = (maxY.textureCoordinates / maxY.position.w - minY.textureCoordinates / minY.position.w) / dy;
    texCoords = minY.textureCoordinates / minY.position.w + texCoordsStep * prestep;


void RenderTarget::DrawScanLine(const Edge& left, const Edge& right, int y){
    int xStart = static_cast<int>(ceil(left.GetCurX()));
    int xEnd = static_cast<int>(ceil(right.GetCurX()));
    float dx = static_cast<float>(xEnd - xStart);
    float prestep = xStart - left.GetCurX();

    float w = left.GetCurW();
    float wStep = (right.GetCurW() - w) / dx;

    glm::vec2 texCoordsStep = (right.GetCurTexCoords() - left.GetCurTexCoords()) / dx;
    glm::vec2 texCoords = left.GetCurTexCoords() + texCoordsStep * prestep;

    Uint32* p = pixels + xStart + y * surface->w;

    for(int x = xStart; x < xEnd; x++){
        float z = 1.0f / w;  //value for perspective correction
        int s = static_cast<int>(texCoords.s * z * texture->surface->w) & texture->surface->w - 1;
        int t = static_cast<int>(texCoords.t * z * texture->surface->h) & texture->surface->h - 1;

        *p = texture->pixels[s + t * texture->surface->w];  //copying the texture's pixel to the framebuffer
        w += wStep;
        texCoords += texCoordsStep;
        color += colorStep;
  • $\begingroup$ I'm confused by why there are vertical lines at all. Is it deliberate that the texture is aligned with the image plane instead of the triangle? What happens when you rotate the texture (mapping)? $\endgroup$
    – Dan Hulme
    Commented Oct 10, 2016 at 8:59
  • $\begingroup$ @DanHulme It's actually aligned with the triangle, you just can't see it in the screenshot. I tried to change the uv mapping but no difference.Weird thing is though, if I rotate the triangle 90° it's perfect. I think this might have something to do with the triangle itself. The problems occur if one side of the triangle is aligned with the x-axis. When aligned with the y-axis it works... $\endgroup$
    – lelgetrekt
    Commented Oct 10, 2016 at 12:44
  • $\begingroup$ The triangle edge jaggies in the top-right corner (against black background) strike me odd though. Why some pixels appear bigger than others? They should all be the same size. $\endgroup$
    – JarkkoL
    Commented Oct 11, 2016 at 20:37
  • $\begingroup$ @JarkkoL that's because I scaled the picture in Photoshop in order to make the issue more visible :) $\endgroup$
    – lelgetrekt
    Commented Oct 11, 2016 at 20:46
  • $\begingroup$ I thought you might have (: You should scale it properly to make it less confusing. $\endgroup$
    – JarkkoL
    Commented Oct 11, 2016 at 20:47

2 Answers 2


I strongly suspect that you are simply running out of floating point precision (at least in "U" dimension). With this sort of mathematics, the LSBs will soon become noisy resulting in the jagged appearance when you are (effectively) doing yes/no comparisons against known constants.

May I suggest instead that, rather than doing point sampling of your texture, try doing (at the very least) bilinear filtering which will hide the precision artefacts and also IMHO look better than having little squares.

UPDATE It just occurred to me that one of the possible causes of inaccuracy is that you are doing increments for each pixel X step, e.g. these two

w  += wStep;
texCoords += texCoordsStep;

These will soon cause errors to accumulate. Remove them and replace with multiplies by the X value and I expect things should improve.

UPDATE2 It will also be affected by how far away from the origin of the texture you are when you sample it. If, for example, your source texture is defined over [0..1]x[0..1] and then repeats outside of that range, you might expect sampling at uv = (1000000.125, 100000.125) to be the same as (0.125,0.125) but, because the precision of FP decreases with increasing magnitude (i.e. the steps between floats also increases), you might end up with something quite different.

  • $\begingroup$ Yeah, that's kind of what I expected. But it has to work somehow... $\endgroup$
    – lelgetrekt
    Commented Oct 11, 2016 at 19:12
  • $\begingroup$ I already have bilinear filtering but I like to have both. This gives it sort of a retro look $\endgroup$
    – lelgetrekt
    Commented Oct 11, 2016 at 19:13
  • $\begingroup$ @Leon2806 see update re not doing per-pixel increments. $\endgroup$
    – Simon F
    Commented Oct 12, 2016 at 8:14
  • 1
    $\begingroup$ I'm not sure if I completely understand what you mean. Should I multiply the step value by x? because that gives me the exact same result. $\endgroup$
    – lelgetrekt
    Commented Oct 12, 2016 at 12:44
  • $\begingroup$ In real mathematics it does, but not in the world of floating point. I believe the appropriate quote is "Floating point numbers are like piles of sand; every time you move them around, you lose a little sand and pick up a little dirt.” ( — Brian Kernighan and P. J. Plauger). Every time you do an operation (e.g an add or a mul) you'll typically lose up to 1/2 a ULP of accuracy. Replacing a sequence "+C+C+C...+C" with +N*C will thus improve your sand to dirt ratio :-) $\endgroup$
    – Simon F
    Commented Oct 12, 2016 at 13:17

In my case, i fixed the problem adding Dx' for interpolation of x-direction. In edge scan line of x-direction, I use integer-type for speed-up but ingnored dx' as below. I changed variable type from interger to float and adding dx'. (picture 1) enter image description here

  • $\begingroup$ when you use interger type with shift, same as float-type $\endgroup$
    – zeroe
    Commented Jan 9, 2017 at 10:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.