From e266c7b234ccad8c4c22cf67de466eaea4d0dd58 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Thu, 24 Sep 2026 18:08:36 +0800 Subject: [PATCH] Draw the Toolpaths Top-Down When the Camera Looks Down on the Print The segments come in print order, bottom layer first, which seen from above is back to front: every hidden fragment is shaded before the one that covers it, and on an integrated GPU that overdraw is most of the frame. Drawing the instances last to first whenever the camera looks down lets the depth test reject the hidden fragments instead. Side views and views from below keep the print order, and the shadow-caster pass is unchanged. --- src/libvgcode/src/Shaders.hpp | 7 ++++++- src/libvgcode/src/ViewerImpl.cpp | 11 +++++++++++ src/libvgcode/src/ViewerImpl.hpp | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libvgcode/src/Shaders.hpp b/src/libvgcode/src/Shaders.hpp index 596b42df5c..9f546d6dc8 100644 --- a/src/libvgcode/src/Shaders.hpp +++ b/src/libvgcode/src/Shaders.hpp @@ -34,6 +34,10 @@ static const char* Segments_Vertex_Shader = // ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not // world_position, so the caster would write a depth the receiver never looks up. "uniform float bias_scale;\n" +// draw the instances last to first, top layers before the ones they hide, so that early depth +// rejection discards most of the hidden fragments; set when the camera looks down on the print +"uniform int reverse_order;\n" +"uniform int instance_count;\n" "in int vertex_id;\n" "out vec3 color;\n" "// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n" @@ -59,7 +63,8 @@ static const char* Segments_Vertex_Shader = " return top_diffuse + front_diffuse + top_specular;\n" "}\n" "void main() {\n" -" int id_a = int(texelFetch(segment_index_tex, gl_InstanceID).r);\n" +" int instance = (reverse_order != 0) ? instance_count - 1 - gl_InstanceID : gl_InstanceID;\n" +" int id_a = int(texelFetch(segment_index_tex, instance).r);\n" " int id_b = id_a + 1;\n" " vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n" " vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n" diff --git a/src/libvgcode/src/ViewerImpl.cpp b/src/libvgcode/src/ViewerImpl.cpp index b1a7550b42..8212717931 100644 --- a/src/libvgcode/src/ViewerImpl.cpp +++ b/src/libvgcode/src/ViewerImpl.cpp @@ -763,6 +763,8 @@ void ViewerImpl::init(const std::string& opengl_context_version) m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex"); m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex"); m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex"); + m_uni_segments_reverse_order_id = glGetUniformLocation(m_segments_shader_id, "reverse_order"); + m_uni_segments_instance_count_id = glGetUniformLocation(m_segments_shader_id, "instance_count"); // ORCA: realistic view m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map"); m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp"); @@ -2090,6 +2092,15 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data())); glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data())); glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data())); + // The segments come in print order, bottom layer first. Seen from above, that is back to front, + // and every hidden fragment is shaded before the one that covers it. Drawing them last to first + // lets the depth test reject the hidden ones instead. The camera looks down when the world's + // up axis points towards it, which is the view matrix's (2, 2) entry being positive. + const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f; + glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0)); +#ifndef ENABLE_OPENGL_ES + glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast(m_enabled_segments_count))); +#endif // ENABLE_OPENGL_ES // ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades // with the lookup off. glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit)); diff --git a/src/libvgcode/src/ViewerImpl.hpp b/src/libvgcode/src/ViewerImpl.hpp index c9d658beb1..707f0f6bb7 100644 --- a/src/libvgcode/src/ViewerImpl.hpp +++ b/src/libvgcode/src/ViewerImpl.hpp @@ -362,6 +362,8 @@ private: int m_uni_segments_height_width_angle_tex_id{ -1 }; int m_uni_segments_colors_tex_id{ -1 }; int m_uni_segments_segment_index_tex_id{ -1 }; + int m_uni_segments_reverse_order_id{ -1 }; + int m_uni_segments_instance_count_id{ -1 }; int m_uni_segments_shadow_map_id{ -1 }; int m_uni_segments_shadow_light_vp_id{ -1 }; int m_uni_segments_shadow_intensity_id{ -1 };