Browse Source

Lesson 22.2 && 22.3

Godzil 3 years ago
parent
commit
25347e90c6
5 changed files with 197 additions and 108 deletions
  1. 21 1
      source/display.c
  2. 6 3
      source/include/display.h
  3. 1 1
      source/include/triangle.h
  4. 28 14
      source/main.c
  5. 141 89
      source/triangle.c

+ 21 - 1
source/display.c

@@ -22,7 +22,8 @@
 
 SDL_Window *window = NULL;
 SDL_Renderer *renderer = NULL;
-uint32_t *frameBuffer = NULL;
+colour_t *frameBuffer = NULL;
+double *zBuffer = NULL;
 SDL_Texture *frameBufferTexture = NULL;
 int32_t windowWidth = 1024;
 int32_t windowHeight = 768;
@@ -112,6 +113,10 @@ void destroyWindow()
     {
         free(frameBuffer);
     }
+    if (zBuffer != NULL)
+    {
+        free(zBuffer);
+    }
     SDL_DestroyRenderer(renderer);
     SDL_DestroyWindow(window);
     SDL_Quit();
@@ -221,6 +226,21 @@ void clearFrameBuffer(colour_t colour)
     }
 }
 
+void clearZBuffer()
+{
+    int32_t x, y;
+    if (doZBuffer)
+    {
+        for (y = 0 ; y < windowHeight ; y++)
+        {
+            for (x = 0 ; x < windowWidth ; x++)
+            {
+                zBuffer[x + (y * windowWidth)] = 1.0;
+            }
+        }
+    }
+}
+
 void drawGrid(int spacing, colour_t colour)
 {
     int32_t x, y;

+ 6 - 3
source/include/display.h

@@ -29,10 +29,12 @@ typedef uint32_t colour_t;
  **********************************************************************************************************************/
 extern SDL_Window *window;
 extern SDL_Renderer *renderer;
-extern uint32_t *frameBuffer;
+extern colour_t *frameBuffer;
+extern double *zBuffer;
 extern SDL_Texture *frameBufferTexture;
 extern int32_t windowWidth;
 extern int32_t windowHeight;
+extern bool doZBuffer;
 
 /***********************************************************************************************************************
  * Prototypes
@@ -43,15 +45,16 @@ void destroyWindow();
 void renderFrameBuffer();
 
 /* --- Drawing functions --- */
+void clearZBuffer();
+void clearFrameBuffer(colour_t colour);
+
 void drawPixel(int32_t x, int32_t y, colour_t colour);
 
-void clearFrameBuffer(colour_t colour);
 void drawGrid(int spacing, colour_t colour);
 void drawRectangle(int32_t x, int32_t y, int32_t w, int32_t h, colour_t colour);
 void drawVLine(int32_t x, int32_t y0, int32_t y1, colour_t colour);
 void drawHLine(int32_t x0, int32_t y, int32_t x1, colour_t colour);
 void drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, colour_t colour);
-
 void drawText(int32_t x, int32_t y, colour_t colour, const char *format, ...);
 
 static inline void intSwap(int *a, int *b)

+ 1 - 1
source/include/triangle.h

@@ -48,7 +48,7 @@ extern bool doPerpectiveCorrection;
  * Prototypes
  **********************************************************************************************************************/
 void drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour);
-void drawFilledTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour);
+void drawFilledTriangle(struct triangle_t *t);
 void drawTextureTriangle(struct triangle_t *t);
 
 int compareTrianglesZOrder(const void *p1, const void *p2);

+ 28 - 14
source/main.c

@@ -38,6 +38,7 @@ typedef enum displayMode_t
 
 bool displayVertex = false;
 bool doNotCull = false;
+bool doZBuffer = true;
 enum displayMode_t currentDisplayMode = DISPLAY_MODE_SOLID;
 matrix4_t projectionMatrix;
 light_t globalLight;
@@ -62,6 +63,13 @@ void setup()
         goto exit;
     }
 
+    zBuffer = (double *)calloc(windowWidth * windowHeight, sizeof(double));
+    if (!frameBuffer)
+    {
+        Log(TLOG_PANIC, NULL, "Memory allocation error.");
+        goto exit;
+    }
+
     frameBufferTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, windowWidth,
                                            windowHeight);
     if (frameBufferTexture == NULL)
@@ -148,13 +156,22 @@ void processInput()
             doNotCull = true;
             break;
 
-        case SDLK_v:
+        case SDLK_f:
             doPerpectiveCorrection = false;
             break;
 
-        case SDLK_f:
+        case SDLK_v:
             doPerpectiveCorrection = true;
             break;
+
+        case SDLK_b:
+            doZBuffer = true;
+            break;
+
+        case SDLK_g:
+            doZBuffer = false;
+            break;
+
         }
         break;
 
@@ -282,12 +299,12 @@ void update()
         arrayAdd(projectedTriangles, currentTriangle);
     }
 
-    trianglesCount = arrayGetSize(projectedTriangles);
+    if (!doZBuffer)
+    {
+        trianglesCount = arrayGetSize(projectedTriangles);
 
-    qsort(projectedTriangles,
-          trianglesCount,
-          sizeof(triangle_t),
-          compareTrianglesZOrder);
+        qsort(projectedTriangles, trianglesCount, sizeof(triangle_t), compareTrianglesZOrder);
+    }
 
     updateTime = (updateTime + (getMicroSecTime() - ticks) / 1000.) / 2.0;
 }
@@ -308,10 +325,7 @@ void render()
         {
         case DISPLAY_MODE_SOLID:
         case DISPLAY_MODE_SOLID_WIRE:
-            drawFilledTriangle(projectedTriangles[i].points[0].x, projectedTriangles[i].points[0].y,
-                               projectedTriangles[i].points[1].x, projectedTriangles[i].points[1].y,
-                               projectedTriangles[i].points[2].x, projectedTriangles[i].points[2].y,
-                               projectedTriangles[i].colour);
+            drawFilledTriangle(&projectedTriangles[i]);
             break;
 
         case DISPLAY_MODE_TEXTURED:
@@ -353,6 +367,7 @@ void render()
 
     renderFrameBuffer();
     clearFrameBuffer(MAKE_RGB(0, 0, 0));
+    clearZBuffer();
 
     renderTime = (renderTime + (getMicroSecTime() - ticks) / 1000.) / 2.0;
 
@@ -361,9 +376,8 @@ void render()
     drawText(5, 29, 0x11FF22, "Render time: %02.2f ms", renderTime);
     drawText(5, 41, 0x11FF22, "FPS: %.2f", 1000 / (waitTime + updateTime + renderTime));
     drawText(5, 53, 0x11FF22, "Triangles: %d / %d", triangleCount, trianglesTotal);
-    drawText(5, 65, 0x11FF22, "Culling: %s / PerspCorrection: %s",
-             doNotCull?"disabled":"enabled",
-             doPerpectiveCorrection?"enabled":"disabled");
+    drawText(5, 65, 0x11FF22, "Cull: %s | PeCo: %s | ZB: %s",
+             doNotCull?"N":"Y", doPerpectiveCorrection?"Y":"N", doZBuffer?"Y":"N");
     SDL_RenderPresent(renderer);
 }
 

+ 141 - 89
source/triangle.c

@@ -16,127 +16,159 @@
 
 bool doPerpectiveCorrection = true;
 
-void drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour)
+static vec3_t barycentricWeights(vec2_t a, vec2_t b, vec2_t c, vec2_t p)
 {
-    drawLine(x0, y0, x1, y1, colour);
-    drawLine(x1, y1, x2, y2, colour);
-    drawLine(x2, y2, x0, y0, colour);
-}
+    vec2_t ab = vec2SubVectors(b, a);
+    vec2_t bc = vec2SubVectors(c, b);
+    vec2_t ac = vec2SubVectors(c, a);
+    vec2_t ap = vec2SubVectors(p, a);
+    vec2_t bp = vec2SubVectors(p, b);
+    vec3_t ret;
 
-/* ----------------------------------- Filled triangles ----------------------------------- */
+    double areaTriangleABC = (ab.x * ac.y) - (ab.y * ac.x);
 
-/* This function expect Point 0 to be the top, 1 to be the bottom left, 2 to be the bottom right */
-static void drawFillBottomFlatTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour)
-{
-    int i;
-    int32_t deltaXL = x1 - x0;
-    int32_t deltaXR = x2 - x0;
-    int32_t deltaY = y1 - y0;
-    int32_t sideLength = abs(deltaY);
+    double alpha = ((bc.x * bp.y) - (bp.x * bc.y)) / areaTriangleABC;
+    double beta = ((ap.x * ac.y) - (ac.x * ap.y)) / areaTriangleABC;
+    double gamma = 1 - alpha - beta;
 
-    double incrementXL = deltaXL / (double)sideLength;
-    double incrementXR = deltaXR / (double)sideLength;
-    double incrementY = deltaY / (double)sideLength;
+    ret.x = alpha;
+    ret.y = beta;
+    ret.z = gamma;
 
-    double currentXL = x0;
-    double currentXR = x0;
-    double currentY = y0;
+    return ret;
+}
 
-    for(i = 0; i < sideLength; i++)
-    {
-        drawHLine(round(currentXL), round(currentY), round(currentXR), colour);
-        currentXL += incrementXL;
-        currentXR += incrementXR;
-        currentY += incrementY;
-    }
+void drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour)
+{
+    drawLine(x0, y0, x1, y1, colour);
+    drawLine(x1, y1, x2, y2, colour);
+    drawLine(x2, y2, x0, y0, colour);
 }
 
-/* This function expect Point 2 to be the bottom, 0 to be the top left, 1 to be the top right */
-static void drawFillTopFlatTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour)
+/* ----------------------------------- Filled triangles ----------------------------------- */
+static void drawZPixel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, colour_t colour)
 {
-    int i;
-    int32_t deltaXL = x0 - x2;
-    int32_t deltaXR = x1 - x2;
-    int32_t deltaY = y0 - y2;
-    int32_t sideLength = abs(deltaY);
+    vec2_t pointP = { x, y };
+    vec2_t a2 = vec2FromVec4(a);
+    vec2_t b2 = vec2FromVec4(b);
+    vec2_t c2 = vec2FromVec4(c);
 
-    if (sideLength == 0)
+    if ((x < 0) || (y < 0) || (x > windowWidth) ||(y > windowHeight))
     {
         return;
     }
 
-    double incrementXL = deltaXL / (double)sideLength;
-    double incrementXR = deltaXR / (double)sideLength;
-    double incrementY = deltaY / (double)sideLength;
+    vec3_t weights = barycentricWeights(a2, b2, c2, pointP);
+
+    double alpha = weights.x;
+    double beta = weights.y;
+    double gamma = weights.z;
+    double interpolatedReciprocalW;
 
-    double currentXL = x2;
-    double currentXR = x2;
-    double currentY = y2;
+    interpolatedReciprocalW = (1 / a.w) * alpha + (1 / b.w) * beta + (1 / c.w) * gamma;
 
-    for(i = 0; i <= sideLength; i++)
+    /* Inverse to get logical W invrementing going further to us */
+    interpolatedReciprocalW = 1 - interpolatedReciprocalW;
+    if (interpolatedReciprocalW < zBuffer[(windowWidth * y) + x])
     {
-        drawHLine(round(currentXL), round(currentY), round(currentXR), colour);
-        currentXL += incrementXL;
-        currentXR += incrementXR;
-        currentY += incrementY;
+        drawPixel(x, y, colour);
+
+        /* Update Z Buffer */
+        zBuffer[(windowWidth * y) + x] = interpolatedReciprocalW;
     }
 }
 
-void drawFilledTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, colour_t colour)
+void drawFilledTriangle(struct triangle_t *t)
 {
-    int32_t My, Mx;
-    if (y0 > y1)
+    int32_t x, y;
+
+    if (t->points[0].y > t->points[1].y)
     {
-        intSwap(&x0, &x1); intSwap(&y0, &y1);
+        doubleSwap(&t->points[0].x, &t->points[1].x); doubleSwap(&t->points[0].y, &t->points[1].y);
+        doubleSwap(&t->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
     }
-    if (y1 > y2)
+    if (t->points[1].y > t->points[2].y)
     {
-        intSwap(&x1, &x2); intSwap(&y1, &y2);
+        doubleSwap(&t->points[1].x, &t->points[2].x); doubleSwap(&t->points[1].y, &t->points[2].y);
+        doubleSwap(&t->points[1].z, &t->points[2].z); doubleSwap(&t->points[1].w, &t->points[2].w);
     }
-    if (y0 > y1)
+    if (t->points[0].y > t->points[1].y)
     {
-        intSwap(&x0, &x1); intSwap(&y0, &y1);
+        doubleSwap(&t->points[0].x, &t->points[1].x); doubleSwap(&t->points[0].y, &t->points[1].y);
+        doubleSwap(&t->points[0].z, &t->points[1].z); doubleSwap(&t->points[0].w, &t->points[1].w);
     }
 
-    /* Determine the mid intersection and point */
-    My = y1;
-    Mx = x0 + (double)((x2 - x0) * (y1 - y0)) / (double)(y2 - y0);
+    vec4_t a = t->points[0];
+    vec4_t b = t->points[1];
+    vec4_t c = t->points[2];
 
-    /* Fill top */
-    if (y0 != y1)
-    {
-        drawFillBottomFlatTriangle(x0, y0, x1, y1, Mx, My, colour);
-    }
-    /* Fill bottom */
-    if (y1 != y2)
+    /* Render the top part */
+    double inverseSlope1 = 0;
+    double inverseSlope2 = 0;
+    int32_t x0 = a.x, y0 = a.y;
+    int32_t x1 = b.x, y1 = b.y;
+    int32_t x2 = c.x, y2 = c.y;
+
+    if ((y1 - y0) != 0) { inverseSlope1 = (double)(x1 - x0) / abs(y1 - y0); }
+    if ((y2 - y0) != 0) { inverseSlope2 = (double)(x2 - x0) / abs(y2 - y0); }
+
+    if ((y1 - y0) != 0)
     {
-        drawFillTopFlatTriangle(x1, y1, Mx, My, x2, y2, colour);
-    }
-}
+        for (y = y0 ; y <= y1 ; y++)
+        {
+            int32_t xStart = x1 + (y - y1) * inverseSlope1;
+            int32_t xEnd = x0 + (y - y0) * inverseSlope2;
 
-/* ----------------------------------- Textured triangles ----------------------------------- */
-static vec3_t barycentricWeights(vec2_t a, vec2_t b, vec2_t c, vec2_t p)
-{
-    vec2_t ab = vec2SubVectors(b, a);
-    vec2_t bc = vec2SubVectors(c, b);
-    vec2_t ac = vec2SubVectors(c, a);
-    vec2_t ap = vec2SubVectors(p, a);
-    vec2_t bp = vec2SubVectors(p, b);
-    vec3_t ret;
+            if (doZBuffer)
+            {
+                if (xEnd < xStart)
+                {
+                    intSwap(&xStart, &xEnd);
+                }
+                for (x = xStart ; x <= xEnd ; x++)
+                {
+                    drawZPixel(x, y, a, b, c,t->colour);
+                }
+            }
+            else
+            {
+                drawHLine(xStart, y, xEnd, t->colour);
+            }
+        }
+    }
 
-    double areaTriangleABC = (ab.x * ac.y) - (ab.y * ac.x);
+    /* Render the bottom part */
+    inverseSlope1 = 0;
+    if ((y2 - y1) != 0) { inverseSlope1 = (double)(x2 - x1) / abs(y2 - y1); }
 
-    double alpha = ((bc.x * bp.y) - (bp.x * bc.y)) / areaTriangleABC;
-    double beta = ((ap.x * ac.y) - (ac.x * ap.y)) / areaTriangleABC;
-    double gamma = 1 - alpha - beta;
+    if ((y2 - y1) != 0)
+    {
+        for (y = y1 ; y <= y2 ; y++)
+        {
+            int32_t xStart = x2 + (y - y2) * inverseSlope1;
+            int32_t xEnd = x0 + (y - y0) * inverseSlope2;
 
-    ret.x = alpha;
-    ret.y = beta;
-    ret.z = gamma;
+            if (doZBuffer)
+            {
+                if (xEnd < xStart)
+                {
+                    intSwap(&xStart, &xEnd);
+                }
+                for (x = xStart ; x <= xEnd ; x++)
+                {
+                    drawZPixel(x, y, a, b, c,t->colour);
+                }
+            }
+            else
+            {
+                drawHLine(xStart, y, xEnd, t->colour);
+            }
+        }
+    }
 
-    return ret;
 }
 
+/* ----------------------------------- Textured triangles ----------------------------------- */
 static void drawTexel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, tex2_t ta, tex2_t tb, tex2_t tc, colour_t *texture)
 {
     vec2_t pointP = { x, y };
@@ -144,21 +176,26 @@ static void drawTexel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, tex2_t
     vec2_t b2 = vec2FromVec4(b);
     vec2_t c2 = vec2FromVec4(c);
 
+    if ((x < 0) || (y < 0) || (x > windowWidth) ||(y > windowHeight))
+    {
+        return;
+    }
+
     vec3_t weights = barycentricWeights(a2, b2, c2, pointP);
 
     double alpha = weights.x;
     double beta = weights.y;
     double gamma = weights.z;
-    double interpolatedU, interpolatedV;
+    double interpolatedU, interpolatedV, interpolatedReciprocalW;
     int32_t texX, texY;
 
+    interpolatedReciprocalW = (1 / a.w) * alpha + (1 / b.w) * beta + (1 / c.w) * gamma;
+
     if (doPerpectiveCorrection)
     {
-        double interpolatedReciprocalW;
-
         interpolatedU = (ta.u / a.w) * alpha + (tb.u / b.w) * beta + (tc.u / c.w) * gamma;
         interpolatedV = ((1 - ta.v) / a.w) * alpha + ((1 - tb.v) / b.w) * beta + ((1 - tc.v) / c.w) * gamma;
-        interpolatedReciprocalW = (1 / a.w) * alpha + (1 / b.w) * beta + (1 / c.w) * gamma;
+
 
         interpolatedU /= interpolatedReciprocalW;
         interpolatedV /= interpolatedReciprocalW;
@@ -175,7 +212,22 @@ static void drawTexel(int32_t x, int32_t y, vec4_t a, vec4_t b, vec4_t c, tex2_t
     texX = texX % textureWidth;
     texY = texY % textureWidth;
 
-    drawPixel(x, y, texture[(texY * textureWidth) + texX]);
+    if (doZBuffer)
+    {
+        /* Inverse to get logical W invrementing going further to us */
+        interpolatedReciprocalW = 1 - interpolatedReciprocalW;
+        if (interpolatedReciprocalW < zBuffer[(windowWidth * y) + x])
+        {
+            drawPixel(x, y, texture[(texY * textureWidth) + texX]);
+
+            /* Update Z Buffer */
+            zBuffer[(windowWidth * y) + x] = interpolatedReciprocalW;
+        }
+    }
+    else
+    {
+        drawPixel(x, y, texture[(texY * textureWidth) + texX]);
+    }
 }