From 10f2e39f4803ca4f1110f3866e7aae1ac29acc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 7 Feb 2020 23:43:36 +0100 Subject: GPencil: Refactor: New linked mask system The new system is more flexible and a bit simpler from the code side. Each layer has a mask layer slot. If filled with a correct layer name, the mask layer will be rendered again in a separate buffer. This buffer will then be used in the blend pass to modulate the opacity of the layer. This approach is not 100% equivalent to the standard layering in most 2D applications if using varying alpha on a mask applied to multiple layers. However this technique does support any ordering arrangement of the masks. The layers used as mask can now still render normaly. --- source/blender/blenkernel/BKE_gpencil.h | 1 + source/blender/blenkernel/intern/gpencil.c | 8 +++ source/blender/draw/CMakeLists.txt | 1 - .../draw/engines/gpencil/gpencil_cache_utils.c | 79 ++++++++-------------- .../blender/draw/engines/gpencil/gpencil_engine.c | 58 ++++++++-------- .../blender/draw/engines/gpencil/gpencil_engine.h | 19 ++++-- .../blender/draw/engines/gpencil/gpencil_shader.c | 12 ---- .../gpencil/shaders/gpencil_layer_blend_frag.glsl | 8 ++- .../gpencil/shaders/gpencil_layer_mask_frag.glsl | 37 ---------- 9 files changed, 86 insertions(+), 137 deletions(-) delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_layer_mask_frag.glsl diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 495fe4eb7f6..f19af3cb1b7 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -179,6 +179,7 @@ struct bGPDframe *BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, struct bGPDframe *BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe); bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf); +struct bGPDlayer *BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name); struct bGPDlayer *BKE_gpencil_layer_active_get(struct bGPdata *gpd); void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active); void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 2b58525396b..02cbbcc4060 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -1002,6 +1002,14 @@ bool BKE_gpencil_layer_frame_delete(bGPDlayer *gpl, bGPDframe *gpf) return changed; } +bGPDlayer *BKE_gpencil_layer_named_get(bGPdata *gpd, const char *name) +{ + if (name[0] == '\0') { + return NULL; + } + return BLI_findstring(&gpd->layers, name, offsetof(bGPDlayer, info)); +} + /* get the active gp-layer for editing */ bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd) { diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 5d11578fe19..46fce6341f0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -283,7 +283,6 @@ data_to_c_simple(engines/gpencil/shaders/gpencil_antialiasing_vert.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_common_lib.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_composite_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_layer_blend_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_layer_mask_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_depth_merge_frag.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_depth_merge_vert.glsl SRC) data_to_c_simple(engines/gpencil/shaders/gpencil_vfx_frag.glsl SRC) diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index ae8ac611570..6b4e017c96b 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -115,6 +115,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, bGP const bool is_obact = ((pd->obact) && (pd->obact == ob)); const bool is_fade = ((pd->fade_layer_opacity > -1.0f) && (is_obact) && ((gpl->flag & GP_LAYER_ACTIVE) == 0)); + bool mask_invert = true; /* True because we invert the dummy texture red channel. */ /* Defines layer opacity. For active object depends of layer opacity factor, and * for no active object, depends if the fade grease pencil objects option is enabled. */ @@ -127,28 +128,21 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, bGP fade_layer_opacity = pd->fade_gp_object_opacity; } } + bGPdata *gpd = (bGPdata *)ob->data; GPENCIL_tLayer *tgp_layer = BLI_memblock_alloc(pd->gp_layer_pool); + tgp_layer->layer_id = BLI_findindex(&gpd->layers, gpl); + tgp_layer->mask_id = -1; - const bool is_mask = (gpl->flag & GP_LAYER_USE_MASK) != 0; - tgp_layer->is_mask = is_mask; - tgp_layer->do_masked_clear = false; - - if (!is_mask) { - tgp_layer->is_masked = false; - for (bGPDlayer *gpl_m = gpl->next; gpl_m; gpl_m = gpl_m->next) { - if (gpl_m->flag & GP_LAYER_USE_MASK) { - if (gpl_m->flag & GP_LAYER_HIDE) { - /* We don't mask but we dont try to mask with further layers. */ - } - else { - tgp_layer->is_masked = true; - } - break; - } - } + bGPDlayer *gpl_mask = BKE_gpencil_layer_named_get(gpd, gpl->mask_layer); + if (gpl_mask && (gpl_mask != gpl) && ((gpl_mask->flag & GP_LAYER_HIDE) == 0)) { + mask_invert = (gpl->flag & GP_LAYER_MASK_INVERT) != 0; + tgp_layer->mask_id = BLI_findindex(&gpd->layers, gpl_mask); + pd->use_mask_fb = true; } + const bool is_masked = tgp_layer->mask_id != -1; + { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; if (GPENCIL_3D_DRAWMODE(ob, gpd) || pd->draw_depth_only) { @@ -160,42 +154,13 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, bGP state |= DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_GREATER; } - if (gpl->flag & GP_LAYER_USE_MASK) { - state |= DRW_STATE_STENCIL_EQUAL; - } - else { - state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; - } + /* Always write stencil. Only used as optimization for blending. */ + state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; tgp_layer->geom_ps = DRW_pass_create("GPencil Layer", state); } - if (is_mask) { - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL | DRW_STATE_BLEND_MUL; - tgp_layer->blend_ps = DRW_pass_create("GPencil Mask Layer", state); - - GPUShader *sh = GPENCIL_shader_layer_mask_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); - DRW_shgroup_uniform_int_copy(grp, "isFirstPass", true); - DRW_shgroup_uniform_float_copy(grp, "maskOpacity", fade_layer_opacity); - DRW_shgroup_uniform_bool_copy(grp, "maskInvert", gpl->flag & GP_LAYER_MASK_INVERT); - DRW_shgroup_uniform_texture_ref(grp, "colorBuf", &pd->color_masked_tx); - DRW_shgroup_uniform_texture_ref(grp, "revealBuf", &pd->reveal_masked_tx); - DRW_shgroup_uniform_texture_ref(grp, "maskBuf", &pd->reveal_layer_tx); - DRW_shgroup_stencil_mask(grp, 0xFF); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - /* We cannot do custom blending on MultiTarget framebuffers. - * Workaround by doing 2 passes. */ - grp = DRW_shgroup_create_sub(grp); - DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); - DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); - DRW_shgroup_uniform_int_copy(grp, "isFirstPass", false); - DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - - pd->use_mask_fb = true; - } - else if ((gpl->blend_mode != eGplBlendMode_Regular) || (fade_layer_opacity < 1.0f)) { + if (is_masked || (gpl->blend_mode != eGplBlendMode_Regular) || (fade_layer_opacity < 1.0f)) { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL; switch (gpl->blend_mode) { case eGplBlendMode_Regular: @@ -227,6 +192,8 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, bGP DRW_shgroup_uniform_float_copy(grp, "blendOpacity", fade_layer_opacity); DRW_shgroup_uniform_texture_ref(grp, "colorBuf", &pd->color_layer_tx); DRW_shgroup_uniform_texture_ref(grp, "revealBuf", &pd->reveal_layer_tx); + DRW_shgroup_uniform_texture_ref(grp, "maskBuf", (is_masked) ? &pd->mask_tx : &pd->dummy_tx); + DRW_shgroup_uniform_bool_copy(grp, "maskInvert", mask_invert); DRW_shgroup_stencil_mask(grp, 0xFF); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); @@ -248,3 +215,17 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, bGP return tgp_layer; } + +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number) +{ + if (number >= 0) { + GPENCIL_tLayer *layer = tgp_ob->layers.first; + while (layer != NULL) { + if (layer->layer_id == number) { + return layer; + } + layer = layer->next; + } + } + return NULL; +} \ No newline at end of file diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 8736857c45c..437988c66c1 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -438,14 +438,9 @@ static void gp_layer_cache_populate(bGPDlayer *gpl, (gpl == iter->pd->sbuffer_layer); } - GPENCIL_tLayer *tgp_layer_prev = iter->tgp_ob->layers.last; GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(iter->pd, iter->ob, gpl); BLI_LINKS_APPEND(&iter->tgp_ob->layers, tgp_layer); - if (tgp_layer->is_masked && (tgp_layer_prev == NULL || !tgp_layer_prev->is_masked)) { - tgp_layer->do_masked_clear = true; - } - gpencil_material_resources_get(iter->matpool, 0, NULL, NULL, &iter->ubo_mat); const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D) || iter->pd->draw_depth_only; @@ -784,17 +779,22 @@ void GPENCIL_cache_finish(void *ved) } if (pd->use_mask_fb) { - /* We need to separate all the masked layer together in order to correctly mix them. */ - pd->color_masked_tx = DRW_texture_pool_query_2d( - size[0], size[1], format, &draw_engine_gpencil_type); - pd->reveal_masked_tx = DRW_texture_pool_query_2d( - size[0], size[1], format, &draw_engine_gpencil_type); - - GPU_framebuffer_ensure_config(&fbl->masked_fb, + /* We need an extra depth to not disturb the normal drawing. + * The color_tx is needed for framebuffer cmpleteness. */ + GPUTexture *color_tx, *depth_tx; + depth_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); + color_tx = DRW_texture_pool_query_2d(size[0], size[1], GPU_R8, &draw_engine_gpencil_type); + /* Use high quality format for render. */ + eGPUTextureFormat mask_format = pd->is_render ? GPU_R16 : GPU_R8; + pd->mask_tx = DRW_texture_pool_query_2d( + size[0], size[1], mask_format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->mask_fb, { - GPU_ATTACHMENT_TEXTURE(pd->depth_tx), - GPU_ATTACHMENT_TEXTURE(pd->color_masked_tx), - GPU_ATTACHMENT_TEXTURE(pd->reveal_masked_tx), + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_TEXTURE(color_tx), + GPU_ATTACHMENT_TEXTURE(pd->mask_tx), }); } @@ -838,6 +838,7 @@ static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob) GPENCIL_PrivateData *pd = vedata->stl->pd; GPENCIL_FramebufferList *fbl = vedata->fbl; float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}; + GPENCIL_tLayer *cached_mask = NULL; DRW_stats_group_start("GPencil Object"); @@ -851,16 +852,21 @@ static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob) } for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) { + GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, layer->mask_id); + if ((mask_layer != NULL) && (mask_layer != cached_mask)) { + cached_mask = mask_layer; + + GPU_framebuffer_bind(fbl->mask_fb); + GPU_framebuffer_clear_color_depth( + fbl->mask_fb, clear_cols[1], ob->is_drawmode3d ? 1.0f : 0.0f); + + DRW_draw_pass(mask_layer->geom_ps); + } + if (layer->blend_ps) { GPU_framebuffer_bind(fbl->layer_fb); GPU_framebuffer_multi_clear(fbl->layer_fb, clear_cols); } - else if (layer->is_masked) { - GPU_framebuffer_bind(fbl->masked_fb); - if (layer->do_masked_clear) { - GPU_framebuffer_multi_clear(fbl->masked_fb, clear_cols); - } - } else { GPU_framebuffer_bind(fb_object); } @@ -868,15 +874,7 @@ static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob) DRW_draw_pass(layer->geom_ps); if (layer->blend_ps) { - if (layer->is_masked) { - GPU_framebuffer_bind(fbl->masked_fb); - if (layer->do_masked_clear) { - GPU_framebuffer_multi_clear(fbl->masked_fb, clear_cols); - } - } - else { - GPU_framebuffer_bind(fb_object); - } + GPU_framebuffer_bind(fb_object); DRW_draw_pass(layer->blend_ps); } } diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 3ebefe83ce6..620cceef12d 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -154,10 +154,10 @@ typedef struct GPENCIL_tLayer { DRWPass *geom_ps; /** Blend pass to composite onto the target buffer (blends modes). NULL if not needed. */ DRWPass *blend_ps; - /** Used to identify which layers are masks and which are masked. */ - bool is_mask; - bool is_masked; - bool do_masked_clear; + /** Index in the layer list. Used as id for masking. */ + int layer_id; + /** Layer id of the mask. */ + int mask_id; } GPENCIL_tLayer; typedef struct GPENCIL_tObject { @@ -204,7 +204,7 @@ typedef struct GPENCIL_FramebufferList { struct GPUFrameBuffer *snapshot_fb; struct GPUFrameBuffer *layer_fb; struct GPUFrameBuffer *object_fb; - struct GPUFrameBuffer *masked_fb; + struct GPUFrameBuffer *mask_fb; struct GPUFrameBuffer *smaa_edge_fb; struct GPUFrameBuffer *smaa_weight_fb; } GPENCIL_FramebufferList; @@ -257,12 +257,12 @@ typedef struct GPENCIL_PrivateData { GPUTexture *color_tx; GPUTexture *color_layer_tx; GPUTexture *color_object_tx; - GPUTexture *color_masked_tx; /* Revealage is 1 - alpha */ GPUTexture *reveal_tx; GPUTexture *reveal_layer_tx; GPUTexture *reveal_object_tx; - GPUTexture *reveal_masked_tx; + /* Mask texture */ + GPUTexture *mask_tx; /* Anti-Aliasing. */ GPUTexture *smaa_edge_tx; GPUTexture *smaa_weight_tx; @@ -335,6 +335,10 @@ typedef struct GPENCIL_PrivateData { float fade_gp_object_opacity; /* Opacity for fading 3D objects. */ float fade_3d_object_opacity; + /* Mask opacity uniform. */ + float mask_opacity; + /* Mask invert uniform. */ + int mask_invert; } GPENCIL_PrivateData; /* geometry batch cache functions */ @@ -344,6 +348,7 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob); GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, Object *ob, struct bGPDlayer *layer); +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number); GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs); void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, int mat_id, diff --git a/source/blender/draw/engines/gpencil/gpencil_shader.c b/source/blender/draw/engines/gpencil/gpencil_shader.c index 3e436d27922..a2830fe5ec6 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader.c @@ -30,7 +30,6 @@ extern char datatoc_gpencil_antialiasing_frag_glsl[]; extern char datatoc_gpencil_antialiasing_vert_glsl[]; extern char datatoc_gpencil_composite_frag_glsl[]; extern char datatoc_gpencil_layer_blend_frag_glsl[]; -extern char datatoc_gpencil_layer_mask_frag_glsl[]; extern char datatoc_gpencil_depth_merge_frag_glsl[]; extern char datatoc_gpencil_depth_merge_vert_glsl[]; extern char datatoc_gpencil_vfx_frag_glsl[]; @@ -49,8 +48,6 @@ static struct { GPUShader *composite_sh; /* All layer blend types in one shader! */ GPUShader *layer_blend_sh; - /* To blend masked layer with other layers. */ - GPUShader *layer_mask_sh; /* Merge the final object depth to the depth buffer. */ GPUShader *depth_merge_sh; /* Effects. */ @@ -185,15 +182,6 @@ GPUShader *GPENCIL_shader_layer_blend_get(void) return g_shaders.layer_blend_sh; } -GPUShader *GPENCIL_shader_layer_mask_get(void) -{ - if (!g_shaders.layer_mask_sh) { - g_shaders.layer_mask_sh = DRW_shader_create_fullscreen(datatoc_gpencil_layer_mask_frag_glsl, - NULL); - } - return g_shaders.layer_mask_sh; -} - GPUShader *GPENCIL_shader_depth_merge_get(void) { if (!g_shaders.depth_merge_sh) { diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl index a43f69bee6a..5dd9aa5253a 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl @@ -1,6 +1,8 @@ uniform sampler2D colorBuf; uniform sampler2D revealBuf; +uniform sampler2D maskBuf; +uniform bool maskInvert; uniform int blendMode; uniform float blendOpacity; @@ -20,8 +22,12 @@ void main() /* Stroke only render mono-chromatic revealage. We convert to alpha. */ color.a = 1.0 - textureLod(revealBuf, uvcoordsvar.xy, 0).r; + float mask = textureLod(maskBuf, uvcoordsvar.xy, 0).r; + mask = (!maskInvert) ? (1.0 - mask) : mask; + mask *= blendOpacity; + fragColor = vec4(1.0, 0.0, 1.0, 1.0); fragRevealage = vec4(1.0, 0.0, 1.0, 1.0); - blend_mode_output(blendMode, color, blendOpacity, fragColor, fragRevealage); + blend_mode_output(blendMode, color, mask, fragColor, fragRevealage); } diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_mask_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_mask_frag.glsl deleted file mode 100644 index 64899b147dc..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_mask_frag.glsl +++ /dev/null @@ -1,37 +0,0 @@ - -uniform sampler2D colorBuf; -uniform sampler2D revealBuf; -uniform sampler2D maskBuf; -uniform float maskOpacity; -uniform bool maskInvert; -uniform bool isFirstPass; - -in vec4 uvcoordsvar; - -layout(location = 0) out vec4 fragColor; -layout(location = 1) out vec4 fragRevealage; - -void main() -{ - vec3 masked_color = texture(colorBuf, uvcoordsvar.xy).rgb; - vec3 masked_reveal = texture(revealBuf, uvcoordsvar.xy).rgb; - float mask = texture(maskBuf, uvcoordsvar.xy).r; - - if (maskInvert) { - mask = 1.0 - mask; - } - - mask = 1.0 - mask * maskOpacity; - - if (isFirstPass) { - /* Blend mode is multiply. */ - fragColor.rgb = fragRevealage.rgb = mix(vec3(1.0), masked_reveal, mask); - fragColor.a = fragRevealage.a = 1.0; - } - else { - /* Blend mode is additive. */ - fragRevealage = vec4(0.0); - fragColor.rgb = mix(vec3(0.0), masked_color, mask); - fragColor.a = 0.0; - } -} -- cgit v1.2.3