diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9c39d53b69..ded748e685 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7634,10 +7634,11 @@ void GLCanvas3D::_picking_pass_imex_ghosts() } } -void GLCanvas3D::_render_imex_ghosts() +void GLCanvas3D::_render_imex_ghosts(bool xray_pass) { - // Called from inside _render_objects' Transparent branch while the gouraud - // shader is bound and globals (z_far/z_near/z_range/clipping_plane) are set. + // Draws through whichever shader the caller bound: the shaded pass leaves gouraud current, + // the X-Ray pass its own program. Both are fed per volume below; the uniforms a pass sets + // once for itself are the caller's (see _render_imex_ghosts_xray for the X-Ray pass's). // GLVolume::render() only binds its mesh, so we must set the per-volume // matrices AND uniform_color that GLVolumeCollection::render would normally // set; otherwise ghosts pick up whatever the last main volume left behind. @@ -7695,7 +7696,14 @@ void GLCanvas3D::_render_imex_ghosts() // GLModel::render() pushes its own data.color into uniform_color, // so we must stamp the ghost's color onto the model before render // or it draws black. Pattern matches 3DScene.cpp:1099. - g->model.set_color(g->render_color); + // X-Ray derives its own coverage from view angle and multiplies the colour's alpha + // into it, so a ghost carrying its translucency as well composites about three times + // fainter than the body it mirrors. Hand that pass an opaque colour and let its + // density be the only source of translucency. + ColorRGBA ghost_color = g->render_color; + if (xray_pass) + ghost_color.a(1.0f); + g->model.set_color(ghost_color); g->render(); } } @@ -8753,6 +8761,8 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with if (_is_xray_view_active()) { if (type == GLVolumeCollection::ERenderType::Opaque) _render_xray_volumes(); + else + _render_imex_ghosts_xray(); m_camera_clipping_plane = ClippingPlane::ClipsNothing(); return; } @@ -8941,6 +8951,36 @@ void GLCanvas3D::_render_xray_volumes() glsafe(::glDepthMask(GL_TRUE)); } +// The IDEX/IQEX ghosts are not in m_volumes, so the X-Ray pass that replaces both shaded passes +// does not reach them on its own. They go through the X-Ray shader here rather than their own, +// which is what makes a ghost read as one more see-through body. +void GLCanvas3D::_render_imex_ghosts_xray() +{ + GLShaderProgram* shader = wxGetApp().get_shader("xray"); + if (shader == nullptr) + return; + + // The blend and depth state _render_xray_volumes() uses, plus its two-sided drawing: the + // shader shades back faces too, so a hollow ghost shows its far wall like a real body does. + glsafe(::glDepthMask(GL_FALSE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + glsafe(::glDisable(GL_CULL_FACE)); + + shader->start_using(); + // _render_imex_ghosts() sets the per-volume matrices and the color, but the two uniforms + // the volume collection would have set for the whole pass are this pass's to supply, and + // the vertex shader discards everything outside z_range - an unset one hides every ghost. + shader->set_uniform("z_range", m_volumes.get_z_range()); + shader->set_uniform("clipping_plane", m_volumes.get_clipping_plane()); + _render_imex_ghosts(/*xray_pass=*/true); + shader->stop_using(); + + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDepthMask(GL_TRUE)); +} + void GLCanvas3D::_render_wireframe_overlay() { if (!wxGetApp().plater()->is_show_wireframe()) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fe82edfd1f..f0b540cc6a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1373,8 +1373,10 @@ private: void _render_cad_grid(const Transform3d& view_matrix, const Transform3d& projection_matrix); //BBS: add outline drawing logic void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); - // IMEX ghost volumes owned by PartPlate; rendered in the transparent pass. - void _render_imex_ghosts(); + // IMEX ghost volumes owned by PartPlate, drawn through the shader the caller bound: + // the shaded pass's transparent half, or the X-Ray pass below. + void _render_imex_ghosts(bool xray_pass = false); + void _render_imex_ghosts_xray(); // IMEX ghost hover tooltip: filament swatch + label drawn as an ImGui overlay. void _render_imex_ghost_tooltip(); void _render_wireframe_overlay();