0

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

  • _Side note:_ Unless it's a macro, doing `Max(Ay,By,Cy)` in the loop condition clause of the `for` loop is _slow_. Better to do: `int ymax = Max(Ay,By,Cy); for (int y=Min(Ay,By,Cy);y<=ymax;y++) ...` – Craig Estey Aug 05 '23 at 17:03
  • You image looks like the polygon isn't closed and "bleeds out". Is the issue with `DrawLine` [which you do _not_ show]? (i.e.) It doesn't stop at the end coordinate? Or, is the end coordinate set incorrectly? I'd simplify: Output just a _single_ triangle. You could specify different ones from command line (e.g.). Then, if one of them is more problematic than the others ... Also, you can/should do debug `printf` of the coordinates. Manually check the values for sanity. – Craig Estey Aug 05 '23 at 17:12
  • Further, your polygons are [somewhat] centered horizontally. So, the X coordinate values filled [particularly for the smaller polygon] should be in a small range (e.g.) For a 1024 wide window, range would be 412-612. So, if the drawer tries to store a pixel with an X value outside that range, that's an error. You could either have the program check this [recommended] or put a condition clause on a breakpoint in `gdb` Or, with the program doing the check and the `printf` you can abort easily/early. – Craig Estey Aug 05 '23 at 17:17
  • I don't know the code that makes up DrawLine, since it is a part of the Raylib library and not made by me. As for your third comment, I don't really understand what you mean by centered horizontally. However I am fairly certain that the issue is not with the vertices I am giving the rasterizer, since it works fine when I use the built in Raylib function for making triangles. However I did manage to sort of fix the lines glitching everywhere by simply allocating buf_x0 and buf_x1 on the heap. However instead of thin lines everywhere, it's a thick line the height of the cube. – Maxim Ricardo Aug 05 '23 at 19:36
  • To help debug, you may need to download the source for raylib and build with (e.g. `-g`) so you can breakpoint and/or single-step `DrawLine`. As to my third comment: You are drawing [mostly] to the center of the screen. So, a given raster line should look like: `lots_of_white|some_green|lots_of_white` but you're getting some lines that are solid green. For given triangle, compute the bounding rectangle [particular emphasis on the X dimension]. Any green pixels that appear _outside_ of that are in error – Craig Estey Aug 05 '23 at 21:02
  • Just noticed ... Your window's width is [much] larger than the window's height. So, you probably have UB (undefined behavior) because you're overflowing the `int buf_x0[WINDOWHEIGHT],buf_x1[WINDOWHEIGHT];` buffers. Heap allocation might partially improve this. But, to fix this, I think you want: `int buf_x0[WINDOWWIDTH],buf_x1[WINDOWWIDTH];` – Craig Estey Aug 05 '23 at 21:06

1 Answers1

1

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.