Browse Source

Follow-up of 6194e67e689d85d4e6d0666dd4c2b993fdeeb90d - Separated the part that computed triangles normals and lighting inside the fragment shader into a separate shader mm_gouraud, which is only used for the multi-material painting gizmo.

Lukáš Hejl 3 years ago
parent
commit
b45675b4e1

+ 3 - 34
resources/shaders/gouraud.fs

@@ -50,8 +50,6 @@ varying float world_pos_z;
 varying float world_normal_z;
 varying vec3 eye_normal;
 
-uniform bool compute_triangle_normals_in_fs;
-
 void main()
 {
     if (any(lessThan(clipping_planes_dots, ZERO)))
@@ -59,36 +57,7 @@ void main()
     vec3  color = uniform_color.rgb;
     float alpha = uniform_color.a;
 
-    vec2  intensity_fs      = intensity;
-    vec3  eye_normal_fs     = eye_normal;
-    float world_normal_z_fs = world_normal_z;
-    if (compute_triangle_normals_in_fs) {
-        vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
-#ifdef FLIP_TRIANGLE_NORMALS
-        triangle_normal = -triangle_normal;
-#endif
-
-        // First transform the normal into camera space and normalize the result.
-        eye_normal_fs = normalize(gl_NormalMatrix * triangle_normal);
-
-        // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
-        // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
-        float NdotL = max(dot(eye_normal_fs, LIGHT_TOP_DIR), 0.0);
-
-        intensity_fs = vec2(0.0, 0.0);
-        intensity_fs.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
-        vec3 position = (gl_ModelViewMatrix * model_pos).xyz;
-        intensity_fs.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal_fs)), 0.0), LIGHT_TOP_SHININESS);
-
-        // Perform the same lighting calculation for the 2nd light source (no specular applied).
-        NdotL = max(dot(eye_normal_fs, LIGHT_FRONT_DIR), 0.0);
-        intensity_fs.x += NdotL * LIGHT_FRONT_DIFFUSE;
-
-        // z component of normal vector in world coordinate used for slope shading
-        world_normal_z_fs = slope.actived ? (normalize(slope.volume_world_normal_matrix * triangle_normal)).z : 0.0;
-    }
-
-    if (slope.actived && world_normal_z_fs < slope.normal_z - EPSILON) {
+    if (slope.actived && world_normal_z < slope.normal_z - EPSILON) {
         color = vec3(0.7, 0.7, 1.0);
         alpha = 1.0;
     }
@@ -96,8 +65,8 @@ void main()
 	color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
 #ifdef ENABLE_ENVIRONMENT_MAP
     if (use_environment_tex)
-        gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal_fs).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity_fs.x, alpha);
+        gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
     else
 #endif
-        gl_FragColor = vec4(vec3(intensity_fs.y) + color * intensity_fs.x, alpha);
+        gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
 }

+ 12 - 17
resources/shaders/gouraud.vs

@@ -54,26 +54,22 @@ varying float world_pos_z;
 varying float world_normal_z;
 varying vec3 eye_normal;
 
-uniform bool compute_triangle_normals_in_fs;
-
 void main()
 {
-    if (!compute_triangle_normals_in_fs) {
-        // First transform the normal into camera space and normalize the result.
-        eye_normal = normalize(gl_NormalMatrix * gl_Normal);
+    // First transform the normal into camera space and normalize the result.
+    eye_normal = normalize(gl_NormalMatrix * gl_Normal);
 
-        // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
-        // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
-        float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
+    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
 
-        intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
-        vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
-        intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
+    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+    vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
+    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
 
-        // Perform the same lighting calculation for the 2nd light source (no specular applied).
-        NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
-        intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-    }
+    // Perform the same lighting calculation for the 2nd light source (no specular applied).
+    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
+    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
 
     model_pos = gl_Vertex;
     // Point in homogenous coordinates.
@@ -90,8 +86,7 @@ void main()
     }
 
     // z component of normal vector in world coordinate used for slope shading
-    if (!compute_triangle_normals_in_fs)
-        world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
+    world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
 
     gl_Position = ftransform();
     // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.

+ 55 - 0
resources/shaders/mm_gouraud.fs

@@ -0,0 +1,55 @@
+#version 110
+
+#define INTENSITY_CORRECTION 0.6
+
+// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
+const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
+#define LIGHT_TOP_DIFFUSE    (0.8 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SPECULAR   (0.125 * INTENSITY_CORRECTION)
+#define LIGHT_TOP_SHININESS  20.0
+
+// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
+const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
+#define LIGHT_FRONT_DIFFUSE  (0.3 * INTENSITY_CORRECTION)
+
+#define INTENSITY_AMBIENT    0.3
+
+const vec3  ZERO    = vec3(0.0, 0.0, 0.0);
+const float EPSILON = 0.0001;
+
+uniform vec4 uniform_color;
+
+varying vec3 clipping_planes_dots;
+varying vec4 model_pos;
+
+void main()
+{
+    if (any(lessThan(clipping_planes_dots, ZERO)))
+        discard;
+    vec3  color = uniform_color.rgb;
+    float alpha = uniform_color.a;
+
+    vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
+#ifdef FLIP_TRIANGLE_NORMALS
+    triangle_normal = -triangle_normal;
+#endif
+
+    // First transform the normal into camera space and normalize the result.
+    vec3 eye_normal = normalize(gl_NormalMatrix * triangle_normal);
+
+    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
+    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
+    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
+
+    // x = diffuse, y = specular;
+    vec2 intensity = vec2(0.0, 0.0);
+    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
+    vec3 position = (gl_ModelViewMatrix * model_pos).xyz;
+    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
+
+    // Perform the same lighting calculation for the 2nd light source (no specular applied).
+    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
+    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
+
+    gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
+}

+ 23 - 0
resources/shaders/mm_gouraud.vs

@@ -0,0 +1,23 @@
+#version 110
+
+const vec3 ZERO = vec3(0.0, 0.0, 0.0);
+
+uniform mat4 volume_world_matrix;
+// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
+uniform vec2 z_range;
+// Clipping plane - general orientation. Used by the SLA gizmo.
+uniform vec4 clipping_plane;
+
+varying vec3 clipping_planes_dots;
+varying vec4 model_pos;
+
+void main()
+{
+    model_pos = gl_Vertex;
+    // Point in homogenous coordinates.
+    vec4 world_pos = volume_world_matrix * gl_Vertex;
+
+    gl_Position = ftransform();
+    // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
+    clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
+}

+ 4 - 1
src/slic3r/GUI/GLCanvas3D.hpp

@@ -456,7 +456,7 @@ private:
     GLGizmosManager m_gizmos;
     GLToolbar m_main_toolbar;
     GLToolbar m_undoredo_toolbar;
-    ClippingPlane m_clipping_planes[2];
+    std::array<ClippingPlane, 2> m_clipping_planes;
     ClippingPlane m_camera_clipping_plane;
     bool m_use_clipping_planes;
     SlaCap m_sla_caps[2];
@@ -651,6 +651,9 @@ public:
     void reset_clipping_planes_cache() { m_sla_caps[0].triangles.clear(); m_sla_caps[1].triangles.clear(); }
     void set_use_clipping_planes(bool use) { m_use_clipping_planes = use; }
 
+    bool                                get_use_clipping_planes() const { return m_use_clipping_planes; }
+    const std::array<ClippingPlane, 2> &get_clipping_planes() const { return m_clipping_planes; };
+
     void set_color_by(const std::string& value);
 
     void refresh_camera_scene_box();

+ 10 - 12
src/slic3r/GUI/GLShadersManager.cpp

@@ -61,25 +61,23 @@ std::pair<bool, std::string> GLShadersManager::init()
     // used to render extrusion and travel paths as lines in gcode preview
     valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
     // used to render objects in 3d editor
-    // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
-    // Because of this, objects had darker colors inside the multi-material gizmo.
-    // Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
-    if (platform_flavor() == PlatformFlavor::OSXOnArm)
-        valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv
-#if ENABLE_ENVIRONMENT_MAP
-            , "ENABLE_ENVIRONMENT_MAP"sv
-#endif
-        });
-    else
-        valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
+    valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }
 #if ENABLE_ENVIRONMENT_MAP
-            , { "ENABLE_ENVIRONMENT_MAP"sv }
+        , { "ENABLE_ENVIRONMENT_MAP"sv }
 #endif
         );
     // used to render variable layers heights in 3d editor
     valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
     // used to render highlight contour around selected triangles inside the multi-material gizmo
     valid &= append_shader("mm_contour", { "mm_contour.vs", "mm_contour.fs" });
+    // Used to render painted triangles inside the multi-material gizmo. Triangle normals are computed inside fragment shader.
+    // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction.
+    // Because of this, objects had darker colors inside the multi-material gizmo.
+    // Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU.
+    if (platform_flavor() == PlatformFlavor::OSXOnArm)
+        valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"}, {"FLIP_TRIANGLE_NORMALS"sv});
+    else
+        valid &= append_shader("mm_gouraud", {"mm_gouraud.vs", "mm_gouraud.fs"});
 
     return { valid, error };
 }

+ 39 - 4
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp

@@ -174,6 +174,43 @@ void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection)
     }
 }
 
+void GLGizmoMmuSegmentation::render_triangles(const Selection &selection, const bool use_polygon_offset_fill) const
+{
+    ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data();
+    auto                    *shader   = wxGetApp().get_shader("mm_gouraud");
+    if (!shader)
+        return;
+    shader->start_using();
+    shader->set_uniform("clipping_plane", clp_data.clp_dataf);
+    shader->set_uniform("z_range", clp_data.z_range);
+    ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
+
+    const ModelObject *mo      = m_c->selection_info()->model_object();
+    int                mesh_id = -1;
+    for (const ModelVolume *mv : mo->volumes) {
+        if (!mv->is_model_part())
+            continue;
+
+        ++mesh_id;
+
+        const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
+
+        bool is_left_handed = trafo_matrix.matrix().determinant() < 0.;
+        if (is_left_handed)
+            glsafe(::glFrontFace(GL_CW));
+
+        glsafe(::glPushMatrix());
+        glsafe(::glMultMatrixd(trafo_matrix.data()));
+
+        shader->set_uniform("volume_world_matrix", trafo_matrix);
+        m_triangle_selectors[mesh_id]->render(m_imgui);
+
+        glsafe(::glPopMatrix());
+        if (is_left_handed)
+            glsafe(::glFrontFace(GL_CCW));
+    }
+}
+
 static void render_extruders_combo(const std::string                       &label,
                                    const std::vector<std::string>          &extruders,
                                    const std::vector<std::array<float, 4>> &extruders_colors,
@@ -554,9 +591,7 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
     auto *shader = wxGetApp().get_current_shader();
     if (!shader)
         return;
-    assert(shader->get_name() == "gouraud");
-    ScopeGuard guard([shader]() { if (shader) shader->set_uniform("compute_triangle_normals_in_fs", false);});
-    shader->set_uniform("compute_triangle_normals_in_fs", true);
+    assert(shader->get_name() == "mm_gouraud");
 
     for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
         if (m_gizmo_scene.has_VBOs(color_idx)) {
@@ -569,7 +604,7 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
         }
 
     if (m_paint_contour.has_VBO()) {
-        ScopeGuard guard_gouraud([shader]() { shader->start_using(); });
+        ScopeGuard guard_mm_gouraud([shader]() { shader->start_using(); });
         shader->stop_using();
 
         auto *contour_shader = wxGetApp().get_shader("mm_contour");

+ 2 - 0
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp

@@ -89,6 +89,8 @@ public:
 
     void set_painter_gizmo_data(const Selection& selection) override;
 
+    void render_triangles(const Selection& selection, bool use_polygon_offset_fill) const override;
+
     // TriangleSelector::serialization/deserialization has a limit to store 19 different states.
     // EXTRUDER_LIMIT + 1 states are used to storing the painting because also uncolored triangles are stored.
     // When increasing EXTRUDER_LIMIT, it needs to ensure that TriangleSelector::serialization/deserialization

+ 22 - 15
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp

@@ -43,12 +43,29 @@ void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection)
     }
 }
 
+GLGizmoPainterBase::ClippingPlaneDataWrapper GLGizmoPainterBase::get_clipping_plane_data() const
+{
+    ClippingPlaneDataWrapper clp_data_out{{0.f, 0.f, 1.f, FLT_MAX}, {-FLT_MAX, FLT_MAX}};
+    // Take care of the clipping plane. The normal of the clipping plane is
+    // saved with opposite sign than we need to pass to OpenGL (FIXME)
+    if (bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.; clipping_plane_active) {
+        const ClippingPlane *clp = m_c->object_clipper()->get_clipping_plane();
+        for (size_t i = 0; i < 3; ++i)
+            clp_data_out.clp_dataf[i] = -1.f * float(clp->get_data()[i]);
+        clp_data_out.clp_dataf[3] = float(clp->get_data()[3]);
+    }
 
+    // z_range is calculated in the same way as in GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
+    if (m_c->get_canvas()->get_use_clipping_planes()) {
+        const std::array<ClippingPlane, 2> &clps = m_c->get_canvas()->get_clipping_planes();
+        clp_data_out.z_range                     = {float(-clps[0].get_data()[3]), float(clps[1].get_data()[3])};
+    }
+
+    return clp_data_out;
+}
 
 void GLGizmoPainterBase::render_triangles(const Selection& selection, const bool use_polygon_offset_fill) const
 {
-    const ModelObject* mo = m_c->selection_info()->model_object();
-
     ScopeGuard offset_fill_guard([&use_polygon_offset_fill]() {
         if (use_polygon_offset_fill)
             glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
@@ -58,27 +75,17 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection, const bool
         glsafe(::glPolygonOffset(-5.0, -5.0));
     }
 
-    // Take care of the clipping plane. The normal of the clipping plane is
-    // saved with opposite sign than we need to pass to OpenGL (FIXME)
-    bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.;
-    float clp_dataf[4] = {0.f, 0.f, 1.f, FLT_MAX};
-    if (clipping_plane_active) {
-        const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane();
-        for (size_t i=0; i<3; ++i)
-            clp_dataf[i] = -1.f * float(clp->get_data()[i]);
-        clp_dataf[3] = float(clp->get_data()[3]);
-    }
-
     auto *shader = wxGetApp().get_shader("gouraud");
     if (! shader)
         return;
     shader->start_using();
     shader->set_uniform("slope.actived", false);
     shader->set_uniform("print_box.actived", false);
-    shader->set_uniform("clipping_plane", clp_dataf, 4);
+    shader->set_uniform("clipping_plane", this->get_clipping_plane_data().clp_dataf);
     ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
 
-    int mesh_id = -1;
+    const ModelObject *mo      = m_c->selection_info()->model_object();
+    int                mesh_id = -1;
     for (const ModelVolume* mv : mo->volumes) {
         if (! mv->is_model_part())
             continue;

+ 9 - 1
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp

@@ -126,7 +126,7 @@ public:
     virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
 
 protected:
-    void render_triangles(const Selection& selection, const bool use_polygon_offset_fill = true) const;
+    virtual void render_triangles(const Selection& selection, bool use_polygon_offset_fill = true) const;
     void render_cursor() const;
     void render_cursor_circle() const;
     void render_cursor_sphere(const Transform3d& trafo) const;
@@ -176,6 +176,14 @@ protected:
         Right
     };
 
+    struct ClippingPlaneDataWrapper
+    {
+        std::array<float, 4> clp_dataf;
+        std::array<float, 2> z_range;
+    };
+
+    ClippingPlaneDataWrapper get_clipping_plane_data() const;
+
 private:
     bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
     void update_raycast_cache(const Vec2d& mouse_position,