I have been working on a 3D software renderer in C recently, and I have managed to get some shapes and wireframes working.
However I have now gotten to rasterization and I can't seem to get it working. I am currently trying to place 2 cubes into the world, one green and one blue. However when I try to rasterize using scanline rasterization, there are green and blue horizontal lines everywhere within the same Y position as the green and blue cubes. Most of the cube is colored properly but there are still a bunch of lines. Another problem I am having is if any of these horizontal lines reaches the top or the bottom of the screen, then the renderer instantly crashes. And lastly, if I move my cursor the lines flicker a bit. This is strange because my program isn't even taking input from my mouse at all.
Image of what the screen looks like
Image of what the screen looks like with wireframes
The Code:
void FillInPolygon(struct Triangle triangle, const int WINDOWHEIGHT, Color color) {
int buf_x0[WINDOWHEIGHT],buf_x1[WINDOWHEIGHT]; //These arrays will store the X start and X end of the lines
LineDrawerForBuffer(buf_x0, buf_x1, triangle.v0.xProj, triangle.v0.yProj, triangle.v1.xProj, triangle.v1.yProj); //Line drawer will get the edges of the triangle
LineDrawerForBuffer(buf_x0, buf_x1, triangle.v1.xProj, triangle.v1.yProj, triangle.v2.xProj, triangle.v2.yProj);
LineDrawerForBuffer(buf_x0, buf_x1, triangle.v2.xProj, triangle.v2.yProj, triangle.v0.xProj, triangle.v0.yProj);
int Ay = triangle.v0.yProj; //Y position of where the first vertex of the triangle is in 2d space
int By = triangle.v1.yProj; //Y position of the second
int Cy = triangle.v2.yProj; //Y position of the third
for (int y=Min(Ay,By,Cy);y<=Max(Ay,By,Cy);y++)
DrawLine(buf_x0[y], y, buf_x1[y], y, color); //Draws a horizontal line between the X start and X end of the lines
}
LineDrawerForBuffer():
void LineDrawerForBuffer(int *buf_x0, int *buf_x1, int x1, int y1, int x2, int y2) {
//Modified version of Bresenham's Line Drawing Algorithm, this one is capable of drawing lines in all directions
int y = y1;
int x = x1;
int dx = Abs(x2-x1);
int dy = Abs(y2-y1);
int s1 = Sign(x2-x1);
int s2 = Sign(y2-y1);
bool interchange;
if (dy > dx) {
int t = dx;
dx = dy;
dy = t;
interchange = true;
}
else {
interchange = false;
}
int e = 2*dy-dx;
int a = 2*dy;
int b = 2*dy-2*dx;
if ((dy < 0 && !interchange) || (dx < 0 && interchange)) {
buf_x0[y] = x;
}
else if ((dy > 0 && !interchange) || (dx > 0 && interchange)) {
buf_x1[y] = x;
}
else if ((dy == 0 && !interchange) || (dx == 0 && interchange)) {
buf_x0[y] = x;
buf_x1[y] = x;
}
for (int i = 0; i<dx; i++) {
if (e < 0) {
if (interchange) y = y + s2;
else x = x + s1;
e = e + a;
}
else {
y = y + s2;
x = x + s1;
e = e + b;
}
if ((dy < 0 && !interchange) || (dx < 0 && interchange)) {
buf_x0[y] = x;
}
else if ((dy > 0 && !interchange) || (dx > 0 && interchange)) {
buf_x1[y] = x;
}
else if ((dy == 0 && !interchange) || (dx == 0 && interchange)) {
buf_x0[y] = x;
buf_x1[y] = x;
}
}
}
Some additional information: The renderer uses Weak Perspective Projection, and I am using Raylib to open a window and draw on it.
I have tried messing around with the line drawer and how the positions are stored in the arrays, and the size of the arrays. But nothing has fixed it yet.
EDIT: I have managed to sort of fix the thin flickering lines everywhere by allocating buf_x0 and buf_x1 using calloc. However now it's one big line the height of the cube it's coming from. The blue cube also has a weird thin line going out through the right. The result after dynamically allocating buf_x0 & buf_x1
I am also fairly sure that the issue isn't the polygons and vertices I am giving the rasterizer, as when I use the built in Raylib function for triangles it works just fine. The result when using Raylib's built in function for triangles
I managed to get the rasterizer working by changing the code that writes to buf_x0 and buf_x1. Instead of using
if ((dy < 0 && !interchange) || (dx < 0 && interchange)) {
buf_x0[y] = x;
}
else if ((dy > 0 && !interchange) || (dx > 0 && interchange)) {
buf_x1[y] = x;
}
else if ((dy == 0 && !interchange) || (dx == 0 && interchange)) {
buf_x0[y] = x;
buf_x1[y] = x;
}
in the for loop and right before it. Now I have an if statement at the start of the for loop that looks like this:
if ((y >= 0) && (y < WINDOWHEIGHT))
{
if (x < buf_x0[y]) buf_x0[y] = x;
if (x > buf_x1[y]) buf_x1[y] = x;
}
This along with a loop at right after declaring buf_x0 and buf_x1 in the FillInPolygon function. The loop initializes every value in pos_x0 to INT_MAX, and every value in pos_x1 to INT_MIN. I believe the reason the lines from the cube went off to the side was due to pos_x0 being initialized to 0, along with the fact that the if statements for writing to the arrays not actually writing to buf_x0 (Found that out after some testing). Meant that the rasterizer would try to draw from x: 0 to the other end of the polygon. The new way I write to the buf arrays also fixes the issue where my program crashes if any of the polygon's vertices are above or below the screen. The issue wasn't with the DrawLine function but rather in the LineDrawerForBuffer function. When Y was below 0 or above WINDOWHEIGHT, that would read to an index in the buf arrays that didn't exist, hence crashing the program. Atleast I am pretty sure it is.