1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 13:27:35 +00:00

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.
This commit is contained in:
Jelle Raaijmakers 2021-12-30 00:47:53 +01:00 committed by Andreas Kling
parent fd4d6b0031
commit fef7f7159c
5 changed files with 76 additions and 60 deletions

View file

@ -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();
} }

View file

@ -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 w1 = p1.clip_coordinates.w();
float w2 = p2.position.w(); float w2 = p2.clip_coordinates.w();
float x1 = clip_plane_normals[plane_index].dot(p1.position); float x1 = clip_plane_normals[plane_index].dot(p1.clip_coordinates);
float x2 = clip_plane_normals[plane_index].dot(p2.position); 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(curr_vec.clip_coordinates, static_cast<ClipPlane>(plane))) {
if (!point_within_clip_plane(prev_vec.position, 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);
} }

View file

@ -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 const v0 { static_cast<int>(vertex0.window_coordinates.x()), static_cast<int>(vertex0.window_coordinates.y()) };
IntVector2 v1 { (int)triangle.vertices[1].position.x(), (int)triangle.vertices[1].position.y() }; IntVector2 const v1 { static_cast<int>(vertex1.window_coordinates.x()), static_cast<int>(vertex1.window_coordinates.y()) };
IntVector2 v2 { (int)triangle.vertices[2].position.x(), (int)triangle.vertices[2].position.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); float z = interpolate(vertex0.window_coordinates.z(), vertex1.window_coordinates.z(), vertex2.window_coordinates.z(), barycentric);
z = options.depth_min + (options.depth_max - options.depth_min) * (z + 1) / 2;
// 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); auto const w_coordinates = FloatVector3 {
float interpolated_w = 1 / interpolated_reciprocal_w; vertex0.window_coordinates.w(),
barycentric = barycentric * FloatVector3(triangle.vertices[0].position.w(), triangle.vertices[1].position.w(), triangle.vertices[2].position.w()) * interpolated_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( vertex_color = interpolate(vertex0.color, vertex1.color, vertex2.color, barycentric);
triangle.vertices[0].color,
triangle.vertices[1].color,
triangle.vertices[2].color,
barycentric);
} else { } else {
vertex_color = triangle.vertices[0].color; vertex_color = vertex0.color;
} }
auto uv = interpolate( auto uv = interpolate(vertex0.tex_coord, vertex1.tex_coord, vertex2.tex_coord, barycentric);
triangle.vertices[0].tex_coord,
triangle.vertices[1].tex_coord,
triangle.vertices[2].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, void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix3x3 const& normal_transform,
FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices, Vector<size_t> const& enabled_texture_units) 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++) { auto const depth_half_range = (m_options.depth_max - m_options.depth_min) / 2;
Triangle& triangle = m_triangle_list.at(i); 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;
// First multiply the vertex by the MODELVIEW matrix and then the PROJECTION matrix // Transform eye coordinates into clip coordinates using the projection transform
triangle.vertices[0].position = transform * triangle.vertices[0].position; triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates;
triangle.vertices[1].position = transform * triangle.vertices[1].position; triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates;
triangle.vertices[2].position = transform * triangle.vertices[2].position; triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates;
// 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;
// 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 // To normalized device coordinates (NDC)
float w = vec.position.w(); auto const one_over_w = 1 / vec.clip_coordinates.w();
vec.position.set_x(vec.position.x() / w); auto const ndc_coordinates = FloatVector4 {
vec.position.set_y(vec.position.y() / w); vec.clip_coordinates.x() * one_over_w,
vec.position.set_z(vec.position.z() / w); vec.clip_coordinates.y() * one_over_w,
vec.position.set_w(1 / w); vec.clip_coordinates.z() * one_over_w,
one_over_w,
};
// to screen space // To window coordinates
vec.position.set_x(scr_width / 2 + vec.position.x() * scr_width / 2); // FIXME: implement viewport functionality
vec.position.set_y(scr_height / 2 - vec.position.y() * scr_height / 2); 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++) { for (auto& triangle : m_processed_triangles) {
Triangle& triangle = m_processed_triangles.at(i);
// 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 dxAB = triangle.vertices[0].window_coordinates.x() - triangle.vertices[1].window_coordinates.x(); // A.x - B.x
float dxBC = triangle.vertices[1].position.x() - triangle.vertices[2].position.x(); // B.X - C.x float dxBC = triangle.vertices[1].window_coordinates.x() - triangle.vertices[2].window_coordinates.x(); // B.X - C.x
float dyAB = triangle.vertices[0].position.y() - triangle.vertices[1].position.y(); float dyAB = triangle.vertices[0].window_coordinates.y() - triangle.vertices[1].window_coordinates.y();
float dyBC = triangle.vertices[1].position.y() - triangle.vertices[2].position.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);
} }
} }

View file

@ -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);

View file

@ -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;