[Bf-blender-cvs] [6bcd877ebb9] greasepencil-refactor: GPencil: Refactor: Add Top to bottom layer masking using stencil

Clément Foucault noreply at git.blender.org
Sun Dec 15 03:43:22 CET 2019


Commit: 6bcd877ebb9a99482bd7f15eae08a3b383b2a706
Author: Clément Foucault
Date:   Sun Dec 15 03:25:25 2019 +0100
Branches: greasepencil-refactor
https://developer.blender.org/rB6bcd877ebb9a99482bd7f15eae08a3b383b2a706

GPencil: Refactor: Add Top to bottom layer masking using stencil

This uses the 8bits as binary mask layer. This is really
efficient but has the downside of not allowing gradient transparency.

This also allows for different masking ranges and even (maybe) masking
boolean operations (nested masks etc..) but implementing this sort of
this might complexify the UI and the implementation.

The reason we went for binary visibility is that our blending modes
cannot operate correctly if not using a bottom-up rendering of the
layers (due to low precision color buffers). So blending all masked
layers to another temp buffer or blending layer top to bottom is not
doable without loosing the blending features. The workaround for this
would be to use RGBA16F everywhere and use many double buffers. Which
would kill performance and is really unpractical.

Now we just limit the number of masks to be 8. Lifting this limit
is a todo.

===================================================================

M	source/blender/draw/engines/gpencil/gpencil_cache_utils.c
M	source/blender/draw/engines/gpencil/gpencil_engine.c
M	source/blender/draw/engines/gpencil/gpencil_engine.h

===================================================================

diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index 00a9b1c05e9..0b351f00c38 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -63,32 +63,33 @@ GPENCIL_tLayer *gpencil_layer_cache_add_new(GPENCIL_PrivateData *pd, Object *ob,
 {
   bGPdata *gpd = (bGPdata *)ob->data;
   GPENCIL_tLayer *tgp_layer = BLI_memblock_alloc(pd->gp_layer_pool);
+  tgp_layer->stencil_clear_value = -1;
+
+  bool is_mask = (gpl->flag & GP_LAYER_USE_MASK) != 0;
 
   {
-    DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL;
-    if (GPENCIL_3D_DRAWMODE(ob, gpd) || pd->draw_depth_only) {
+    DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_WRITE_DEPTH |
+                     DRW_STATE_STENCIL_EQUAL;
+
+    if (is_mask) {
+      /* We only write the stencil. */
+      state = DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL;
+    }
+    else if (GPENCIL_3D_DRAWMODE(ob, gpd) || pd->draw_depth_only) {
       /* TODO better 3D mode. */
-      state |= DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
+      state |= DRW_STATE_DEPTH_LESS_EQUAL;
     }
     else {
       /* We render all strokes with uniform depth (increasing with stroke id). */
-      state |= DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_GREATER;
-    }
-
-    if (!pd->draw_depth_only) {
-      if (gpl->flag & GP_LAYER_USE_MASK) {
-        state |= DRW_STATE_STENCIL_EQUAL;
-      }
-      else {
-        state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS;
-      }
+      state |= DRW_STATE_DEPTH_GREATER;
     }
 
     tgp_layer->geom_ps = DRW_pass_create("GPencil Layer", state);
   }
 
-  if ((gpl->blend_mode != eGplBlendMode_Regular) || (gpl->opacity < 1.0f)) {
-    DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL;
+  if (!is_mask && ((gpl->blend_mode != eGplBlendMode_Regular) || (gpl->opacity < 1.0f))) {
+    /* TODO add stencil opti */
+    DRWState state = DRW_STATE_WRITE_COLOR /* | DRW_STATE_STENCIL_NEQUAL*/;
     switch (gpl->blend_mode) {
       case eGplBlendMode_Regular:
         state |= DRW_STATE_BLEND_ALPHA_PREMUL;
@@ -109,6 +110,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add_new(GPENCIL_PrivateData *pd, Object *ob,
       case eGplBlendMode_Divide:
         /* Same Caveat as Subtract. This is conserved until there is a blend with a LDR buffer. */
       case eGplBlendMode_Overlay:
+        /* Same Caveat as Subtract. This is conserved until there is a blend with a LDR buffer. */
         state |= DRW_STATE_BLEND_MUL;
         break;
     }
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 767f1a46923..630d7a58c49 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -780,6 +780,12 @@ typedef struct gpIterPopulateData {
   GPENCIL_PrivateData *pd;
   GPENCIL_MaterialPool *matpool;
   DRWShadingGroup *grp;
+  /* Last mask layer encountered. This allow to render all masks before the masked layers. */
+  GPENCIL_tLayer *mask_layer_last;
+  /* First mask layer encountered. This allow to edit the clear stencil value. */
+  GPENCIL_tLayer *mask_layer_first;
+  /* Stencil id used for masks test. */
+  uint mask_id;
   /* Offset in the material pool to the first material of this object. */
   int mat_ofs;
   /* Last material UBO bound. Used to avoid uneeded buffer binding. */
@@ -797,15 +803,34 @@ static void gp_layer_cache_populate(bGPDlayer *gpl,
   gpIterPopulateData *iter = (gpIterPopulateData *)thunk;
   bGPdata *gpd = (bGPdata *)iter->ob->data;
 
+  const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D) || iter->pd->draw_depth_only;
+  const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
+  const bool is_mask = (gpl->flag & GP_LAYER_USE_MASK) != 0;
+  const bool is_mask_invert = false; /* TODO */
+
   GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add_new(iter->pd, iter->ob, gpl);
-  BLI_LINKS_APPEND(&iter->tgp_ob->layers, tgp_layer);
+
+  if (is_mask) {
+    /* We render all masks before other layers. */
+    if (iter->mask_layer_last) {
+      BLI_LINKS_INSERT_AFTER(&iter->tgp_ob->layers, iter->mask_layer_last, tgp_layer);
+    }
+    else {
+      BLI_LINKS_PREPEND(iter->tgp_ob->layers.first, tgp_layer);
+      iter->mask_layer_first = tgp_layer;
+      /* We follow alpha mask convention. 0 is masked, 1 is not masked.
+       * Unmask everything at first. Note that we only clear for the first layer. */
+      iter->mask_layer_first->stencil_clear_value = 0xFF;
+    }
+    iter->mask_layer_last = tgp_layer;
+  }
+  else {
+    BLI_LINKS_APPEND(&iter->tgp_ob->layers, tgp_layer);
+  }
 
   GPUUniformBuffer *ubo_mat;
   gpencil_material_resources_get(iter->matpool, 0, NULL, NULL, &ubo_mat);
 
-  const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D) || iter->pd->draw_depth_only;
-  const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
-
   float object_scale = mat4_to_scale(iter->ob->obmat);
   /* Negate thickness sign to tag that strokes are in screen space.
    * Convert to world units (by default, 1 meter = 2000 px). */
@@ -829,7 +854,18 @@ static void gp_layer_cache_populate(bGPDlayer *gpl,
   DRW_shgroup_uniform_float_copy(iter->grp, "thicknessWorldScale", thickness_scale);
   DRW_shgroup_uniform_float_copy(iter->grp, "vertexColorOpacity", gpl->vertex_paint_opacity);
   DRW_shgroup_uniform_vec4_copy(iter->grp, "layerTint", gpl->tintcolor);
-  DRW_shgroup_stencil_mask(iter->grp, 0xFF);
+
+  /* We only test against/modify a single bit. */
+  /* TODO allow more than 8 masks. */
+  uint mask = (1u << iter->mask_id) & 0xFF;
+  if (is_mask) {
+    DRW_shgroup_stencil_set(iter->grp, mask, is_mask_invert ? 0x00 : 0xFF, mask);
+    SET_FLAG_FROM_TEST(iter->mask_layer_first->stencil_clear_value, is_mask_invert, mask);
+    iter->mask_id++;
+  }
+  else {
+    DRW_shgroup_stencil_set(iter->grp, 0x00, 0xFF, mask);
+  }
 }
 
 static void gp_stroke_cache_populate(bGPDlayer *UNUSED(gpl),
@@ -1326,7 +1362,7 @@ static void GPENCIL_draw_scene_new(void *ved)
     DRW_stats_group_start("GPencil Object");
 
     GPU_framebuffer_bind(fbl->gpencil_fb);
-    GPU_framebuffer_clear_depth_stencil(fbl->gpencil_fb, ob->is_drawmode3d ? 1.0f : 0.0f, 0x00);
+    GPU_framebuffer_clear_depth_stencil(fbl->gpencil_fb, ob->is_drawmode3d ? 1.0f : 0.0f, 0xFF);
 
     if (ob->vfx.first) {
       /* TODO vfx */
@@ -1334,6 +1370,11 @@ static void GPENCIL_draw_scene_new(void *ved)
     }
 
     for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) {
+      if (layer->stencil_clear_value != -1) {
+        GPU_framebuffer_bind(fbl->gpencil_fb);
+        GPU_framebuffer_clear_stencil(fbl->gpencil_fb, layer->stencil_clear_value);
+      }
+
       if (layer->blend_ps) {
         GPU_framebuffer_bind(fbl->layer_fb);
         GPU_framebuffer_multi_clear(fbl->layer_fb, clear_cols);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 7f7d09274eb..e3c23a4df87 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -180,6 +180,8 @@ typedef struct GPENCIL_tLayer {
   DRWPass *geom_ps;
   /** Blend pass to composite onto the target buffer (blends modes). NULL if not needed. */
   DRWPass *blend_ps;
+  /** Value to clear the stencil with before drawing. -1 is bypass. (used for masking) */
+  int stencil_clear_value;
 } GPENCIL_tLayer;
 
 typedef struct GPENCIL_tObject {



More information about the Bf-blender-cvs mailing list