diff --git a/resources/shaders/110/gouraud.fs b/resources/shaders/110/gouraud.fs index 6f354ff9a6..664ed4e144 100644 --- a/resources/shaders/110/gouraud.fs +++ b/resources/shaders/110/gouraud.fs @@ -36,6 +36,9 @@ uniform SlopeDetection slope; //BBS: add outline_color uniform bool is_outline; +uniform sampler2D depth_tex; +uniform vec2 screen_size; + #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -44,6 +47,9 @@ uniform bool is_outline; uniform PrintVolumeDetection print_volume; +uniform float z_far; +uniform float z_near; + varying vec3 clipping_planes_dots; varying float color_clip_plane_dot; @@ -54,6 +60,71 @@ varying vec4 world_pos; varying float world_normal_z; varying vec3 eye_normal; +vec3 getBackfaceColor(vec3 fill) { + float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b; + return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988); +} + +// Silhouette edge detection & rendering algorithem by leoneruggiero +// https://www.shadertoy.com/view/DslXz2 +#define INFLATE 1 + +float GetTolerance(float d, float k) +{ + // ------------------------------------------- + // Find a tolerance for depth that is constant + // in view space (k in view space). + // + // tol = k*ddx(ZtoDepth(z)) + // ------------------------------------------- + + float A=- (z_far+z_near)/(z_far-z_near); + float B=-2.0*z_far*z_near /(z_far-z_near); + + d = d*2.0-1.0; + + return -k*(d+A)*(d+A)/B; +} + +float DetectSilho(ivec2 fragCoord, ivec2 dir) +{ + // ------------------------------------------- + // x0 ___ x1----o + // :\ : + // r0 : \ : r1 + // : \ : + // o---x2 ___ x3 + // + // r0 and r1 are the differences between actual + // and expected (as if x0..3 where on the same + // plane) depth values. + // ------------------------------------------- + + float x0 = abs(texelFetch(depth_tex, (fragCoord + dir*-2), 0).r); + float x1 = abs(texelFetch(depth_tex, (fragCoord + dir*-1), 0).r); + float x2 = abs(texelFetch(depth_tex, (fragCoord + dir* 0), 0).r); + float x3 = abs(texelFetch(depth_tex, (fragCoord + dir* 1), 0).r); + + float d0 = (x1-x0); + float d1 = (x2-x3); + + float r0 = x1 + d0 - x2; + float r1 = x2 + d1 - x1; + + float tol = GetTolerance(x2, 0.04); + + return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0)); + +} + +float DetectSilho(ivec2 fragCoord) +{ + return max( + DetectSilho(fragCoord, ivec2(1,0)), // Horizontal + DetectSilho(fragCoord, ivec2(0,1)) // Vertical + ); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -94,10 +165,20 @@ void main() pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); } color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; - + //BBS: add outline_color - if (is_outline) - gl_FragColor = uniform_color; + if (is_outline) { + color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); + ivec2 fragCoord = ivec2(gl_FragCoord.xy); + float s = DetectSilho(fragCoord); + // Makes silhouettes thicker. + for(int i=1;i<=INFLATE; i++) + { + s = max(s, DetectSilho(fragCoord.xy + ivec2(i, 0))); + s = max(s, DetectSilho(fragCoord.xy + ivec2(0, i))); + } + gl_FragColor = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a); + } #ifdef ENABLE_ENVIRONMENT_MAP else if (use_environment_tex) gl_FragColor = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); diff --git a/resources/shaders/140/gouraud.fs b/resources/shaders/140/gouraud.fs index 84bce5c035..d3f8cbffb5 100644 --- a/resources/shaders/140/gouraud.fs +++ b/resources/shaders/140/gouraud.fs @@ -36,6 +36,8 @@ uniform SlopeDetection slope; //BBS: add outline_color uniform bool is_outline; +uniform sampler2D depth_tex; +uniform vec2 screen_size; #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -44,6 +46,9 @@ uniform bool is_outline; uniform PrintVolumeDetection print_volume; +uniform float z_far; +uniform float z_near; + in vec3 clipping_planes_dots; in float color_clip_plane_dot; @@ -54,6 +59,71 @@ in vec4 world_pos; in float world_normal_z; in vec3 eye_normal; +vec3 getBackfaceColor(vec3 fill) { + float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b; + return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988); +} + +// Silhouette edge detection & rendering algorithem by leoneruggiero +// https://www.shadertoy.com/view/DslXz2 +#define INFLATE 1 + +float GetTolerance(float d, float k) +{ + // ------------------------------------------- + // Find a tolerance for depth that is constant + // in view space (k in view space). + // + // tol = k*ddx(ZtoDepth(z)) + // ------------------------------------------- + + float A=- (z_far+z_near)/(z_far-z_near); + float B=-2.0*z_far*z_near /(z_far-z_near); + + d = d*2.0-1.0; + + return -k*(d+A)*(d+A)/B; +} + +float DetectSilho(ivec2 fragCoord, ivec2 dir) +{ + // ------------------------------------------- + // x0 ___ x1----o + // :\ : + // r0 : \ : r1 + // : \ : + // o---x2 ___ x3 + // + // r0 and r1 are the differences between actual + // and expected (as if x0..3 where on the same + // plane) depth values. + // ------------------------------------------- + + float x0 = abs(texelFetch(depth_tex, (fragCoord + dir*-2), 0).r); + float x1 = abs(texelFetch(depth_tex, (fragCoord + dir*-1), 0).r); + float x2 = abs(texelFetch(depth_tex, (fragCoord + dir* 0), 0).r); + float x3 = abs(texelFetch(depth_tex, (fragCoord + dir* 1), 0).r); + + float d0 = (x1-x0); + float d1 = (x2-x3); + + float r0 = x1 + d0 - x2; + float r1 = x2 + d1 - x1; + + float tol = GetTolerance(x2, 0.04); + + return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0)); + +} + +float DetectSilho(ivec2 fragCoord) +{ + return max( + DetectSilho(fragCoord, ivec2(1,0)), // Horizontal + DetectSilho(fragCoord, ivec2(0,1)) // Vertical + ); +} + out vec4 out_color; void main() @@ -96,10 +166,20 @@ void main() pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); } color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; - + //BBS: add outline_color - if (is_outline) - out_color = uniform_color; + if (is_outline) { + color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); + ivec2 fragCoord = ivec2(gl_FragCoord.xy); + float s = DetectSilho(fragCoord); + // Makes silhouettes thicker. + for(int i=1;i<=INFLATE; i++) + { + s = max(s, DetectSilho(fragCoord.xy + ivec2(i, 0))); + s = max(s, DetectSilho(fragCoord.xy + ivec2(0, i))); + } + out_color = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a); + } #ifdef ENABLE_ENVIRONMENT_MAP else if (use_environment_tex) out_color = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 86ab5d2239..c8cb2dd82d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -423,7 +423,7 @@ void GLVolume::render() } //BBS: add outline related logic -void GLVolume::render_with_outline(const Transform3d &view_model_matrix) +void GLVolume::render_with_outline(const GUI::Size& cnv_size) { if (!is_active) return; @@ -435,37 +435,80 @@ void GLVolume::render_with_outline(const Transform3d &view_model_matrix) ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects; std::vector colors = get_extruders_colors(); - glEnable(GL_STENCIL_TEST); - glStencilMask(0xFF); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0xff, 0xFF); + const GUI::OpenGLManager::EFramebufferType framebuffers_type = GUI::OpenGLManager::get_framebuffers_type(); + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Unknown) { + // No supported, degrade to normal rendering + simple_render(shader, model_objects, colors); + return; + } - simple_render(shader, model_objects, colors); + // 1st. render pass, render the model into a separate render target that has only depth buffer + GLuint depth_fbo = 0; + GLuint depth_tex = 0; + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glGenFramebuffers(1, &depth_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo)); - // 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing. - // Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing - // the objects' size differences, making it look like borders. - glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF); - glStencilMask(0x00); - float scale = 1.02f; - ColorRGBA body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red + glActiveTexture(GL_TEXTURE0); + glsafe(::glGenTextures(1, &depth_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr)); - model.set_color(body_color); - shader->set_uniform("is_outline", true); + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0)); + } else { + glsafe(::glGenFramebuffersEXT(1, &depth_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo)); - Transform3d matrix = view_model_matrix; - matrix.scale(scale); - shader->set_uniform("view_model_matrix", matrix); + glActiveTexture(GL_TEXTURE0); + glsafe(::glGenTextures(1, &depth_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr)); + + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0)); + } + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); if (tverts_range == std::make_pair(0, -1)) model.render(); else model.render(this->tverts_range); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - shader->set_uniform("view_model_matrix", view_model_matrix); + // 2nd. render pass, just a normal render with the depth buffer passed as a texture + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + } + shader->set_uniform("is_outline", true); + shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()}); + glActiveTexture(GL_TEXTURE0); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + shader->set_uniform("depth_tex", 0); + simple_render(shader, model_objects, colors); + + // Some clean up to do + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + shader->set_uniform("screen_size", 0); shader->set_uniform("is_outline", false); - - glDisable(GL_STENCIL_TEST); + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (depth_fbo != 0) + glsafe(::glDeleteFramebuffers(1, &depth_fbo)); + } else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + if (depth_fbo != 0) + glsafe(::glDeleteFramebuffersEXT(1, &depth_fbo)); + } + if (depth_tex != 0) + glsafe(::glDeleteTextures(1, &depth_tex)); } //BBS add render for simple case @@ -847,8 +890,8 @@ 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, - std::function filter_func, bool with_outline) const +void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size, + std::function filter_func) const { GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); if (to_render.empty()) @@ -953,9 +996,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); //BBS: add outline related logic - //if (with_outline && volume.first->selected) - // volume.first->render_with_outline(view_matrix * model_matrix); - //else + if (volume.first->selected) + volume.first->render_with_outline(cnv_size); + else volume.first->render(); #if ENABLE_ENVIRONMENT_MAP diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 4479c24632..cd89efa36a 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -39,6 +39,10 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C namespace Slic3r { +namespace GUI { + class Size; +} + class SLAPrintObject; enum SLAPrintObjectStep : unsigned int; class BuildVolume; @@ -322,7 +326,7 @@ public: virtual void render(); //BBS: add outline related logic and add virtual specifier - virtual void render_with_outline(const Transform3d &view_model_matrix); + virtual void render_with_outline(const GUI::Size& cnv_size); //BBS: add simple render function for thumbnail void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector& extruder_colors, bool ban_light =false); @@ -355,7 +359,7 @@ class GLWipeTowerVolume : public GLVolume { public: GLWipeTowerVolume(const std::vector& colors); void render() override; - void render_with_outline(const Transform3d &view_model_matrix) override { render(); } + void render_with_outline(const GUI::Size& cnv_size) override { render(); } std::vector model_per_colors; bool IsTransparent(); @@ -465,8 +469,8 @@ public: int get_selection_support_threshold_angle(bool&) const; // Render the volumes by OpenGL. //BBS: add outline drawing logic - void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, - std::function filter_func = std::function(), bool with_outline = true) const; + void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size, + std::function filter_func = std::function()) const; // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0d649b9a6a..88aa496129 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1244,7 +1244,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin) #endif // ENABLE_GCODE_VIEWER_STATISTICS glsafe(::glEnable(GL_DEPTH_TEST)); - render_shells(); + render_shells(canvas_width, canvas_height); if (m_roles.empty()) return; @@ -4023,7 +4023,7 @@ void GCodeViewer::render_toolpaths() } } -void GCodeViewer::render_shells() +void GCodeViewer::render_shells(int canvas_width, int canvas_height) { //BBS: add shell previewing logic if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty()) @@ -4039,7 +4039,9 @@ void GCodeViewer::render_shells() shader->start_using(); shader->set_uniform("emission_factor", 0.1f); const Camera& camera = wxGetApp().plater()->get_camera(); - m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix()); + 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}); shader->set_uniform("emission_factor", 0.0f); shader->stop_using(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0d730bb0f9..18073c6a96 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -893,7 +893,7 @@ private: //void load_shells(const Print& print); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void render_toolpaths(); - void render_shells(); + void render_shells(int canvas_width, int canvas_height); //BBS: GUI refactor: add canvas size void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6365b88a26..ae0b24f588 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7228,6 +7228,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with if (shader != nullptr) { shader->start_using(); + const Size& cvn_size = get_canvas_size(); + { + 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()); + } switch (type) { default: @@ -7239,7 +7245,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(), [object_id](const GLVolume& volume) { + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), 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); }); @@ -7255,14 +7261,14 @@ 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(), [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier && !volume.is_wipe_tower; } else { return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); } - }, with_outline); + }); } } else { @@ -7289,14 +7295,14 @@ 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(), [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier; } else { return true; } - }, with_outline); + }); if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) { const GLGizmosManager& gm = get_gizmos_manager(); shader->stop_using();