# c++ – Implementing integer 1/z depth buffer

I am trying to adapt the code from this answer to use 1/z depth buffer and here is my attempt

/* g++ trig.cpp -o trig -lSDL2 */

#include <algorithm>

#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>

#define SCREEN_WIDTH  600
#define SCREEN_HEIGHT 400

// TODO make an array of values to be interpolated
// TODO uv texture mapping
typedef struct
{
int x, y, z;
int zInv; // 1/z
uint8_t r, g, b;
int u, v;
} Point2D;

constexpr int SIZE = SCREEN_WIDTH * SCREEN_HEIGHT;

uint32_t pixels[SIZE];
int zDepth[SIZE];

void SetPixel(int x, int y, int zInv, uint8_t r, uint8_t g, uint8_t b)
{
int offset = y * SCREEN_WIDTH + x;

if (zDepth[offset] < zInv)
{
zDepth[offset] = zInv;
pixels[offset] = (255 << 24) | (r << 16) | (g << 8) | b;
}
}

int ContourX[SCREEN_HEIGHT][2]; // min X and max X for every horizontal line within the triangle
int ContourZ[SCREEN_HEIGHT][2]; // 1/z
int ContourR[SCREEN_HEIGHT][2]; // red value for every horizontal line
int ContourG[SCREEN_HEIGHT][2]; // green value for every horizontal line
int ContourB[SCREEN_HEIGHT][2]; // blue value for every horizontal line

// Scans a side of a triangle setting min X and max X in ContourX[][] (same for ContourR, ContourG, and ContourB) using the Bresenham's
void TriangleLine(const Point2D& p0, const Point2D& p1)
{
// DDA variables
int kx, ky, kz, kr, kg, kb; // step directions
int dx, dy, dz, dr, dg, db; // abs delta
kx = 0; dx = p1.x - p0.x; if (dx > 0) kx = 1; if (dx < 0) { kx = -1; dx = -dx; }
ky = 0; dy = p1.y - p0.y; if (dy > 0) ky = 1; if (dy < 0) { ky = -1; dy = -dy; }
kz = 0; dz = p1.zInv - p0.zInv; if (dz > 0) kz = 1; if (dz < 0) { kz = -1; dz = -dz; }
kr = 0; dr = p1.r - p0.r; if (dr > 0) kr = 1; if (dr < 0) { kr = -1; dr = -dr; }
kg = 0; dg = p1.g - p0.g; if (dg > 0) kg = 1; if (dg < 0) { kg = -1; dg = -dg; }
kb = 0; db = p1.b - p0.b; if (db > 0) kb = 1; if (db < 0) { kb = -1; db = -db; }

int n;
n = dx;
if (n < dy) n = dy;
if (n < dz) n = dz;
if (n < dr) n = dr;
if (n < dg) n = dg;
if (n < db) n = db;
if (!n) n = 1;

// target buffer according to ky direction
int target; if (ky > 0) target = 0; else target = 1;

// integer DDA line start point
int x = p0.x;
int y = p0.y;
int z = p0.zInv;
int r = p0.r;
int g = p0.g;
int b = p0.b;

// fix endpoints just to be sure (wrong division constants by +/-1 can cause that last point is missing)
ContourX[p0.y][target] = p0.x;
ContourZ[p0.y][target] = p0.zInv;
ContourR[p0.y][target] = p0.r;
ContourG[p0.y][target] = p0.g;
ContourB[p0.y][target] = p0.b;

ContourX[p1.y][target] = p1.x;
ContourZ[p1.y][target] = p1.zInv;
ContourR[p1.y][target] = p1.r;
ContourG[p1.y][target] = p1.g;
ContourB[p1.y][target] = p1.b;

int cx, cy, cz, cr, cg, cb, i;
for (cx = cy = cz = cr = cg = cb = n, i = 0; i < n; ++i)
{
ContourX[y][target] = x;
ContourZ[y][target] = z;
ContourR[y][target] = r;
ContourG[y][target] = g;
ContourB[y][target] = b;

cx -= dx; if (cx <= 0){ cx += n; x += kx; }
cy -= dy; if (cy <= 0){ cy += n; y += ky; }
cz -= dz; if (cz <= 0){ cz += n; z += kz; }
cr -= dr; if (cr <= 0){ cr += n; r += kr; }
cg -= dg; if (cg <= 0){ cg += n; g += kg; }
cb -= db; if (cb <= 0){ cb += n; b += kb; }
}
}

void DrawTriangle(const Point2D& p0, const Point2D& p1, const Point2D& p2)
{
TriangleLine(p0, p1);
TriangleLine(p1, p2);
TriangleLine(p2, p0);

int y0, y1; // min and max y
y0 = p0.y; if (y0 > p1.y) y0 = p1.y; if (y0 > p2.y) y0 = p2.y;
y1 = p0.y; if (y1 < p1.y) y1 = p1.y; if (y1 < p2.y) y1 = p2.y;

int x0, z0, r0, g0, b0;
int x1, z1, r1, g1, b1;
int dx;
int kz, kr, kg, kb;
int dz, dr, dg, db;
int z, cz;
int r, cr;
int g, cg;
int b, cb;

for (int y = y0; y <= y1; ++y)
{
if (ContourX[y][0] < ContourX[y][1])
{
x0 = ContourX[y][0];
z0 = ContourZ[y][0];
r0 = ContourR[y][0];
g0 = ContourG[y][0];
b0 = ContourB[y][0];

x1 = ContourX[y][1];
z1 = ContourZ[y][1];
r1 = ContourR[y][1];
g1 = ContourG[y][1];
b1 = ContourB[y][1];
}
else
{
x1 = ContourX[y][0];
z1 = ContourZ[y][0];
r1 = ContourR[y][0];
g1 = ContourG[y][0];
b1 = ContourB[y][0];

x0 = ContourX[y][1];
z0 = ContourZ[y][1];
r0 = ContourR[y][1];
g0 = ContourG[y][1];
b0 = ContourB[y][1];
}

dx = x1 - x0;

kz = 0; dz = z1 - z0; if (dz > 0) kz = 1; if (dz < 0) { kz = -1; dz = -dz; }
kr = 0; dr = r1 - r0; if (dr > 0) kr = 1; if (dr < 0) { kr = -1; dr = -dr; }
kg = 0; dg = g1 - g0; if (dg > 0) kg = 1; if (dg < 0) { kg = -1; dg = -dg; }
kb = 0; db = b1 - b0; if (db > 0) kb = 1; if (db < 0) { kb = -1; db = -db; }

z = z0; cz = dx;
r = r0; cr = dx;
g = g0; cg = dx;
b = b0; cb = dx;

// x<x1 to follow top left rule (ie. don't draw bottom or right edges)
for (int x = x0; x < x1; ++x)
{
SetPixel(x, y, z, r, g, b);

cz -= dz; if (cz <= 0) { cz += dx; z += kz; }
cr -= dr; if (cr <= 0) { cr += dx; r += kr; }
cg -= dg; if (cg <= 0) { cg += dx; g += kg; }
cb -= db; if (cb <= 0) { cb += dx; b += kb; }
}
}
}

int main(void)
{
// clear the screen
std::fill(pixels, pixels + SIZE, 0);
std::fill(zDepth, zDepth + SIZE, 0);
/*
Point2D p0, p1, p2, p3;

p0.x = 30;
p0.y = 41;
p0.r = 255;
p0.g = 0;
p0.b = 0;

p1.x = 350;
p1.y = 41;
p1.r = 0;
p1.g = 255;
p1.b = 0;

p2.x = 40;
p2.y = 311;
p2.r = 0;
p2.g = 0;
p2.b = 255;

p3.x = 572;
p3.y = 280;
p3.r = 255;
p3.g = 140;
p3.b = 0;

DrawTriangle(p0, p1, p2);
DrawTriangle(p1, p2, p3);
*/

Point2D p0, p1, p2, p3, p4, p5;

p0.x = 10;
p0.y = 50;
p0.z = 10;
p0.zInv = 0xfffff / p0.z;
p0.r = 255;
p0.g = 0;
p0.b = 0;

p1.x = 400;
p1.y = 100;
p1.z = 10;
p1.zInv = 0xfffff / p1.z;
p1.r = 255;
p1.g = 0;
p1.b = 0;

p2.x = 290;
p2.y = 380;
p2.z = 10;
p2.zInv = 0xfffff / p2.z;
p2.r = 255;
p2.g = 0;
p2.b = 0;

DrawTriangle(p0, p1, p2);

p3.x = 50;
p3.y = 350;
p3.z = 2;
p3.zInv = 0xfffff / p3.z;
p3.r = 0;
p3.g = 255;
p3.b = 0;

p4.x = 130;
p4.y = 40;
p4.z = 20;
p4.zInv = 0xfffff / p4.z;
p4.r = 0;
p4.g = 255;
p4.b = 0;

p5.x = 380;
p5.y = 200;
p5.z = 5;
p5.zInv = 0xfffff / p5.z;
p5.r = 0;
p5.g = 255;
p5.b = 0;

DrawTriangle(p3, p4, p5);

SDL_Init(SDL_INIT_EVERYTHING);

SDL_Window* window = SDL_CreateWindow
(
"Trig",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN
);

SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT);
SDL_Event event;

bool quit = false;

while (!quit)
{
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
{
quit = true;
break;
}
}
}

SDL_UpdateTexture(texture, NULL, &pixels[0], SCREEN_WIDTH * 4);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}

SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();

return 0;
}


Unfortunately I didn’t get the output I desired

it should look like this (I used floating point arithmetic with naive z-buffer implementation)

For reference, here is the output by OpenGL:

I am not sure what might be the problem. Maybe I need to decrease the step size for 1/z? Any pointers?