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;
}