Repository: napari/napari

Issue:
Track vertex shader does not handle blending/opacity correctly
## 🐛 Bug

Two somewhat related bugs in `_vispy_tracks_shader.py` in the `Tracks` layer:
1. The opacity slider only changes the vispy node opacity, which has no effect on the vertex shader used for rendering the tracks or graph.
2. The translucent mode renders parts of the track, which should not be rendered, as dark lines:

![bug](https://user-images.githubusercontent.com/8217795/96697072-64ef9b00-1383-11eb-9648-54c92319ae84.gif)

## To Reproduce

Steps to reproduce the behavior:

1. run `examples\tracks_3d.py`
2. Change the blending mode to translucent in the `Tracks` layer
3. Change the opacity slider 

## Expected behavior

`opacity` should change the track opacity, and the `translucent` blending should not render parts of the track which should not be visible.

## Environment

napari: 0.3.9.dev32+g911e095.d20201007
Platform: Linux-4.15.0-1100-oem-x86_64-with-debian-buster-sid
Python: 3.7.7 (default, May 7 2020, 21:25:33) [GCC 7.3.0]
Qt: 5.14.2
PySide2: 5.14.2.2
NumPy: 1.19.1
SciPy: 1.4.1
Dask: 2.17.2
VisPy: 0.6.4

GL version: 4.6.0 NVIDIA 450.80.02
MAX_TEXTURE_SIZE: 32768

Plugins:
- napari-plugin-engine: 0.1.5
- svg: 0.1.3

## Additional context
This relates to the fact that the shader manipulates the alpha values of the vertices directly, late in the rendering pipeline.


Discussion:

Resolving patch:
diff --git a/napari/_vispy/_vispy_tracks_shader.py b/napari/_vispy/_vispy_tracks_shader.py
--- a/napari/_vispy/_vispy_tracks_shader.py
+++ b/napari/_vispy/_vispy_tracks_shader.py
@@ -6,7 +6,7 @@
 
 
 class TrackShader(Filter):
-    """ TrackShader
+    """TrackShader.
 
     Custom vertex and fragment shaders for visualizing tracks quickly with
     vispy. The central assumption is that the tracks are rendered as a
@@ -30,17 +30,11 @@ class TrackShader(Filter):
     vertex_time : 1D array, list
         a vector describing the time associated with each vertex
 
-
     TODO
     ----
     - the track is still displayed, albeit with fading, once the track has
      finished but is still within the 'tail_length' window. Should it
      disappear?
-    - check the shader positioning within the GL pipeline, currently
-     overrides layer opacity settings
-
-    vertex_mask: 1D array, list
-        a vector describing whether to mask each vertex
 
     """
 
@@ -78,15 +72,21 @@ class TrackShader(Filter):
     FRAG_SHADER = """
         varying vec4 v_track_color;
         void apply_track_shading() {
+
+            // if the alpha is below the threshold, discard the fragment
+            if( v_track_color.a <= 0.0 ) {
+                discard;
+            }
+
             // interpolate
-            gl_FragColor.a = clamp(v_track_color.a, 0.0, 1.0);
+            gl_FragColor.a = clamp(v_track_color.a * gl_FragColor.a, 0.0, 1.0);
         }
     """
 
     def __init__(
         self,
-        current_time=0,
-        tail_length=30,
+        current_time: Union[int, float] = 0,
+        tail_length: Union[int, float] = 30,
         use_fade: bool = True,
         vertex_time: Union[List, np.ndarray] = None,
     ):
diff --git a/napari/_vispy/vispy_tracks_layer.py b/napari/_vispy/vispy_tracks_layer.py
--- a/napari/_vispy/vispy_tracks_layer.py
+++ b/napari/_vispy/vispy_tracks_layer.py
@@ -51,7 +51,7 @@ def __init__(self, layer):
         self._on_appearance_change()
 
     def _on_data_change(self, event=None):
-        """ update the display """
+        """Update the display."""
 
         # update the shaders
         self.track_shader.current_time = self.layer.current_time
@@ -68,7 +68,7 @@ def _on_data_change(self, event=None):
         self._on_matrix_change()
 
     def _on_appearance_change(self, event=None):
-        """ change the appearance of the data """
+        """Change the appearance of the data."""
 
         # update shader properties related to appearance
         self.track_shader.use_fade = self.layer.use_fade
@@ -88,7 +88,7 @@ def _on_appearance_change(self, event=None):
         self.node._subvisuals[2].set_data(width=self.layer.tail_width,)
 
     def _on_tracks_change(self, event=None):
-        """ update the shader when the track data changes """
+        """Update the shader when the track data changes."""
 
         self.track_shader.use_fade = self.layer.use_fade
         self.track_shader.tail_length = self.layer.tail_length
@@ -106,7 +106,7 @@ def _on_tracks_change(self, event=None):
         self._on_matrix_change()
 
     def _on_graph_change(self, event=None):
-        """ update the shader when the graph data changes """
+        """Update the shader when the graph data changes."""
 
         self.graph_shader.use_fade = self.layer.use_fade
         self.graph_shader.tail_length = self.layer.tail_length


Test patch: