mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 16:21:29 +00:00
LibGL: Move polygon clipping to Clipper
class
This code has also been optimised to be much more memory friendly by removing a _lot_ of extraneous copies. The result is that, when profiled, it's around 8x faster than the previous implementation. Co-Authored-By: Ali Mohammad Pur <ali.mpfard@gmail.com>
This commit is contained in:
parent
834f3c64f0
commit
e8cd89a538
Notes:
sideshowbarker
2024-07-18 18:31:54 +09:00
Author: https://github.com/Quaker762 Commit: https://github.com/SerenityOS/serenity/commit/e8cd89a5381 Pull-request: https://github.com/SerenityOS/serenity/pull/6375 Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/IdanHo Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/ccapitalK Reviewed-by: https://github.com/gmta Reviewed-by: https://github.com/sunverwerth ✅
|
@ -1,4 +1,5 @@
|
|||
set(SOURCES
|
||||
Clipper.cpp
|
||||
GLColor.cpp
|
||||
GLMat.cpp
|
||||
GLContext.cpp
|
||||
|
|
110
Userland/Libraries/LibGL/Clipper.cpp
Normal file
110
Userland/Libraries/LibGL/Clipper.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibGL/Clipper.h>
|
||||
|
||||
bool GL::Clipper::point_within_clip_plane(const FloatVector4& vertex, ClipPlane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case ClipPlane::LEFT:
|
||||
return vertex.x() > -vertex.w();
|
||||
case ClipPlane::RIGHT:
|
||||
return vertex.x() < vertex.w();
|
||||
case ClipPlane::TOP:
|
||||
return vertex.y() < vertex.w();
|
||||
case ClipPlane::BOTTOM:
|
||||
return vertex.y() > -vertex.w();
|
||||
case ClipPlane::NEAR:
|
||||
return vertex.z() > -vertex.w();
|
||||
case ClipPlane::FAR:
|
||||
return vertex.z() < vertex.w();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This needs to interpolate color/UV data as well!
|
||||
FloatVector4 GL::Clipper::clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClipPlane plane_index)
|
||||
{
|
||||
|
||||
//
|
||||
// This is an implementation of the Cyrus-Beck algorithm to clip lines against a plane
|
||||
// using the triangle's line segment in parametric form.
|
||||
// In this case, we find that n . [P(t) - plane] == 0 if the point lies on
|
||||
// the boundary, which in this case is the clip plane. We then substitute
|
||||
// P(t)= P1 + (P2-P1)*t (where P2 is a point inside the clipping boundary, and P1 is,
|
||||
// in this case, the point that lies outside the boundary due to our implementation of Sutherland-Hogdman)
|
||||
// into P(t) to arrive at the equation:
|
||||
//
|
||||
// n · [P1 + ((P2 - P1) * t) - plane] = 0.
|
||||
// Substitude seg_length = P2 - P1 (length of line segment) and dist = P1 - plane (distance from P1 to plane)
|
||||
//
|
||||
// By rearranging, we now end up with
|
||||
//
|
||||
// n·[P1 + (seg_length * t) - plane] = 0
|
||||
// n·(dist) + n·(seg_length * t) = 0
|
||||
// n·(seg_length*t) = -n·(dist)
|
||||
// Therefore
|
||||
// t = (-n·(dist))/(n·seg_length)
|
||||
//
|
||||
// NOTE: n is the normal vector to the plane we are clipping against.
|
||||
//
|
||||
// Proof of algorithm found here
|
||||
// http://studymaterial.unipune.ac.in:8080/jspui/bitstream/123456789/4843/1/Cyrus_Beck_Algo.pdf
|
||||
// And here (slightly more intuitive with a better diagram)
|
||||
// https://www.csee.umbc.edu/~rheingan/435/pages/res/gen-5.Clipping-single-page-0.html
|
||||
|
||||
FloatVector4 seg_length = vec - prev_vec; // P2 - P1
|
||||
FloatVector4 dist = prev_vec - clip_planes[plane_index]; // Distance from "out of bounds" vector to plane
|
||||
|
||||
float plane_normal_line_segment_dot_product = clip_plane_normals[plane_index].dot(seg_length);
|
||||
float neg_plane_normal_dist_dot_procut = -clip_plane_normals[plane_index].dot(dist);
|
||||
float t = (plane_normal_line_segment_dot_product / neg_plane_normal_dist_dot_procut);
|
||||
|
||||
// P(t) = P1 + (t * (P2 - P1))
|
||||
FloatVector4 lerped_vec = prev_vec + (seg_length * t);
|
||||
|
||||
return lerped_vec;
|
||||
}
|
||||
|
||||
// FIXME: Getting too close to zNear causes VERY serious artifacting. Should we cull the whole triangle??
|
||||
void GL::Clipper::clip_triangle_against_frustum(Vector<FloatVector4>& input_verts)
|
||||
{
|
||||
Vector<FloatVector4, 6> clipped_polygon;
|
||||
Vector<FloatVector4, 6> input = input_verts;
|
||||
Vector<FloatVector4, 6>* current = &clipped_polygon;
|
||||
Vector<FloatVector4, 6>* output_list = &input;
|
||||
ScopeGuard guard { [&] { input_verts = *output_list; } };
|
||||
|
||||
for (u8 plane = 0; plane < NUMBER_OF_CLIPPING_PLANES; plane++) {
|
||||
swap(current, output_list);
|
||||
clipped_polygon.clear();
|
||||
|
||||
if (current->size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save me, C++23
|
||||
for (size_t i = 0; i < current->size(); i++) {
|
||||
const auto& curr_vec = current->at(i);
|
||||
const auto& prev_vec = current->at((i - 1) % current->size());
|
||||
|
||||
if (point_within_clip_plane(curr_vec, static_cast<ClipPlane>(plane))) {
|
||||
if (!point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
|
||||
FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
|
||||
clipped_polygon.append(intersect);
|
||||
}
|
||||
|
||||
clipped_polygon.append(curr_vec);
|
||||
} else if (point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
|
||||
FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
|
||||
clipped_polygon.append(intersect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
Userland/Libraries/LibGL/Clipper.h
Normal file
59
Userland/Libraries/LibGL/Clipper.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Vector4.h>
|
||||
|
||||
namespace GL {
|
||||
|
||||
class Clipper final {
|
||||
enum ClipPlane : u8 {
|
||||
LEFT = 0,
|
||||
RIGHT,
|
||||
TOP,
|
||||
BOTTOM,
|
||||
NEAR,
|
||||
FAR
|
||||
};
|
||||
|
||||
static constexpr u8 NUMBER_OF_CLIPPING_PLANES = 6;
|
||||
static constexpr u8 MAX_CLIPPED_VERTS = 6;
|
||||
|
||||
static constexpr FloatVector4 clip_planes[] = {
|
||||
{ -1, 0, 0, 1 }, // Left Plane
|
||||
{ 1, 0, 0, 1 }, // Right Plane
|
||||
{ 0, 1, 0, 1 }, // Top Plane
|
||||
{ 0, -1, 0, 1 }, // Bottom plane
|
||||
{ 0, 0, 1, 1 }, // Near Plane
|
||||
{ 0, 0, -1, 1 } // Far Plane
|
||||
};
|
||||
|
||||
static constexpr FloatVector4 clip_plane_normals[] = {
|
||||
{ 1, 0, 0, 1 }, // Left Plane
|
||||
{ -1, 0, 0, 1 }, // Right Plane
|
||||
{ 0, -1, 0, 1 }, // Top Plane
|
||||
{ 0, 1, 0, 1 }, // Bottom plane
|
||||
{ 0, 0, -1, 1 }, // Near Plane
|
||||
{ 0, 0, 1, 1 } // Far Plane
|
||||
};
|
||||
|
||||
public:
|
||||
Clipper() { }
|
||||
|
||||
void clip_triangle_against_frustum(Vector<FloatVector4>& input_vecs);
|
||||
const Vector<FloatVector4, MAX_CLIPPED_VERTS>& clipped_verts() const;
|
||||
|
||||
private:
|
||||
bool point_within_clip_plane(const FloatVector4& vertex, ClipPlane plane);
|
||||
FloatVector4 clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClipPlane plane_index);
|
||||
|
||||
private:
|
||||
Vector<FloatVector4, MAX_CLIPPED_VERTS> m_clipped_verts;
|
||||
};
|
||||
|
||||
}
|
|
@ -22,117 +22,9 @@ using AK::dbgln;
|
|||
|
||||
namespace GL {
|
||||
|
||||
static constexpr size_t NUM_CLIP_PLANES = 6;
|
||||
|
||||
static FloatVector4 clip_planes[] = {
|
||||
{ -1, 0, 0, 1 }, // Left Plane
|
||||
{ 1, 0, 0, 1 }, // Right Plane
|
||||
{ 0, 1, 0, 1 }, // Top Plane
|
||||
{ 0, -1, 0, 1 }, // Bottom plane
|
||||
{ 0, 0, 1, 1 }, // Near Plane
|
||||
{ 0, 0, -1, 1 } // Far Plane
|
||||
};
|
||||
|
||||
static FloatVector4 clip_plane_normals[] = {
|
||||
{ 1, 0, 0, 1 }, // Left Plane
|
||||
{ -1, 0, 0, 1 }, // Right Plane
|
||||
{ 0, -1, 0, 1 }, // Top Plane
|
||||
{ 0, 1, 0, 1 }, // Bottom plane
|
||||
{ 0, 0, -1, 1 }, // Near Plane
|
||||
{ 0, 0, 1, 1 } // Far Plane
|
||||
};
|
||||
|
||||
enum ClippingPlane {
|
||||
LEFT = 0,
|
||||
RIGHT = 1,
|
||||
TOP = 2,
|
||||
BOTTOM = 3,
|
||||
NEAR = 4,
|
||||
FAR = 5
|
||||
};
|
||||
|
||||
// FIXME: We should set this up when we create the context!
|
||||
static constexpr size_t MATRIX_STACK_LIMIT = 1024;
|
||||
|
||||
// FIXME: Change this to accept a vertex!
|
||||
// Determines whether or not a vertex is inside the frustum for a given plane
|
||||
static bool vert_inside_plane(const FloatVector4& vec, ClippingPlane plane)
|
||||
{
|
||||
switch (plane) {
|
||||
case ClippingPlane::LEFT:
|
||||
return vec.x() > -vec.w();
|
||||
case ClippingPlane::RIGHT:
|
||||
return vec.x() < vec.w();
|
||||
case ClippingPlane::TOP:
|
||||
return vec.y() < vec.w();
|
||||
case ClippingPlane::BOTTOM:
|
||||
return vec.y() > -vec.w();
|
||||
case ClippingPlane::NEAR:
|
||||
return vec.z() > -vec.w();
|
||||
case ClippingPlane::FAR:
|
||||
return vec.z() < vec.w();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: This needs to interpolate color/UV data as well!
|
||||
static FloatVector4 clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClippingPlane plane_index)
|
||||
{
|
||||
// https://github.com/fogleman/fauxgl/blob/master/clipping.go#L20
|
||||
FloatVector4 u, w;
|
||||
FloatVector4 ret = prev_vec;
|
||||
FloatVector4 plane = clip_planes[plane_index];
|
||||
FloatVector4 plane_normal = clip_plane_normals[plane_index];
|
||||
|
||||
u = vec;
|
||||
u -= prev_vec;
|
||||
w = prev_vec;
|
||||
w -= plane;
|
||||
float d = plane_normal.dot(u);
|
||||
float n = -plane_normal.dot(w);
|
||||
|
||||
ret += (u * (n / d));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// https://groups.csail.mit.edu/graphics/classes/6.837/F04/lectures/07_Pipeline_II.pdf
|
||||
// This is a really rough implementation of the Sutherland-Hodgman algorithm in clip-space
|
||||
static void clip_triangle_against_frustum(Vector<FloatVector4>& in_vec)
|
||||
{
|
||||
Vector<FloatVector4> clipped_polygon = in_vec; // in_vec = subjectPolygon, clipped_polygon = outputList
|
||||
for (size_t i = 0; i < NUM_CLIP_PLANES; i++) // Test against each clip plane
|
||||
{
|
||||
ClippingPlane plane = static_cast<ClippingPlane>(i); // Hahaha, what the fuck
|
||||
in_vec = clipped_polygon;
|
||||
clipped_polygon.clear();
|
||||
|
||||
// Prevent a crash from .at() undeflow
|
||||
if (in_vec.size() == 0)
|
||||
return;
|
||||
|
||||
FloatVector4 prev_vec = in_vec.at(in_vec.size() - 1);
|
||||
|
||||
for (size_t j = 0; j < in_vec.size(); j++) // Perform this for each vertex
|
||||
{
|
||||
const FloatVector4& vec = in_vec.at(j);
|
||||
if (vert_inside_plane(vec, plane)) {
|
||||
if (!vert_inside_plane(prev_vec, plane)) {
|
||||
FloatVector4 intersect = clip_intersection_point(prev_vec, vec, plane);
|
||||
clipped_polygon.append(intersect);
|
||||
}
|
||||
|
||||
clipped_polygon.append(vec);
|
||||
} else if (vert_inside_plane(prev_vec, plane)) {
|
||||
FloatVector4 intersect = clip_intersection_point(prev_vec, vec, plane);
|
||||
clipped_polygon.append(intersect);
|
||||
}
|
||||
|
||||
prev_vec = vec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoftwareGLContext::SoftwareGLContext(Gfx::Bitmap& frontbuffer)
|
||||
: m_frontbuffer(frontbuffer)
|
||||
, m_rasterizer(frontbuffer.size())
|
||||
|
@ -296,7 +188,7 @@ void SoftwareGLContext::gl_end()
|
|||
vecs.append(veca);
|
||||
vecs.append(vecb);
|
||||
vecs.append(vecc);
|
||||
clip_triangle_against_frustum(vecs);
|
||||
m_clipper.clip_triangle_against_frustum(vecs);
|
||||
|
||||
// TODO: Copy color and UV information too!
|
||||
for (size_t vec_idx = 0; vec_idx < vecs.size(); vec_idx++) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Clipper.h"
|
||||
#include "GLContext.h"
|
||||
#include "GLStruct.h"
|
||||
#include "SoftwareRasterizer.h"
|
||||
|
@ -61,9 +62,9 @@ private:
|
|||
FloatVector4 m_clear_color = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
FloatVector4 m_current_vertex_color = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
|
||||
Vector<GLVertex> vertex_list;
|
||||
Vector<GLTriangle> triangle_list;
|
||||
Vector<GLTriangle> processed_triangles;
|
||||
Vector<GLVertex, 96> vertex_list;
|
||||
Vector<GLTriangle, 32> triangle_list;
|
||||
Vector<GLTriangle, 32> processed_triangles;
|
||||
|
||||
GLenum m_error = GL_NO_ERROR;
|
||||
bool m_in_draw_state = false;
|
||||
|
@ -74,6 +75,8 @@ private:
|
|||
|
||||
NonnullRefPtr<Gfx::Bitmap> m_frontbuffer;
|
||||
|
||||
Clipper m_clipper;
|
||||
|
||||
SoftwareRasterizer m_rasterizer;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue