LineArt: Trimming edges right at the image border

This option allows the edge to end right at the border
instead of extending beyond.
Useful when having multiple camera setup where you
want the border to be clean.
Also moved overscan option down inside "Composition" sub panel
so it makes more sense.

Reviewed By: Antonio Vazquez (antoniov)

Differential Revision: https://developer.blender.org/D12126
This commit is contained in:
YimingWu 2021-10-27 00:10:25 +08:00
parent 3371a4c472
commit ec831ce5df
Notes: blender-bot 2023-02-14 04:46:12 +01:00
Referenced by issue #87739, Line Art further improvement list
6 changed files with 146 additions and 9 deletions

View File

@ -406,7 +406,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE);
uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
}
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
@ -692,6 +692,9 @@ static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE);
if (show_in_front) {
uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR);
}

View File

@ -182,9 +182,9 @@ typedef struct LineartEdgeChain {
typedef struct LineartEdgeChainItem {
struct LineartEdgeChainItem *next, *prev;
/** Need z value for fading */
float pos[3];
/** For restoring position to 3d space */
/** Need z value for fading, w value for image frame clipping. */
float pos[4];
/** For restoring position to 3d space. */
float gpos[3];
float normal[3];
unsigned char line_type;
@ -299,6 +299,7 @@ typedef struct LineartRenderBuffer {
bool use_loose_as_contour;
bool use_loose_edge_chain;
bool use_geometry_space_chain;
bool use_image_boundary_trimming;
bool filter_face_mark;
bool filter_face_mark_invert;
@ -594,6 +595,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb);
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb);
void MOD_lineart_chain_connect(LineartRenderBuffer *rb);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance);
void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb,

View File

@ -126,7 +126,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem));
copy_v2_v2(eci->pos, fbcoord);
copy_v4_v4(eci->pos, fbcoord);
copy_v3_v3(eci->gpos, gpos);
eci->index = index;
copy_v3_v3(eci->normal, normal);
@ -156,7 +156,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb
eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem));
copy_v2_v2(eci->pos, fbcoord);
copy_v4_v4(eci->pos, fbcoord);
copy_v3_v3(eci->gpos, gpos);
eci->index = index;
copy_v3_v3(eci->normal, normal);
@ -177,15 +177,15 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
int last_occlusion;
unsigned char last_transparency;
/* Used when converting from double. */
float use_fbcoord[2];
float use_fbcoord[4];
float use_gpos[3];
#define VERT_COORD_TO_FLOAT(a) \
copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \
copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \
copy_v3fl_v3db(use_gpos, (a)->gloc);
#define POS_TO_FLOAT(lpos, gpos) \
copy_v2fl_v2db(use_fbcoord, lpos); \
copy_v3fl_v3db(use_fbcoord, lpos); \
copy_v3fl_v3db(use_gpos, gpos);
LRT_ITER_ALL_LINES_BEGIN
@ -262,6 +262,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
ec,
@ -287,6 +288,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
ec,
@ -340,6 +342,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
ec,
@ -403,6 +406,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency;
POS_TO_FLOAT(lpos, gpos)
@ -430,6 +434,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
ec,
@ -952,6 +957,118 @@ void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance)
}
}
static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb,
LineartEdgeChainItem *eci_inside,
LineartEdgeChainItem *eci_outside)
{
float isec[2];
float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f};
bool found = false;
LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside;
if (eci2->pos[0] < -1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0);
}
if (!found && eci2->pos[0] > 1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0);
}
if (!found && eci2->pos[1] < -1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0);
}
if (!found && eci2->pos[1] > 1.0f) {
found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0);
}
if (UNLIKELY(!found)) {
return NULL;
}
float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ?
ratiof(eci1->pos[0], eci2->pos[0], isec[0]) :
ratiof(eci1->pos[1], eci2->pos[1], isec[1]);
float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]);
LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool,
sizeof(LineartEdgeChainItem));
memcpy(eci, eci1, sizeof(LineartEdgeChainItem));
interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio);
interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio);
eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio);
eci->next = eci->prev = NULL;
return eci;
}
#define LRT_ECI_INSIDE(eci) \
((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \
(eci)->pos[1] <= 1.0f)
void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb)
{
LineartEdgeChain *ec;
LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci;
bool is_inside, new_inside;
ListBase swap = {0};
swap.first = rb->chains.first;
swap.last = rb->chains.last;
rb->chains.last = rb->chains.first = NULL;
while ((ec = BLI_pophead(&swap)) != NULL) {
bool ec_added = false;
LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
is_inside = LRT_ECI_INSIDE(first_eci) ? true : false;
if (!is_inside) {
ec->picked = true;
}
for (eci = first_eci->next; eci; eci = next_eci) {
next_eci = eci->next;
prev_eci = eci->prev;
/* We only need to do something if the edge crossed from outside to the inside or from inside
* to the outside. */
if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) {
if (new_inside == false) {
/* Stroke goes out. */
new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci);
LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool,
sizeof(LineartEdgeChain));
memcpy(new_ec, ec, sizeof(LineartEdgeChain));
new_ec->chain.first = next_eci;
eci->prev = NULL;
prev_eci->next = NULL;
ec->chain.last = prev_eci;
BLI_addtail(&ec->chain, new_eci);
BLI_addtail(&rb->chains, ec);
ec_added = true;
ec = new_ec;
next_eci = eci->next;
is_inside = new_inside;
continue;
}
else {
/* Stroke comes in. */
new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci);
ec->chain.first = eci;
eci->prev = NULL;
BLI_addhead(&ec->chain, new_eci);
ec_added = false;
next_eci = eci->next;
is_inside = new_inside;
continue;
}
}
}
if ((!ec_added) && is_inside) {
BLI_addtail(&rb->chains, ec);
}
}
}
/**
* This should always be the last stage!, see the end of
* #MOD_lineart_chain_split_for_fixed_occlusion().

View File

@ -3077,6 +3077,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0;
rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0;
rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0;
rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) !=
0;
/* See lineart_edge_from_triangle() for how this option may impact performance. */
rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0;
@ -4202,6 +4204,10 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50);
}
if (rb->use_image_boundary_trimming) {
MOD_lineart_chain_clip_at_border(rb);
}
if (rb->angle_splitting_threshold > FLT_EPSILON) {
MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold);
}

View File

@ -50,6 +50,7 @@ typedef enum eLineartMainFlags {
LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15),
LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16),
LRT_USE_CUSTOM_CAMERA = (1 << 17),
LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20),
} eLineartMainFlags;
typedef enum eLineartEdgeFlag {

View File

@ -3405,6 +3405,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING);
RNA_def_property_ui_text(
prop,
"Image Boundary Trimming",
"Trim all edges right at the boundary of image(including overscan region)");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
RNA_define_lib_overridable(false);
}