mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 10:22:05 -05: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:
parent
fd4d6b0031
commit
fef7f7159c
5 changed files with 76 additions and 60 deletions
|
@ -289,7 +289,7 @@ void SoftwareGLContext::gl_end()
|
|||
mv_elements[2][0], mv_elements[2][1], mv_elements[2][2]);
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// "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));
|
||||
|
||||
Vertex out;
|
||||
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.tex_coord = p1.tex_coord * (1 - a) + p2.tex_coord * a;
|
||||
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& 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));
|
||||
write_to->append(intersect);
|
||||
}
|
||||
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));
|
||||
write_to->append(intersect);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
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);
|
||||
if (area == 0)
|
||||
return;
|
||||
|
||||
float one_over_area = 1.0f / area;
|
||||
auto const one_over_area = 1.0f / area;
|
||||
|
||||
FloatVector4 src_constant {};
|
||||
float src_factor_src_alpha = 0;
|
||||
|
@ -282,9 +287,7 @@ static void rasterize_triangle(const RasterizerOptions& options, Gfx::Bitmap& re
|
|||
continue;
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
FloatVector4 vertex_color;
|
||||
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 {
|
||||
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
|
||||
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
|
||||
// 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
|
||||
for (size_t i = 0; i < m_triangle_list.size(); i++) {
|
||||
Triangle& triangle = m_triangle_list.at(i);
|
||||
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;
|
||||
|
||||
// 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;
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
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 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 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 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;
|
||||
|
@ -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
|
||||
// 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);
|
||||
|
||||
if (area == 0.0f)
|
||||
|
@ -690,6 +695,12 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
|
||||
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 clear_color(const FloatVector4&);
|
||||
void clear_depth(float);
|
||||
|
|
|
@ -14,6 +14,9 @@ namespace SoftGPU {
|
|||
|
||||
struct Vertex {
|
||||
FloatVector4 position;
|
||||
FloatVector4 eye_coordinates;
|
||||
FloatVector4 clip_coordinates;
|
||||
FloatVector4 window_coordinates;
|
||||
FloatVector4 color;
|
||||
FloatVector4 tex_coord;
|
||||
FloatVector3 normal;
|
||||
|
|
Loading…
Add table
Reference in a new issue