Kaynağa Gözat

LibGL: Add depth tests and writes to SoftwareRasterizer

Tests against and writes to the depth buffer when GL_DEPTH_TEST is
enabled via glEnable(). Currently fragment z is always compared against
existing depth with GL_LESS.
Stephan Unverwerth 4 yıl önce
ebeveyn
işleme
e8f66f821c

+ 40 - 11
Userland/Libraries/LibGL/SoftwareRasterizer.cpp

@@ -22,6 +22,12 @@ constexpr static float triangle_area(const FloatVector2& a, const FloatVector2&
     return ((c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x)) / 2;
 }
 
+template<typename T>
+constexpr static T interpolate(const T& v0, const T& v1, const T& v2, const FloatVector4& barycentric_coords)
+{
+    return v0 * barycentric_coords.x() + v1 * barycentric_coords.y() + v2 * barycentric_coords.z();
+}
+
 static Gfx::RGBA32 to_rgba32(const FloatVector4& v)
 {
     auto clamped = v.clamped(0, 1);
@@ -33,7 +39,7 @@ static Gfx::RGBA32 to_rgba32(const FloatVector4& v)
 }
 
 template<typename PS>
-static void rasterize_triangle(Gfx::Bitmap& render_target, const GLTriangle& triangle, PS pixel_shader)
+static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& render_target, DepthBuffer& depth_buffer, const GLTriangle& triangle, PS pixel_shader)
 {
     // Since the algorithm is based on blocks of uniform size, we need
     // to ensure that our render_target size is actually a multiple of the block size
@@ -125,7 +131,7 @@ static void rasterize_triangle(Gfx::Bitmap& render_target, const GLTriangle& tri
                 || (a.z() < zero.z() && b.z() < zero.z() && c.z() < zero.z() && d.z() < zero.z()))
                 continue;
 
-            // barycentric coordinate derrivatives
+            // barycentric coordinate derivatives
             auto dcdx = (b - a) / RASTERIZER_BLOCK_SIZE;
             auto dcdy = (c - a) / RASTERIZER_BLOCK_SIZE;
 
@@ -134,9 +140,20 @@ static void rasterize_triangle(Gfx::Bitmap& render_target, const GLTriangle& tri
                 // Fill the block without further coverage tests
                 for (int y = y0; y < y1; y++) {
                     auto coords = a;
-                    auto* pixels = &render_target.scanline(y)[x0];
+                    auto* pixel = &render_target.scanline(y)[x0];
+                    auto* depth = &depth_buffer.scanline(y)[x0];
                     for (int x = x0; x < x1; x++) {
-                        *pixels++ = to_rgba32(pixel_shader(coords, triangle));
+                        if (options.enable_depth_test) {
+                            float z = interpolate(triangle.vertices[0].z, triangle.vertices[1].z, triangle.vertices[2].z, coords);
+                            if (z < *depth) {
+                                *pixel = to_rgba32(pixel_shader(coords, triangle));
+                                *depth = z;
+                            }
+                        } else {
+                            *pixel = to_rgba32(pixel_shader(coords, triangle));
+                        }
+                        pixel++;
+                        depth++;
                         coords = coords + dcdx;
                     }
                     a = a + dcdy;
@@ -146,12 +163,22 @@ static void rasterize_triangle(Gfx::Bitmap& render_target, const GLTriangle& tri
                 // We need to test coverage of every pixel within the block
                 for (int y = y0; y < y1; y++) {
                     auto coords = a;
-                    auto* pixels = &render_target.scanline(y)[x0];
+                    auto* pixel = &render_target.scanline(y)[x0];
+                    auto* depth = &depth_buffer.scanline(y)[x0];
                     for (int x = x0; x < x1; x++) {
                         if (test_point(coords)) {
-                            *pixels = to_rgba32(pixel_shader(coords, triangle));
+                            if (options.enable_depth_test) {
+                                float z = interpolate(triangle.vertices[0].z, triangle.vertices[1].z, triangle.vertices[2].z, coords);
+                                if (z < *depth) {
+                                    *pixel = to_rgba32(pixel_shader(coords, triangle));
+                                    *depth = z;
+                                }
+                            } else {
+                                *pixel = to_rgba32(pixel_shader(coords, triangle));
+                            }
                         }
-                        pixels++;
+                        pixel++;
+                        depth++;
                         coords = coords + dcdx;
                     }
                     a = a + dcdy;
@@ -170,13 +197,14 @@ static Gfx::IntSize closest_multiple(const Gfx::IntSize& min_size, size_t step)
 
 SoftwareRasterizer::SoftwareRasterizer(const Gfx::IntSize& min_size)
     : m_render_target { Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, closest_multiple(min_size, RASTERIZER_BLOCK_SIZE)) }
+    , m_depth_buffer { adopt_own(*new DepthBuffer(closest_multiple(min_size, RASTERIZER_BLOCK_SIZE))) }
 {
 }
 
 void SoftwareRasterizer::submit_triangle(const GLTriangle& triangle)
 {
     if (m_options.shade_smooth) {
-        rasterize_triangle(*m_render_target, triangle, [](const FloatVector4& v, const GLTriangle& t) -> FloatVector4 {
+        rasterize_triangle(m_options, *m_render_target, *m_depth_buffer, triangle, [](const FloatVector4& v, const GLTriangle& t) -> FloatVector4 {
             const float r = t.vertices[0].r * v.x() + t.vertices[1].r * v.y() + t.vertices[2].r * v.z();
             const float g = t.vertices[0].g * v.x() + t.vertices[1].g * v.y() + t.vertices[2].g * v.z();
             const float b = t.vertices[0].b * v.x() + t.vertices[1].b * v.y() + t.vertices[2].b * v.z();
@@ -184,7 +212,7 @@ void SoftwareRasterizer::submit_triangle(const GLTriangle& triangle)
             return { r, g, b, a };
         });
     } else {
-        rasterize_triangle(*m_render_target, triangle, [](const FloatVector4&, const GLTriangle& t) -> FloatVector4 {
+        rasterize_triangle(m_options, *m_render_target, *m_depth_buffer, triangle, [](const FloatVector4&, const GLTriangle& t) -> FloatVector4 {
             return { t.vertices[0].r, t.vertices[0].g, t.vertices[0].b, t.vertices[0].a };
         });
     }
@@ -195,6 +223,7 @@ void SoftwareRasterizer::resize(const Gfx::IntSize& min_size)
     wait_for_all_threads();
 
     m_render_target = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, closest_multiple(min_size, RASTERIZER_BLOCK_SIZE));
+    m_depth_buffer = adopt_own(*new DepthBuffer(m_render_target->size()));
 }
 
 void SoftwareRasterizer::clear_color(const FloatVector4& color)
@@ -209,11 +238,11 @@ void SoftwareRasterizer::clear_color(const FloatVector4& color)
     m_render_target->fill(Gfx::Color(r, g, b, a));
 }
 
-void SoftwareRasterizer::clear_depth(float)
+void SoftwareRasterizer::clear_depth(float depth)
 {
     wait_for_all_threads();
 
-    // FIXME: implement this
+    m_depth_buffer->clear(depth);
 }
 
 void SoftwareRasterizer::blit_to(Gfx::Bitmap& target)

+ 3 - 0
Userland/Libraries/LibGL/SoftwareRasterizer.h

@@ -6,7 +6,9 @@
 
 #pragma once
 
+#include "DepthBuffer.h"
 #include "GLStruct.h"
+#include <AK/OwnPtr.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Vector4.h>
 
@@ -32,6 +34,7 @@ public:
 
 private:
     RefPtr<Gfx::Bitmap> m_render_target;
+    OwnPtr<DepthBuffer> m_depth_buffer;
     RasterizerOptions m_options;
 };