Prechádzať zdrojové kódy

LibGL+LibSoftGPU: Implement eye, clip, NDC and window coordinates

This follows the OpenGL 1.5 spec much more closely. We need to store
the eye coordinates especially, since they are used in texture
coordinate generation and fog fragment depth calculation.
Jelle Raaijmakers 3 rokov pred
rodič
commit
fef7f7159c

+ 1 - 1
Userland/Libraries/LibGL/SoftwareGLContext.cpp

@@ -289,7 +289,7 @@ void SoftwareGLContext::gl_end()
         mv_elements[2][0], mv_elements[2][1], mv_elements[2][2]);
         mv_elements[2][0], mv_elements[2][1], mv_elements[2][2]);
     normal_transform = normal_transform.inverse();
     normal_transform = normal_transform.inverse();
 
 
-    m_rasterizer.draw_primitives(primitive_type, m_projection_matrix * m_model_view_matrix, normal_transform, m_texture_matrix, m_vertex_list, enabled_texture_units);
+    m_rasterizer.draw_primitives(primitive_type, m_model_view_matrix, normal_transform, m_projection_matrix, m_texture_matrix, m_vertex_list, enabled_texture_units);
 
 
     m_vertex_list.clear_with_capacity();
     m_vertex_list.clear_with_capacity();
 }
 }

+ 9 - 7
Userland/Libraries/LibSoftGPU/Clipper.cpp

@@ -36,14 +36,16 @@ Vertex Clipper::clip_intersection_point(const Vertex& p1, const Vertex& p2, Clip
     // See https://www.microsoft.com/en-us/research/wp-content/uploads/1978/01/p245-blinn.pdf
     // See https://www.microsoft.com/en-us/research/wp-content/uploads/1978/01/p245-blinn.pdf
     // "Clipping Using Homogeneous Coordinates" Blinn/Newell, 1978
     // "Clipping Using Homogeneous Coordinates" Blinn/Newell, 1978
 
 
-    float w1 = p1.position.w();
-    float w2 = p2.position.w();
-    float x1 = clip_plane_normals[plane_index].dot(p1.position);
-    float x2 = clip_plane_normals[plane_index].dot(p2.position);
+    float w1 = p1.clip_coordinates.w();
+    float w2 = p2.clip_coordinates.w();
+    float x1 = clip_plane_normals[plane_index].dot(p1.clip_coordinates);
+    float x2 = clip_plane_normals[plane_index].dot(p2.clip_coordinates);
     float a = (w1 + x1) / ((w1 + x1) - (w2 + x2));
     float a = (w1 + x1) / ((w1 + x1) - (w2 + x2));
 
 
     Vertex out;
     Vertex out;
     out.position = p1.position * (1 - a) + p2.position * a;
     out.position = p1.position * (1 - a) + p2.position * a;
+    out.eye_coordinates = p1.eye_coordinates * (1 - a) + p2.eye_coordinates * a;
+    out.clip_coordinates = p1.clip_coordinates * (1 - a) + p2.clip_coordinates * a;
     out.color = p1.color * (1 - a) + p2.color * a;
     out.color = p1.color * (1 - a) + p2.color * a;
     out.tex_coord = p1.tex_coord * (1 - a) + p2.tex_coord * a;
     out.tex_coord = p1.tex_coord * (1 - a) + p2.tex_coord * a;
     return out;
     return out;
@@ -64,13 +66,13 @@ void Clipper::clip_triangle_against_frustum(Vector<Vertex>& input_verts)
             const auto& curr_vec = read_from->at((i + 1) % read_from->size());
             const auto& curr_vec = read_from->at((i + 1) % read_from->size());
             const auto& prev_vec = read_from->at(i);
             const auto& prev_vec = read_from->at(i);
 
 
-            if (point_within_clip_plane(curr_vec.position, static_cast<ClipPlane>(plane))) {
-                if (!point_within_clip_plane(prev_vec.position, static_cast<ClipPlane>(plane))) {
+            if (point_within_clip_plane(curr_vec.clip_coordinates, static_cast<ClipPlane>(plane))) {
+                if (!point_within_clip_plane(prev_vec.clip_coordinates, static_cast<ClipPlane>(plane))) {
                     auto intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
                     auto intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
                     write_to->append(intersect);
                     write_to->append(intersect);
                 }
                 }
                 write_to->append(curr_vec);
                 write_to->append(curr_vec);
-            } else if (point_within_clip_plane(prev_vec.position, static_cast<ClipPlane>(plane))) {
+            } else if (point_within_clip_plane(prev_vec.clip_coordinates, static_cast<ClipPlane>(plane))) {
                 auto intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
                 auto intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
                 write_to->append(intersect);
                 write_to->append(intersect);
             }
             }

+ 64 - 53
Userland/Libraries/LibSoftGPU/Device.cpp

@@ -123,16 +123,21 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
     if (options.enable_alpha_test && options.alpha_test_func == AlphaTestFunction::Never)
     if (options.enable_alpha_test && options.alpha_test_func == AlphaTestFunction::Never)
         return;
         return;
 
 
+    // Vertices
+    Vertex const vertex0 = triangle.vertices[0];
+    Vertex const vertex1 = triangle.vertices[1];
+    Vertex const vertex2 = triangle.vertices[2];
+
     // Calculate area of the triangle for later tests
     // Calculate area of the triangle for later tests
-    IntVector2 v0 { (int)triangle.vertices[0].position.x(), (int)triangle.vertices[0].position.y() };
-    IntVector2 v1 { (int)triangle.vertices[1].position.x(), (int)triangle.vertices[1].position.y() };
-    IntVector2 v2 { (int)triangle.vertices[2].position.x(), (int)triangle.vertices[2].position.y() };
+    IntVector2 const v0 { static_cast<int>(vertex0.window_coordinates.x()), static_cast<int>(vertex0.window_coordinates.y()) };
+    IntVector2 const v1 { static_cast<int>(vertex1.window_coordinates.x()), static_cast<int>(vertex1.window_coordinates.y()) };
+    IntVector2 const v2 { static_cast<int>(vertex2.window_coordinates.x()), static_cast<int>(vertex2.window_coordinates.y()) };
 
 
     int area = edge_function(v0, v1, v2);
     int area = edge_function(v0, v1, v2);
     if (area == 0)
     if (area == 0)
         return;
         return;
 
 
-    float one_over_area = 1.0f / area;
+    auto const one_over_area = 1.0f / area;
 
 
     FloatVector4 src_constant {};
     FloatVector4 src_constant {};
     float src_factor_src_alpha = 0;
     float src_factor_src_alpha = 0;
@@ -282,9 +287,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
                             continue;
                             continue;
 
 
                         auto barycentric = FloatVector3(coords.x(), coords.y(), coords.z()) * one_over_area;
                         auto barycentric = FloatVector3(coords.x(), coords.y(), coords.z()) * one_over_area;
-                        float z = interpolate(triangle.vertices[0].position.z(), triangle.vertices[1].position.z(), triangle.vertices[2].position.z(), barycentric);
-
-                        z = options.depth_min + (options.depth_max - options.depth_min) * (z + 1) / 2;
+                        float z = interpolate(vertex0.window_coordinates.z(), vertex1.window_coordinates.z(), vertex2.window_coordinates.z(), barycentric);
 
 
                         // FIXME: Also apply depth_offset_factor which depends on the depth gradient
                         // FIXME: Also apply depth_offset_factor which depends on the depth gradient
                         z += options.depth_offset_constant * NumericLimits<float>::epsilon();
                         z += options.depth_offset_constant * NumericLimits<float>::epsilon();
@@ -367,27 +370,24 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
 
 
                     // Perspective correct barycentric coordinates
                     // Perspective correct barycentric coordinates
                     auto barycentric = FloatVector3(coords.x(), coords.y(), coords.z()) * one_over_area;
                     auto barycentric = FloatVector3(coords.x(), coords.y(), coords.z()) * one_over_area;
-                    float interpolated_reciprocal_w = interpolate(triangle.vertices[0].position.w(), triangle.vertices[1].position.w(), triangle.vertices[2].position.w(), barycentric);
-                    float interpolated_w = 1 / interpolated_reciprocal_w;
-                    barycentric = barycentric * FloatVector3(triangle.vertices[0].position.w(), triangle.vertices[1].position.w(), triangle.vertices[2].position.w()) * interpolated_w;
+                    auto const w_coordinates = FloatVector3 {
+                        vertex0.window_coordinates.w(),
+                        vertex1.window_coordinates.w(),
+                        vertex2.window_coordinates.w(),
+                    };
+                    float const interpolated_reciprocal_w = interpolate(w_coordinates.x(), w_coordinates.y(), w_coordinates.z(), barycentric);
+                    float const interpolated_w = 1 / interpolated_reciprocal_w;
+                    barycentric = barycentric * w_coordinates * interpolated_w;
 
 
                     // FIXME: make this more generic. We want to interpolate more than just color and uv
                     // FIXME: make this more generic. We want to interpolate more than just color and uv
                     FloatVector4 vertex_color;
                     FloatVector4 vertex_color;
                     if (options.shade_smooth) {
                     if (options.shade_smooth) {
-                        vertex_color = interpolate(
-                            triangle.vertices[0].color,
-                            triangle.vertices[1].color,
-                            triangle.vertices[2].color,
-                            barycentric);
+                        vertex_color = interpolate(vertex0.color, vertex1.color, vertex2.color, barycentric);
                     } else {
                     } else {
-                        vertex_color = triangle.vertices[0].color;
+                        vertex_color = vertex0.color;
                     }
                     }
 
 
-                    auto uv = interpolate(
-                        triangle.vertices[0].tex_coord,
-                        triangle.vertices[1].tex_coord,
-                        triangle.vertices[2].tex_coord,
-                        barycentric);
+                    auto uv = interpolate(vertex0.tex_coord, vertex1.tex_coord, vertex2.tex_coord, barycentric);
 
 
                     // Calculate depth of fragment for fog
                     // Calculate depth of fragment for fog
                     float z = interpolate(triangle.vertices[0].position.z(), triangle.vertices[1].position.z(), triangle.vertices[2].position.z(), barycentric);
                     float z = interpolate(triangle.vertices[0].position.z(), triangle.vertices[1].position.z(), triangle.vertices[2].position.z(), barycentric);
@@ -525,8 +525,9 @@ DeviceInfo Device::info() const
     };
     };
 }
 }
 
 
-void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& transform, FloatMatrix3x3 const& normal_transform,
-    FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units)
+void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix3x3 const& normal_transform,
+    FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices,
+    Vector<size_t> const& enabled_texture_units)
 {
 {
     // At this point, the user has effectively specified that they are done with defining the geometry
     // At this point, the user has effectively specified that they are done with defining the geometry
     // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
     // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
@@ -598,19 +599,18 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
     }
     }
 
 
     // Now let's transform each triangle and send that to the GPU
     // Now let's transform each triangle and send that to the GPU
-    for (size_t i = 0; i < m_triangle_list.size(); i++) {
-        Triangle& triangle = m_triangle_list.at(i);
-
-        // First multiply the vertex by the MODELVIEW matrix and then the PROJECTION matrix
-        triangle.vertices[0].position = transform * triangle.vertices[0].position;
-        triangle.vertices[1].position = transform * triangle.vertices[1].position;
-        triangle.vertices[2].position = transform * triangle.vertices[2].position;
-
-        // Apply texture transformation
-        // FIXME: implement multi-texturing: texcoords should be stored per texture unit
-        triangle.vertices[0].tex_coord = texture_matrix * triangle.vertices[0].tex_coord;
-        triangle.vertices[1].tex_coord = texture_matrix * triangle.vertices[1].tex_coord;
-        triangle.vertices[2].tex_coord = texture_matrix * triangle.vertices[2].tex_coord;
+    auto const depth_half_range = (m_options.depth_max - m_options.depth_min) / 2;
+    auto const depth_halfway = (m_options.depth_min + m_options.depth_max) / 2;
+    for (auto& triangle : m_triangle_list) {
+        // Transform vertices into eye coordinates using the model-view transform
+        triangle.vertices[0].eye_coordinates = model_view_transform * triangle.vertices[0].position;
+        triangle.vertices[1].eye_coordinates = model_view_transform * triangle.vertices[1].position;
+        triangle.vertices[2].eye_coordinates = model_view_transform * triangle.vertices[2].position;
+
+        // Transform eye coordinates into clip coordinates using the projection transform
+        triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates;
+        triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates;
+        triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates;
 
 
         // At this point, we're in clip space
         // At this point, we're in clip space
         // Here's where we do the clipping. This is a really crude implementation of the
         // Here's where we do the clipping. This is a really crude implementation of the
@@ -632,16 +632,23 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
             continue;
             continue;
 
 
         for (auto& vec : m_clipped_vertices) {
         for (auto& vec : m_clipped_vertices) {
-            // perspective divide
-            float w = vec.position.w();
-            vec.position.set_x(vec.position.x() / w);
-            vec.position.set_y(vec.position.y() / w);
-            vec.position.set_z(vec.position.z() / w);
-            vec.position.set_w(1 / w);
-
-            // to screen space
-            vec.position.set_x(scr_width / 2 + vec.position.x() * scr_width / 2);
-            vec.position.set_y(scr_height / 2 - vec.position.y() * scr_height / 2);
+            // To normalized device coordinates (NDC)
+            auto const one_over_w = 1 / vec.clip_coordinates.w();
+            auto const ndc_coordinates = FloatVector4 {
+                vec.clip_coordinates.x() * one_over_w,
+                vec.clip_coordinates.y() * one_over_w,
+                vec.clip_coordinates.z() * one_over_w,
+                one_over_w,
+            };
+
+            // To window coordinates
+            // FIXME: implement viewport functionality
+            vec.window_coordinates = {
+                scr_width / 2 + ndc_coordinates.x() * scr_width / 2,
+                scr_height / 2 - ndc_coordinates.y() * scr_height / 2,
+                depth_half_range * ndc_coordinates.z() + depth_halfway,
+                ndc_coordinates.w(),
+            };
         }
         }
 
 
         Triangle tri;
         Triangle tri;
@@ -653,15 +660,13 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
         }
         }
     }
     }
 
 
-    for (size_t i = 0; i < m_processed_triangles.size(); i++) {
-        Triangle& triangle = m_processed_triangles.at(i);
-
+    for (auto& triangle : m_processed_triangles) {
         // Let's calculate the (signed) area of the triangle
         // Let's calculate the (signed) area of the triangle
         // https://cp-algorithms.com/geometry/oriented-triangle-area.html
         // https://cp-algorithms.com/geometry/oriented-triangle-area.html
-        float dxAB = triangle.vertices[0].position.x() - triangle.vertices[1].position.x(); // A.x - B.x
-        float dxBC = triangle.vertices[1].position.x() - triangle.vertices[2].position.x(); // B.X - C.x
-        float dyAB = triangle.vertices[0].position.y() - triangle.vertices[1].position.y();
-        float dyBC = triangle.vertices[1].position.y() - triangle.vertices[2].position.y();
+        float dxAB = triangle.vertices[0].window_coordinates.x() - triangle.vertices[1].window_coordinates.x(); // A.x - B.x
+        float dxBC = triangle.vertices[1].window_coordinates.x() - triangle.vertices[2].window_coordinates.x(); // B.X - C.x
+        float dyAB = triangle.vertices[0].window_coordinates.y() - triangle.vertices[1].window_coordinates.y();
+        float dyBC = triangle.vertices[1].window_coordinates.y() - triangle.vertices[2].window_coordinates.y();
         float area = (dxAB * dyBC) - (dxBC * dyAB);
         float area = (dxAB * dyBC) - (dxBC * dyAB);
 
 
         if (area == 0.0f)
         if (area == 0.0f)
@@ -690,6 +695,12 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
             triangle.vertices[2].normal.normalize();
             triangle.vertices[2].normal.normalize();
         }
         }
 
 
+        // Apply texture transformation
+        // FIXME: implement multi-texturing: texcoords should be stored per texture unit
+        triangle.vertices[0].tex_coord = texture_transform * triangle.vertices[0].tex_coord;
+        triangle.vertices[1].tex_coord = texture_transform * triangle.vertices[1].tex_coord;
+        triangle.vertices[2].tex_coord = texture_transform * triangle.vertices[2].tex_coord;
+
         submit_triangle(triangle, enabled_texture_units);
         submit_triangle(triangle, enabled_texture_units);
     }
     }
 }
 }

+ 1 - 1
Userland/Libraries/LibSoftGPU/Device.h

@@ -67,7 +67,7 @@ public:
 
 
     DeviceInfo info() const;
     DeviceInfo info() const;
 
 
-    void draw_primitives(PrimitiveType, FloatMatrix4x4 const& transform, FloatMatrix3x3 const& normal_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units);
+    void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix3x3 const& normal_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units);
     void resize(const Gfx::IntSize& min_size);
     void resize(const Gfx::IntSize& min_size);
     void clear_color(const FloatVector4&);
     void clear_color(const FloatVector4&);
     void clear_depth(float);
     void clear_depth(float);

+ 3 - 0
Userland/Libraries/LibSoftGPU/Vertex.h

@@ -14,6 +14,9 @@ namespace SoftGPU {
 
 
 struct Vertex {
 struct Vertex {
     FloatVector4 position;
     FloatVector4 position;
+    FloatVector4 eye_coordinates;
+    FloatVector4 clip_coordinates;
+    FloatVector4 window_coordinates;
     FloatVector4 color;
     FloatVector4 color;
     FloatVector4 tex_coord;
     FloatVector4 tex_coord;
     FloatVector3 normal;
     FloatVector3 normal;