From bc6d358cc3d9ee58b7eccccc145cdb520184e707 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Fri, 5 Jun 2026 19:23:04 +0800 Subject: [PATCH 1/4] [Feat]- Render_Optimize: CPU Frustum culling GLVolume --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DScene.cpp | 13 ++- src/slic3r/GUI/3DScene.hpp | 4 +- src/slic3r/GUI/Camera.cpp | 55 +++++++++++ src/slic3r/GUI/Camera.hpp | 14 ++- src/slic3r/GUI/GCodeViewer.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/Utils/Frustum.cpp | 176 +++++++++++++++++++++++++++++++++ src/slic3r/Utils/Frustum.hpp | 75 ++++++++++++++ 9 files changed, 338 insertions(+), 11 deletions(-) create mode 100644 src/slic3r/Utils/Frustum.cpp create mode 100644 src/slic3r/Utils/Frustum.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9b70587c054..eb7c17988f6 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -563,6 +563,8 @@ set(SLIC3R_GUI_SOURCES Utils/Flashforge.hpp Utils/FontConfigHelp.cpp Utils/FontConfigHelp.hpp + Utils/Frustum.cpp + Utils/Frustum.hpp Utils/HexFile.cpp Utils/HexFile.hpp Utils/Http.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d10671c0475..67c9709bbc2 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -7,7 +7,7 @@ #include "Plater.hpp" #include "BitmapCache.hpp" #include "Camera.hpp" - +#include "Frustum.hpp" #include "libslic3r/BuildVolume.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -878,12 +878,13 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool& enable_suppo // BBS: add outline drawing logic void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, - const Transform3d& view_matrix, - const Transform3d& projection_matrix, + const GUI::Camera& camera, const GUI::Size& cnv_size, std::function filter_func, bool partly_inside_enable) const { + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); if (to_render.empty()) return; @@ -905,6 +906,12 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, glsafe(::glDisable(GL_CULL_FACE)); for (GLVolumeWithIdAndZ& volume : to_render) { + //CPU Frustum culling + auto _worldAABB = volume.first->transformed_bounding_box(); + if (!camera.GetFrustum().Intersects(_worldAABB)) + { + continue; + } #if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT if (type == ERenderType::Transparent) { volume.first->force_transparent = true; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index acdacac3d1b..68c6ace8c6b 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -41,6 +41,7 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C namespace Slic3r { namespace GUI { class Size; + class Camera; } class SLAPrintObject; @@ -475,8 +476,7 @@ class GLVolumeCollection //BBS: add outline drawing logic void render(ERenderType type, bool disable_cullface, - const Transform3d & view_matrix, - const Transform3d& projection_matrix, + const GUI::Camera& camera, const GUI::Size& cnv_size, std::function filter_func = std::function(), bool partly_inside_enable =true diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index fd18a1cefdd..2508f6553ae 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -679,6 +679,61 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up update_zenit(); } +void Camera::UpdateFrustum() +{ + // fast extraction of frustum planes from the view-projection matrix + const Matrix4d& vp = m_projection_matrix.matrix() * m_view_matrix.matrix(); + const Eigen::Matrix4d& vp_matrix = vp.eval(); + const double* vp_data = vp_matrix.data(); + // left + float a = vp_data[0 * 4 + 3] + vp_data[0 * 4 + 0]; + float b = vp_data[1 * 4 + 3] + vp_data[1 * 4 + 0]; + float c = vp_data[2 * 4 + 3] + vp_data[2 * 4 + 0]; + float d = vp_data[3 * 4 + 3] + vp_data[3 * 4 + 0]; + m_frustum.planes[0].SetEquation(a, b, c, d); + m_frustum.planes[0].Normalize(); + + // right + a = vp_data[0 * 4 + 3] - vp_data[0 * 4 + 0]; + b = vp_data[1 * 4 + 3] - vp_data[1 * 4 + 0]; + c = vp_data[2 * 4 + 3] - vp_data[2 * 4 + 0]; + d = vp_data[3 * 4 + 3] - vp_data[3 * 4 + 0]; + m_frustum.planes[1].SetEquation(a, b, c, d); + m_frustum.planes[1].Normalize(); + + // bottom + a = vp_data[0 * 4 + 3] + vp_data[0 * 4 + 1]; + b = vp_data[1 * 4 + 3] + vp_data[1 * 4 + 1]; + c = vp_data[2 * 4 + 3] + vp_data[2 * 4 + 1]; + d = vp_data[3 * 4 + 3] + vp_data[3 * 4 + 1]; + m_frustum.planes[2].SetEquation(a, b, c, d); + m_frustum.planes[2].Normalize(); + + // top + a = vp_data[0 * 4 + 3] - vp_data[0 * 4 + 1]; + b = vp_data[1 * 4 + 3] - vp_data[1 * 4 + 1]; + c = vp_data[2 * 4 + 3] - vp_data[2 * 4 + 1]; + d = vp_data[3 * 4 + 3] - vp_data[3 * 4 + 1]; + m_frustum.planes[3].SetEquation(a, b, c, d); + m_frustum.planes[3].Normalize(); + + // near + a = vp_data[0 * 4 + 3] + vp_data[0 * 4 + 2]; + b = vp_data[1 * 4 + 3] + vp_data[1 * 4 + 2]; + c = vp_data[2 * 4 + 3] + vp_data[2 * 4 + 2]; + d = vp_data[3 * 4 + 3] + vp_data[3 * 4 + 2]; + m_frustum.planes[4].SetEquation(a, b, c, d); + m_frustum.planes[4].Normalize(); + + // far + a = vp_data[0 * 4 + 3] - vp_data[0 * 4 + 2]; + b = vp_data[1 * 4 + 3] - vp_data[1 * 4 + 2]; + c = vp_data[2 * 4 + 3] - vp_data[2 * 4 + 2]; + d = vp_data[3 * 4 + 3] - vp_data[3 * 4 + 2]; + m_frustum.planes[5].SetEquation(a, b, c, d); + m_frustum.planes[5].Normalize(); +} + void Camera::set_default_orientation() { m_zenit = 45.0f; diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 48990f593c4..0d8af0ea5f6 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -3,7 +3,7 @@ #include "libslic3r/BoundingBox.hpp" #include "3DScene.hpp" -#include +#include "../Utils/Frustum.hpp" namespace Slic3r { namespace GUI { @@ -56,6 +56,7 @@ struct Camera std::pair m_frustrum_zs; BoundingBoxf3 m_scene_box; + Frustum m_frustum; public: Camera() { set_default_orientation(); } @@ -167,6 +168,17 @@ struct Camera double max_zoom() const { return 250.0; } double min_zoom() const { return 0.2 * calc_zoom_to_bounding_box_factor(m_scene_box); } + /// + /// get the current frustrum planes. The planes are in world space and normalized (the normal vector has unit length). + /// + /// + const Frustum& GetFrustum() const { return m_frustum; } + + /// + /// extract the planes of the frustrum from the current projection and view matrix and update m_frustrum + /// + void UpdateFrustum(); + private: // returns tight values for nearZ and farZ plane around the given bounding box // the camera MUST be outside of the bounding box in eye coordinate of the given box diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 23f9788899a..992d573726f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4040,7 +4040,7 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height) const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("z_far", camera.get_far_z()); shader->set_uniform("z_near", camera.get_near_z()); - m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height}); + m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera, {canvas_width, canvas_height}); shader->set_uniform("emission_factor", 0.0f); shader->stop_using(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d08ddd2414c..087b33ab61b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1927,7 +1927,7 @@ void GLCanvas3D::render(bool only_init) } camera.apply_projection(_max_bounding_box(true, true, true)); - + camera.UpdateFrustum(); wxGetApp().imgui()->new_frame(); if (m_picking_enabled) { @@ -7415,7 +7415,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; const Camera& camera = wxGetApp().plater()->get_camera(); - m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [object_id](const GLVolume& volume) { + m_volumes.render(type, false, camera, cvn_size, [object_id](const GLVolume& volume) { // Which volume to paint without the layer height profile shader? return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); }); @@ -7431,7 +7431,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with //BBS:add assemble view related logic // do not cull backfaces to show broken geometry, if any const Camera& camera = wxGetApp().plater()->get_camera(); - m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, m_picking_enabled, camera, cvn_size, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier && !volume.is_wipe_tower; } @@ -7466,7 +7466,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with }*/ const Camera& camera = wxGetApp().plater()->get_camera(); //BBS:add assemble view related logic - m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [canvas_type](const GLVolume& volume) { + m_volumes.render(type, false, camera, cvn_size, [canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier; } diff --git a/src/slic3r/Utils/Frustum.cpp b/src/slic3r/Utils/Frustum.cpp new file mode 100644 index 00000000000..cfb7d3a6bfd --- /dev/null +++ b/src/slic3r/Utils/Frustum.cpp @@ -0,0 +1,176 @@ +#include "Frustum.hpp" + +namespace Slic3r +{ + +float Plane::PointToPlaneDistance(const Vec3f& p) const +{ + return m_normal.dot(p) + m_distance; +} + +bool Plane::PointInFrontOfPlane(const Vec3f& p) const +{ + IntersectionResult res = Intersect(p); + return res != IntersectionResult::Outside; +} + +void Plane::Normalize() +{ + double magnitude = m_normal.norm(); + if (magnitude > EPSILON) + { + m_normal /= magnitude; + m_distance /= magnitude; + } +} + +IntersectionResult Plane::Intersect(const BoundingBoxf3& box) const +{ + Vec3f boxMin = box.min.cast(); + Vec3f boxMax = box.max.cast(); + const Vec3f aabbCenter = (boxMin + boxMax) * 0.5f; + const Vec3f aabbHalfSize = boxMax - aabbCenter; + const float projRadius = aabbHalfSize.x() * std::abs(m_normal.x()) + + aabbHalfSize.y() * std::abs(m_normal.y()) + + aabbHalfSize.z() * std::abs(m_normal.z()); + const float aabbCenter2Plane = aabbCenter.dot(m_normal) + m_distance; + + if (std::abs(aabbCenter2Plane) <= projRadius) + { + return IntersectionResult::Intersect; + } + if (aabbCenter2Plane > projRadius) + { + return IntersectionResult::Inside; + } + return IntersectionResult::Outside; +} + +IntersectionResult Plane::Intersect(const Plane& other) const +{ + const Vec3f& n1 = m_normal; + const Vec3f& n2 = other.GetNormal(); + const float dot = n1.dot(n2); + const float norm_prod = n1.norm() * n2.norm(); + const bool parallel = std::abs(dot - norm_prod) < EPSILON || std::abs(dot + norm_prod) < EPSILON; + + if (!parallel) + { + return IntersectionResult::Intersect; + } + + float scale = 1.0f; + if (n1.x() != 0.0f) + { + scale = n2.x() / n1.x(); + } else if (n1.y() != 0.0f) + { + scale = n2.y() / n1.y(); + } else if (n1.z() != 0.0f) + { + scale = n2.z() / n1.z(); + } + + if (std::abs(m_distance * scale - other.GetDistance()) < EPSILON) + { + return IntersectionResult::Inside; + } + return IntersectionResult::Outside; +} + +IntersectionResult Plane::Intersect(const Vec3f& p) const +{ + const float dist = PointToPlaneDistance(p); + if (dist > 0) + { + return IntersectionResult::Inside; + } + if (dist < 0) + { + return IntersectionResult::Outside; + } + return IntersectionResult::Intersect; +} + +IntersectionResult Plane::Intersect(const Vec3f& p0, const Vec3f& p1) const +{ + const bool p0Inside = PointInFrontOfPlane(p0); + const bool p1Inside = PointInFrontOfPlane(p1); + + if (p0Inside && p1Inside) + { + return IntersectionResult::Inside; + } + if (!p0Inside && !p1Inside) + { + return IntersectionResult::Outside; + } + return IntersectionResult::Intersect; +} + +IntersectionResult Plane::Intersect(const Vec3f& p0, const Vec3f& p1, const Vec3f& p2) const +{ + const bool p0Inside = PointInFrontOfPlane(p0); + const bool p1Inside = PointInFrontOfPlane(p1); + const bool p2Inside = PointInFrontOfPlane(p2); + + if (p0Inside && p1Inside && p2Inside) + { + return IntersectionResult::Inside; + } + if (!p0Inside && !p1Inside && !p2Inside) + { + return IntersectionResult::Outside; + } + return IntersectionResult::Intersect; +} + +bool Frustum::Intersects(const BoundingBoxf3& box) const +{ + for (const Plane& plane : planes) + { + if (plane.Intersect(box) == IntersectionResult::Outside) + { + return false; + } + } + return true; +} + +bool Frustum::Intersects(const Vec3f& p0) const +{ + for (const Plane& plane : planes) + { + if (plane.Intersect(p0) == IntersectionResult::Outside) + { + return false; + } + } + return true; +} + +bool Frustum::Intersects(const Vec3f& p0, const Vec3f& p1) const +{ + for (const Plane& plane : planes) + { + if (plane.Intersect(p0, p1) == IntersectionResult::Outside) + { + return false; + } + } + return true; +} + +bool Frustum::Intersects(const Vec3f& p0, const Vec3f& p1, const Vec3f& p2) const +{ + for (const Plane& plane : planes) + { + if (plane.Intersect(p0, p1, p2) == IntersectionResult::Outside) + { + return false; + } + } + return true; +} + +} // namespace Slic3r diff --git a/src/slic3r/Utils/Frustum.hpp b/src/slic3r/Utils/Frustum.hpp new file mode 100644 index 00000000000..43904ebdab8 --- /dev/null +++ b/src/slic3r/Utils/Frustum.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "BoundingBox.hpp" + +namespace Slic3r +{ + enum class IntersectionResult + { + Outside, + Intersect, + Inside + }; + + // Plane defined by ax + by + cz + d = 0, where (a,b,c) is normal and d is distance + class Plane + { + public: + Plane() = default; + ~Plane() = default; + + Plane(const Vec4f& planeEquation) + : m_normal(planeEquation.head<3>()) + , m_distance(planeEquation.w()) + {} + + void SetEquation(const Vec4f& planeEquation) + { + SetEquation(planeEquation.head<3>(), planeEquation.w()); + } + + void SetEquation(float a, float b, float c, float d) + { + SetEquation(Vec3f(a, b, c), d); + } + + void SetEquation(const Vec3f& normal, float distance) + { + m_normal = normal; + m_distance = distance; + } + + void Normalize(); + + const Vec3f& GetNormal() const { return m_normal; } + float GetDistance() const { return m_distance; } + + float PointToPlaneDistance(const Vec3f& p) const; + bool PointInFrontOfPlane(const Vec3f& p) const; + + IntersectionResult Intersect(const BoundingBoxf3& box) const; + IntersectionResult Intersect(const Plane& other) const; + IntersectionResult Intersect(const Vec3f& p) const; + IntersectionResult Intersect(const Vec3f& p0, const Vec3f& p1) const; + IntersectionResult Intersect(const Vec3f& p0, const Vec3f& p1, const Vec3f& p2) const; + + private: + Vec3f m_normal = Vec3f::Zero(); + float m_distance = 0.0; + }; + + class Frustum + { + public: + Frustum() = default; + ~Frustum() = default; + + bool Intersects(const BoundingBoxf3& box) const; + bool Intersects(const Vec3f& p0) const; + bool Intersects(const Vec3f& p0, const Vec3f& p1) const; + bool Intersects(const Vec3f& p0, const Vec3f& p1, const Vec3f& p2) const; + + public: + Plane planes[6]; + }; +} // namespace Slic3r From f7dfa4592d4c3a68596c753f41ef2c04622c877f Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Mon, 8 Jun 2026 21:15:22 +0800 Subject: [PATCH 2/4] [Feat]: render optimize, glToolBar refactor --- resources/shaders/110/background.vs | 3 +- resources/shaders/110/flat_texture.vs | 7 ++- resources/shaders/140/background.vs | 3 +- resources/shaders/140/flat_texture.vs | 7 ++- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++-- src/slic3r/GUI/GLTexture.cpp | 75 ++++++++++++++++++++------- src/slic3r/GUI/GLTexture.hpp | 4 ++ 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/resources/shaders/110/background.vs b/resources/shaders/110/background.vs index 9b56ab43a26..e8e16815591 100644 --- a/resources/shaders/110/background.vs +++ b/resources/shaders/110/background.vs @@ -1,12 +1,11 @@ #version 110 attribute vec3 v_position; -attribute vec2 v_tex_coord; varying vec2 tex_coord; void main() { - tex_coord = v_tex_coord; + tex_coord = v_position.xy * 0.5 + 0.5; gl_Position = vec4(v_position, 1.0); } diff --git a/resources/shaders/110/flat_texture.vs b/resources/shaders/110/flat_texture.vs index dc4868b04df..bdfe3636b4e 100644 --- a/resources/shaders/110/flat_texture.vs +++ b/resources/shaders/110/flat_texture.vs @@ -2,14 +2,17 @@ uniform mat4 view_model_matrix; uniform mat4 projection_matrix; +uniform mat3 u_uvTransformMatrix; attribute vec3 v_position; -attribute vec2 v_tex_coord; varying vec2 tex_coord; void main() { - tex_coord = v_tex_coord; + vec2 texCoord; + texCoord.x = v_position.x; + texCoord.y = -v_position.y; + tex_coord = (u_uvTransformMatrix * vec3(texCoord, 1.0)).xy; gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); } diff --git a/resources/shaders/140/background.vs b/resources/shaders/140/background.vs index 13609b3a210..2fe039854f7 100644 --- a/resources/shaders/140/background.vs +++ b/resources/shaders/140/background.vs @@ -1,12 +1,11 @@ #version 140 in vec3 v_position; -in vec2 v_tex_coord; out vec2 tex_coord; void main() { - tex_coord = v_tex_coord; + tex_coord = tex_coord = v_position.xy * 0.5 + 0.5; gl_Position = vec4(v_position, 1.0); } diff --git a/resources/shaders/140/flat_texture.vs b/resources/shaders/140/flat_texture.vs index 57d8ca3b7c6..0943b9dee26 100644 --- a/resources/shaders/140/flat_texture.vs +++ b/resources/shaders/140/flat_texture.vs @@ -2,14 +2,17 @@ uniform mat4 view_model_matrix; uniform mat4 projection_matrix; +uniform mat3 u_uvTransformMatrix; in vec3 v_position; -in vec2 v_tex_coord; out vec2 tex_coord; void main() { - tex_coord = v_tex_coord; + vec2 texCoord; + texCoord.x = v_position.x; + texCoord.y = -v_position.y; + tex_coord = (u_uvTransformMatrix * vec3(texCoord, 1.0)).xy; gl_Position = projection_matrix * view_model_matrix * vec4(v_position, 1.0); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 087b33ab61b..f60df551fa9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7266,15 +7266,15 @@ void GLCanvas3D::_render_background() m_background.reset(); GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2 }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2 }; init_data.reserve_vertices(4); init_data.reserve_indices(6); // vertices - init_data.add_vertex(Vec2f(-1.0f, -1.0f), Vec2f(0.0f, 0.0f)); - init_data.add_vertex(Vec2f(1.0f, -1.0f), Vec2f(1.0f, 0.0f)); - init_data.add_vertex(Vec2f(1.0f, 1.0f), Vec2f(1.0f, 1.0f)); - init_data.add_vertex(Vec2f(-1.0f, 1.0f), Vec2f(0.0f, 1.0f)); + init_data.add_vertex(Vec2f(-1.0f, -1.0f)/*, Vec2f(0.0f, 0.0f)*/); + init_data.add_vertex(Vec2f(1.0f, -1.0f)/*, Vec2f(1.0f, 0.0f)*/); + init_data.add_vertex(Vec2f(1.0f, 1.0f)/*, Vec2f(1.0f, 1.0f)*/); + init_data.add_vertex(Vec2f(-1.0f, 1.0f)/*, Vec2f(0.0f, 1.0f)*/); // indices init_data.add_triangle(0, 1, 2); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index be0b4026538..47d0ec81b97 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -654,6 +654,32 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs) { + GLModel& model = InitModelForRenderImage(); + + // position and scale from normalized unit quad + const float center_x = (left + right) * 0.5f; + const float center_y = (bottom + top) * 0.5f; + const float scale_x = (right - left); + const float scale_y = (top - bottom); + + Transform3d model_matrix = Transform3d::Identity(); + model_matrix.data()[3 * 4 + 0] = center_x; + model_matrix.data()[3 * 4 + 1] = center_y; + model_matrix.data()[0 * 4 + 0] = scale_x; + model_matrix.data()[1 * 4 + 1] = scale_y; + + // UV transform — maps unit UVs to sprite atlas sub-region + const float center_u = (uvs.right_bottom.u + uvs.left_bottom.u) * 0.5f; + const float center_v = (uvs.right_top.v + uvs.right_bottom.v) * 0.5f; + const float scale_u = (uvs.right_bottom.u - uvs.left_bottom.u); + const float scale_v = (uvs.right_bottom.v - uvs.right_top.v); + + Matrix3f uv_matrix = Matrix3f::Identity(); + uv_matrix(0, 2) = center_u; + uv_matrix(1, 2) = center_v; + uv_matrix(0, 0) = scale_u; + uv_matrix(1, 1) = scale_v; + glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -662,29 +688,12 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id)); - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P2T2 }; - init_data.reserve_vertices(4); - init_data.reserve_indices(6); - - // vertices - init_data.add_vertex(Vec2f(left, bottom), Vec2f(uvs.left_bottom.u, uvs.left_bottom.v)); - init_data.add_vertex(Vec2f(right, bottom), Vec2f(uvs.right_bottom.u, uvs.right_bottom.v)); - init_data.add_vertex(Vec2f(right, top), Vec2f(uvs.right_top.u, uvs.right_top.v)); - init_data.add_vertex(Vec2f(left, top), Vec2f(uvs.left_top.u, uvs.left_top.v)); - - // indices - init_data.add_triangle(0, 1, 2); - init_data.add_triangle(2, 3, 0); - - GLModel model; - model.init_from(std::move(init_data)); - GLShaderProgram* shader = wxGetApp().get_shader("flat_texture"); if (shader != nullptr) { shader->start_using(); - shader->set_uniform("view_model_matrix", Transform3d::Identity()); + shader->set_uniform("view_model_matrix", model_matrix); shader->set_uniform("projection_matrix", Transform3d::Identity()); + shader->set_uniform("u_uvTransformMatrix", uv_matrix); model.render(); shader->stop_using(); } @@ -695,6 +704,34 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, glsafe(::glDisable(GL_BLEND)); } +GLModel& GLTexture::GetModelForRenderImage() +{ + static GLModel g_model_for_render_image; + return g_model_for_render_image; +} + +GLModel& GLTexture::InitModelForRenderImage() +{ + auto& model = GetModelForRenderImage(); + if (!model.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + + init_data.add_vertex(Vec3f(-0.5f, -0.5f, 0.0f)/*, Vec2f(-0.5f, 0.5f)*/); + init_data.add_vertex(Vec3f( 0.5f, -0.5f, 0.0f)/*, Vec2f( 0.5f, 0.5f)*/); + init_data.add_vertex(Vec3f( 0.5f, 0.5f, 0.0f)/*, Vec2f( 0.5f, -0.5f)*/); + init_data.add_vertex(Vec3f(-0.5f, 0.5f, 0.0f)/*, Vec2f(-0.5f, -0.5f)*/); + + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + + model.init_from(std::move(init_data)); + } + return model; +} + static bool to_squared_power_of_two(const std::string& filename, int max_size_px, int& w, int& h) { auto is_power_of_two = [](int v) { return v != 0 && (v & (v - 1)) == 0; }; diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index 41068a9fb72..26d376079b1 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -14,6 +14,8 @@ class wxImage; namespace Slic3r { namespace GUI { +class GLModel; + class GLTexture { class Compressor @@ -131,6 +133,8 @@ namespace GUI { private: bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy); bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px); + static GLModel& GetModelForRenderImage(); + static GLModel& InitModelForRenderImage(); friend class Compressor; }; From 5321b00bbba550a0c8ec3448230ac434e0707705 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Wed, 10 Jun 2026 14:53:26 +0800 Subject: [PATCH 3/4] add some Comments in function extract Frustum from vp-matrix --- src/slic3r/GUI/Camera.cpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 2508f6553ae..ed47e7dbd46 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -682,6 +682,8 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up void Camera::UpdateFrustum() { // fast extraction of frustum planes from the view-projection matrix + // 1. plane equation: ax + by + cz + d = 0, vec3(a,b,c) is normal of plane, d is scalar of plane, distance to origin of world frame + // 2. -w' < x' < w' -w' < y' < w' -w' < z' < w' const Matrix4d& vp = m_projection_matrix.matrix() * m_view_matrix.matrix(); const Eigen::Matrix4d& vp_matrix = vp.eval(); const double* vp_data = vp_matrix.data(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f60df551fa9..c1e9d03c95b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7271,10 +7271,10 @@ void GLCanvas3D::_render_background() init_data.reserve_indices(6); // vertices - init_data.add_vertex(Vec2f(-1.0f, -1.0f)/*, Vec2f(0.0f, 0.0f)*/); - init_data.add_vertex(Vec2f(1.0f, -1.0f)/*, Vec2f(1.0f, 0.0f)*/); - init_data.add_vertex(Vec2f(1.0f, 1.0f)/*, Vec2f(1.0f, 1.0f)*/); - init_data.add_vertex(Vec2f(-1.0f, 1.0f)/*, Vec2f(0.0f, 1.0f)*/); + init_data.add_vertex(Vec2f(-1.0f, -1.0f)); + init_data.add_vertex(Vec2f(1.0f, -1.0f)); + init_data.add_vertex(Vec2f(1.0f, 1.0f)); + init_data.add_vertex(Vec2f(-1.0f, 1.0f)); // indices init_data.add_triangle(0, 1, 2); From 9d8b320eb5855a5427cb94e59f117af34e0b7d65 Mon Sep 17 00:00:00 2001 From: kenshin627 <87zhaoxiaobo@163.com> Date: Wed, 10 Jun 2026 15:16:08 +0800 Subject: [PATCH 4/4] delete unnecessary tex_coord assignment --- resources/shaders/140/background.vs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/140/background.vs b/resources/shaders/140/background.vs index 2fe039854f7..f01eef669f5 100644 --- a/resources/shaders/140/background.vs +++ b/resources/shaders/140/background.vs @@ -6,6 +6,6 @@ out vec2 tex_coord; void main() { - tex_coord = tex_coord = v_position.xy * 0.5 + 0.5; + tex_coord = v_position.xy * 0.5 + 0.5; gl_Position = vec4(v_position, 1.0); }