0

I'm quite new to c++ and coding in general, so excuse what may be strange looking c++. I've been making my own 3d engine for the past while and have run into an issue with the z buffer. All the pixels work out great usually, but there are certain perspectives where the z buffer doesn't seem to be working.

I think it has something to do with floating point rounding, but I'm not experienced enough to say. Ill include the code and a picture of the issue if anyone is cool enough to have a look. Thanks a bunch if so!

void Triangle::draw(Screen screen, Camera& camera, float* zbuffer) {

    //drawTriangle(screen, camera, sP1, sP2, sP3, color, mP1, mP2, mP3, zbuffer);
    draw3(screen, camera, zbuffer);
}

void Triangle::draw3(Screen screen, Camera& camera, float* zbuffer) {

    //check if the triangle is visible on screen
    if (sP1.x < 0 && sP2.x < 0 && sP3.x < 0 ||
        sP1.y < 0 && sP2.y < 0 && sP3.y < 0 ||
        sP1.x > Screen::SCREEN_WIDTH && sP2.x > Screen::SCREEN_WIDTH && sP3.x > Screen::SCREEN_WIDTH ||
        sP1.y > Screen::SCREEN_HEIGHT && sP2.y > Screen::SCREEN_HEIGHT && sP3.y > Screen::SCREEN_HEIGHT) {
        return;
    }

    //sort points based on height
    Point2D temp2d; Point3D temp3d;
    if (sP1.y > sP2.y) { temp2d = sP1; sP1 = sP2; sP2 = temp2d; temp3d = mP1; mP1 = mP2; mP2 = temp3d; }
    if (sP1.y > sP3.y) { temp2d = sP1; sP1 = sP3; sP3 = temp2d; temp3d = mP1; mP1 = mP3; mP3 = temp3d; }
    if (sP2.y > sP3.y) { temp2d = sP2; sP2 = sP3; sP3 = temp2d; temp3d = mP2; mP2 = mP3; mP3 = temp3d; }

    //dont draw triangle with no area
    if (sP1.y == sP3.y) { return; }

    float longSlope = (sP1.x - sP3.x) / (sP3.y - sP1.y);
    //if the triangle has a flat top, dont calculate the short sloap
    float shortSlope = 0;
    float bottomSloap = 0;
    if (sP1.y != sP2.y) { shortSlope = (sP1.x - sP2.x) / (sP2.y - sP1.y); }
    if (sP2.y != sP3.y) { bottomSloap = (sP2.x - sP3.x) / (sP3.y - sP2.y); }

    //start at the peak of the triangle and work down
    float leftX = sP1.x;
    float rightX = sP1.x;
    float bisectX;
    float leftSlope = longSlope;
    float rightSlope = shortSlope;
    float end = sP2.y;
    bool swap = false;
    int y = sP1.y;
    if (sP1.y > 0) { y = sP1.y + 1; }
    double totalArea = areaOfTriangle(sP1, sP2, sP3), areaP1P2P4, areaP1P3P4, areaP2P3P4;
    float ratioP1, ratioP2, ratioP3;
    float mP4Dist;

    //make sure leftSlope corrosponds to the left most sloap
    bisectX = sP1.x - (sP1.x - sP3.x) / (sP3.y - sP1.y) * (sP2.y - sP1.y);
    if (sP2.x < bisectX) {
        leftSlope = shortSlope;
        rightSlope = longSlope;
        swap = true;
    }
    if (sP1.y == sP2.y) { y += 1; }
    if (sP1.y != sP2.y) {
        for (y; y < sP2.y; ++y) {
            for (int x = leftX; x < rightX - 1; x++) {

                sP4.x = x;
                sP4.y = y;
                //find bary coords
                areaP1P2P4 = areaOfTriangle(sP1, sP2, sP4);
                areaP1P3P4 = areaOfTriangle(sP1, sP3, sP4);
                areaP2P3P4 = areaOfTriangle(sP2, sP3, sP4);

                ratioP1 = areaP2P3P4 / totalArea;
                ratioP2 = areaP1P3P4 / totalArea;
                ratioP3 = areaP1P2P4 / totalArea;

                mP4.x = mP1.x * ratioP1 + mP2.x * ratioP2 + mP3.x * ratioP3;
                mP4.y = mP1.y * ratioP1 + mP2.y * ratioP2 + mP3.y * ratioP3;
                mP4.z = mP1.z * ratioP1 + mP2.z * ratioP2 + mP3.z * ratioP3;

                mP4Dist = getLineDist3d(mP4, camera.map);
                    if (x > 0 && x < Screen::SCREEN_WIDTH && y > 0 && y < Screen::SCREEN_HEIGHT) {
                        if (mP4Dist < screen.m_zbuffer[x + y * Screen::SCREEN_WIDTH]) {
                            screen.setPixel(x, y, color);
                            screen.setZBuffer(x, y, mP4Dist);
                        }
                    }
            }
            leftX -= leftSlope;
            rightX -= rightSlope;
        }
    }

    if (swap) { leftSlope = bottomSloap; leftX = sP2.x; }
    else { rightSlope = bottomSloap; rightX = sP2.x; }


    if (sP2.y != sP3.y) {
        for (y; y < sP3.y; y++) {

            for (int x = leftX; x < rightX - 1; x++) {

                sP4.x = x;
                sP4.y = y;
                //find bary coords
                areaP1P2P4 = areaOfTriangle(sP1, sP2, sP4);
                areaP1P3P4 = areaOfTriangle(sP1, sP3, sP4);
                areaP2P3P4 = areaOfTriangle(sP2, sP3, sP4);

                ratioP1 = areaP2P3P4 / totalArea;
                ratioP2 = areaP1P3P4 / totalArea;
                ratioP3 = areaP1P2P4 / totalArea;

                mP4.x = mP1.x * ratioP1 + mP2.x * ratioP2 + mP3.x * ratioP3;
                mP4.y = mP1.y * ratioP1 + mP2.y * ratioP2 + mP3.y * ratioP3;
                mP4.z = mP1.z * ratioP1 + mP2.z * ratioP2 + mP3.z * ratioP3;

                mP4Dist = getLineDist3d(mP4, camera.map);

                if (x > 0 && x < Screen::SCREEN_WIDTH && y > 0 && y < Screen::SCREEN_HEIGHT) {
                    if (mP4Dist < screen.m_zbuffer[x + y * Screen::SCREEN_WIDTH]) {
                        screen.setPixel(x, y, color);
                        screen.setZBuffer(x, y, mP4Dist);

                    }
                }
            }
            leftX -= leftSlope;
            rightX -= rightSlope;
        }
    }
}

float areaOfTriangle(Point2D p1, Point2D p2, Point2D p3) {

    //black box Heron's Formula finds area of triangle based on 3 points
    float area = 0.5 * abs((p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)));
    return area;
}

red triangle clipping into brown triangle

Sol
  • 1
  • 1
  • For starters, you should identify the exact X,Y,Z values involved in the problem area. If the problem is indeed due to floating point inaccuracy, there are quite a few things you can do: 1) switch to double 2) try to revisit your Z buffer so the problematic part fits in the more "sensitive" [0..1( range – Botje Oct 03 '22 at 08:02
  • @davidfong here is another angle of the issue:https://cdn.discordapp.com/attachments/1012030750645813340/1026347678596075580/unknown.png – Sol Oct 03 '22 at 15:42
  • @Botje thank you for the suggestions. I tried changing everything that I could to doubles, but that didnt seem to help. I also rewrote the entire function to use a different method of filling the triangles. the same error occurred. I think that means It has to be an issue with how I am calculating the z buffer variables. Rn, it is a buffer of floats. changing it to a buffer of doubles doesnt fix the bug. (and yes I made sure to have the barycentric coordinate calcs also use doubles). – Sol Oct 03 '22 at 16:19

0 Answers0