C++ 3D software rasteriser depth buffer producing incorrect results

31 Views Asked by At

I have been recently implementing a 3D software rasteriser as part of my third year project. Currently I am implementing a depth buffer however I am getting odd results as shown in the picture (the cube object is on top of the ground object).

Current output

The implementation is based on Gabriel Gambettas blog (https://gabrielgambetta.com/computer-graphics-from-scratch/12-hidden-surface-removal.html)

After perspective projecting the triangles the draw filled triangle method is called.

void Renderer::drawFilledTriangle(haris::triangle tri, const RGBColor& color) {
    vec3d a = { tri.p[0].x, tri.p[0].y, tri.p[0].z };
    vec3d b = { tri.p[1].x, tri.p[1].y, tri.p[1].z };
    vec3d c = { tri.p[2].x, tri.p[2].y, tri.p[2].z };
    // Sort the points so that y0 <= y1 <= y2
    if (b.y < a.y) {
        std::swap(a, b);
    }
    if (c.y < a.y) {
        std::swap(a, c);
    }
    if (c.y < b.y) {
        std::swap(c, b);
    }

    std::vector<float> xab = interpolate(a.y, a.x, b.y, b.x);
    std::vector<float> zab = interpolate(a.y, 1/a.z, b.y, 1/b.z);

    std::vector<float> xbc = interpolate(b.y, b.x, c.y, c.x);
    std::vector<float> zbc = interpolate(b.y, 1/b.z, c.y, 1/c.z);

    std::vector<float> xac = interpolate(a.y, a.x, c.y, c.x);
    std::vector<float> zac = interpolate(a.y, 1/a.z, c.y, 1/c.z);


    xab.pop_back();
    std::vector<float> xabc;
    xabc.reserve(xab.size() + xbc.size());
    xabc.insert(xabc.end(), xab.begin(), xab.end());
    xabc.insert(xabc.end(), xbc.begin(), xbc.end());

    zab.pop_back();
    std::vector<float> zabc;
    zabc.reserve(zab.size() + zbc.size());
    zabc.insert(zabc.end(), zab.begin(), zab.end());
    zabc.insert(zabc.end(), zbc.begin(), zbc.end());

    int m = std::floor(xabc.size() / 2);

    std::vector<float> xLeft;
    std::vector<float> xRight;

    std::vector<float> zLeft;
    std::vector<float> zRight;
    if (xac.at(m) < xabc.at(m)) {
        xLeft = xac;
        zLeft = zac;

        xRight = xabc;
        zRight = zabc;
    }
    else {
        xRight = xac;
        zRight = zac;

        xLeft = xabc;
        zLeft = zabc;
    }

    BitmapBuffer& buffer = getInstance().buffer;

    for (int y = a.y; y < c.y-1; y++) {
        float xL = xLeft.at(y - a.y);
        float xR = xRight.at(y - a.y);
        std::vector<float> zSegment;
        zSegment.reserve(xR - xL + 1);
        zSegment= interpolate(xL, zLeft.at(y - a.y), xR, zRight.at(y - a.y));
        for (int x = xLeft.at(y - a.y); x < xRight.at(y - a.y); x++) {
            //Checking zBuffer
            float zDepth = zSegment.at(x - xL);
            if (zDepth > pDepthBuffer[x * y]) {
                pDepthBuffer[x * y] = zDepth;
                uint32_t raw_color = (color.red << 16) | (color.green << 8) | (color.blue << 0);
                int tempX = buffer.width - x;
                int tempY = buffer.height - y;
                if (tempX > 0 && tempX < buffer.width && tempY>0 && tempY < buffer.height) {
                    uint32_t* pixel = (uint32_t*)((uint8_t*)buffer.memory + tempX * bytes_per_pixel + tempY * buffer.pitch);
                    *pixel = raw_color;
                }
            }
        }
    }
}

The implementation uses linear interpolation to calculate the zDepth for each pixel. I pass 1/Z of the input points into the interpolation function. The depth buffer list is initialised to a float value of 0.0f and each value is reset to 0.0f before the next frame. Also note that I am impleminting this using the Win32 API hence the code for setting a pixel is like that, I have a wrapper function setpixel but I'm keeping it like this for now.

std::vector<float> Renderer::interpolate(int i0, float d0, int i1, float d1) {
    std::vector<float> values;
    if (i0 == i1) {
        values.push_back(d0);
        return values;
    }

    float a = (d1 - d0) / (static_cast<float>(i1) - static_cast<float>(i0));
    float d = d0;

    values.reserve(i1 - i0 + 1);    //make room for elements

    for (int i = i0; i <= i1; i++) {
        values.push_back(d);
        d += a;
    }
    return values;
}

The program was functioning correctly when implemented with the painters algoritm (just sorted all triangles from back to front) I am not entirely sure why the result is like this.

0

There are 0 best solutions below