Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gpencil_common_lib.glsl « shaders « gpencil « engines « draw « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: cb34515a960ff8f55d28ccc8256b83c0a3cab560 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606

/* Must match C declaration. */
struct gpMaterial {
  vec4 stroke_color;
  vec4 fill_color;
  vec4 fill_mix_color;
  vec4 fill_uv_rot_scale;
  vec4 fill_uv_offset;
  /* Put float/int at the end to avoid padding error */
  float stroke_texture_mix;
  float stroke_u_scale;
  float fill_texture_mix;
  int flag;
  /* Please ensure 16 byte alignment (multiple of vec4). */
};

/* flag */
#define GP_STROKE_ALIGNMENT_STROKE 1
#define GP_STROKE_ALIGNMENT_OBJECT 2
#define GP_STROKE_ALIGNMENT_FIXED 3
#define GP_STROKE_ALIGNMENT 0x3
#define GP_STROKE_OVERLAP (1 << 2)
#define GP_STROKE_TEXTURE_USE (1 << 3)
#define GP_STROKE_TEXTURE_STENCIL (1 << 4)
#define GP_STROKE_TEXTURE_PREMUL (1 << 5)
#define GP_STROKE_DOTS (1 << 6)
#define GP_FILL_TEXTURE_USE (1 << 10)
#define GP_FILL_TEXTURE_PREMUL (1 << 11)
#define GP_FILL_TEXTURE_CLIP (1 << 12)
#define GP_FILL_GRADIENT_USE (1 << 13)
#define GP_FILL_GRADIENT_RADIAL (1 << 14)
/* High bits are used to pass material ID to fragment shader. */
#define GP_MATID_SHIFT 16

/* Multiline defines can crash blender with certain GPU drivers. */
/* clang-format off */
#define GP_FILL_FLAGS (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP | GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL)
/* clang-format on */

#define GP_FLAG_TEST(flag, val) (((flag) & (val)) != 0)

/* Must match C declaration. */
struct gpLight {
  vec4 color_type;
  vec4 right;
  vec4 up;
  vec4 forward;
  vec4 position;
  /* Please ensure 16 byte alignment (multiple of vec4). */
};

#define spot_size right.w
#define spot_blend up.w

#define GP_LIGHT_TYPE_POINT 0.0
#define GP_LIGHT_TYPE_SPOT 1.0
#define GP_LIGHT_TYPE_SUN 2.0
#define GP_LIGHT_TYPE_AMBIENT 3.0

#ifdef GP_MATERIAL_BUFFER_LEN

layout(std140) uniform gpMaterialBlock
{
  gpMaterial materials[GP_MATERIAL_BUFFER_LEN];
};

#endif

#ifdef GPENCIL_LIGHT_BUFFER_LEN

layout(std140) uniform gpLightBlock
{
  gpLight lights[GPENCIL_LIGHT_BUFFER_LEN];
};

#endif

/* Must match eGPLayerBlendModes */
#define MODE_REGULAR 0
#define MODE_OVERLAY 1
#define MODE_ADD 2
#define MODE_SUB 3
#define MODE_MULTIPLY 4
#define MODE_DIVIDE 5
#define MODE_OVERLAY_SECOND_PASS 999

void blend_mode_output(
    int blend_mode, vec4 color, float opacity, out vec4 frag_color, out vec4 frag_revealage)
{
  switch (blend_mode) {
    case MODE_REGULAR:
      /* Reminder: Blending func is premult alpha blend (dst.rgba * (1 - src.a) + src.rgb).*/
      color *= opacity;
      frag_color = color;
      frag_revealage = vec4(0.0, 0.0, 0.0, color.a);
      break;
    case MODE_MULTIPLY:
      /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/
      color.a *= opacity;
      frag_revealage = frag_color = (1.0 - color.a) + color.a * color;
      break;
    case MODE_DIVIDE:
      /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/
      color.a *= opacity;
      frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18);
      break;
    case MODE_OVERLAY:
      /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/
      /**
       * We need to separate the overlay equation into 2 term (one mul and one add).
       * This is the standard overlay equation (per channel):
       * rtn = (src < 0.5) ? (2.0 * src * dst) : (1.0 - 2.0 * (1.0 - src) * (1.0 - dst));
       * We rewrite the second branch like this:
       * rtn = 1 - 2 * (1 - src) * (1 - dst);
       * rtn = 1 - 2 (1 - dst + src * dst - src);
       * rtn = 1 - 2 (1 - dst * (1 - src) - src);
       * rtn = 1 - 2 + dst * (2 - 2 * src) + 2 * src;
       * rtn = (- 1 + 2 * src) + dst * (2 - 2 * src);
       **/
      color = mix(vec4(0.5), color, color.a * opacity);
      vec4 s = step(-0.5, -color);
      frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0);
      break;
    case MODE_OVERLAY_SECOND_PASS:
      /* Reminder: Blending func is additive blend (dst.rgba + src.rgba).*/
      color = mix(vec4(0.5), color, color.a * opacity);
      frag_revealage = frag_color = (-1.0 + 2.0 * color) * step(-0.5, -color);
      break;
    case MODE_SUB:
    case MODE_ADD:
      /* Reminder: Blending func is additive / subtractive blend (dst.rgba +/- src.rgba).*/
      frag_color = color * color.a * opacity;
      frag_revealage = vec4(0.0);
      break;
  }
}

#ifdef GPU_VERTEX_SHADER
#  define IN_OUT out
#else
#  define IN_OUT in
#endif

/* Shader interface. */
IN_OUT vec4 finalColorMul;
IN_OUT vec4 finalColorAdd;
IN_OUT vec3 finalPos;
IN_OUT vec2 finalUvs;
noperspective IN_OUT float strokeThickness;
noperspective IN_OUT float strokeHardeness;
flat IN_OUT vec2 strokeAspect;
flat IN_OUT vec2 strokePt1;
flat IN_OUT vec2 strokePt2;
flat IN_OUT int matFlag;
flat IN_OUT float depth;

#ifdef GPU_FRAGMENT_SHADER

#  define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))

float stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac)
{
  /* We create our own uv space to avoid issues with triangulation and linear
   * interpolation artifacts. */
  vec2 line = p2.xy - p1.xy;
  vec2 pos = gl_FragCoord.xy - p1.xy;
  float line_len = length(line);
  float half_line_len = line_len * 0.5;
  /* Normalize */
  line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0);
  /* Create a uv space that englobe the whole segment into a capsule. */
  vec2 uv_end;
  uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0);
  uv_end.y = dot(vec2(-line.y, line.x), pos);
  /* Divide by stroke radius. */
  uv_end /= thickness;
  uv_end *= aspect;

  float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0);
  if (hardfac > 0.999) {
    return step(1e-8, dist);
  }
  else {
    /* Modulate the falloff profile */
    float hardness = 1.0 - hardfac;
    dist = pow(dist, mix(0.01, 10.0, hardness));
    return smoothstep(0.0, 1.0, dist);
  }
}

#endif

uniform vec2 sizeViewport;
uniform vec2 sizeViewportInv;

/* Per Object */
uniform bool strokeOrder3d;
uniform int gpMaterialOffset;
uniform float thicknessScale;
uniform float thicknessWorldScale;
#define thicknessIsScreenSpace (thicknessWorldScale < 0.0)
#define MATERIAL(m) materials[m + gpMaterialOffset]

#ifdef GPU_VERTEX_SHADER

/* Per Layer */
uniform float thicknessOffset;
uniform float vertexColorOpacity;
uniform vec4 layerTint;
uniform float layerOpacity; /* Used for onion skin. */
uniform float strokeIndexOffset = 0.0;

/* All of these attribs are quad loaded the same way
 * as GL_LINES_ADJACENCY would feed a geometry shader:
 * - ma reference the previous adjacency point.
 * - ma1 reference the current line first point.
 * - ma2 reference the current line second point.
 * - ma3 reference the next adjacency point.
 * Note that we are rendering quad instances and not using any index buffer (except for fills).
 */
/* x is material index, y is stroke_id, z is point_id, w is aspect & rotation & hardness packed. */
in ivec4 ma;
in ivec4 ma1;
in ivec4 ma2;
in ivec4 ma3;
/* Position contains thickness in 4th component. */
in vec4 pos;  /* Prev adj vert */
in vec4 pos1; /* Current edge */
in vec4 pos2; /* Current edge */
in vec4 pos3; /* Next adj vert */
/* xy is UV for fills, z is U of stroke, w is strength.  */
in vec4 uv1;
in vec4 uv2;
in vec4 col1;
in vec4 col2;
in vec4 fcol1;
/* WARNING: Max attrib count is actually 14 because OSX OpenGL implementation
 * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536) */
#  define stroke_id1 ma1.y
#  define point_id1 ma1.z
#  define thickness1 pos1.w
#  define thickness2 pos2.w
#  define strength1 uv1.w
#  define strength2 uv2.w
/* Packed! need to be decoded. */
#  define hardness1 ma1.w
#  define hardness2 ma2.w
#  define uvrot1 ma1.w
#  define aspect1 ma1.w

vec2 decode_aspect(int packed_data)
{
  float asp = float(uint(packed_data) & 0x1FFu) * (1.0 / 255.0);
  return (asp > 1.0) ? vec2(1.0, (asp - 1.0)) : vec2(asp, 1.0);
}

float decode_uvrot(int packed_data)
{
  uint udata = uint(packed_data);
  float uvrot = 1e-8 + float((udata & 0x1FE00u) >> 9u) * (1.0 / 255.0);
  return ((udata & 0x20000u) != 0u) ? -uvrot : uvrot;
}

float decode_hardness(int packed_data)
{
  return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0);
}

void discard_vert()
{
  /* We set the vertex at the camera origin to generate 0 fragments. */
  gl_Position = vec4(0.0, 0.0, -3e36, 0.0);
}

vec2 project_to_screenspace(vec4 v)
{
  return ((v.xy / v.w) * 0.5 + 0.5) * sizeViewport;
}

vec2 rotate_90deg(vec2 v)
{
  /* Counter Clock-Wise. */
  return vec2(-v.y, v.x);
}

mat4 model_matrix_get()
{
  return ModelMatrix;
}

vec3 transform_point(mat4 m, vec3 v)
{
  return (m * vec4(v, 1.0)).xyz;
}

vec2 safe_normalize(vec2 v)
{
  float len_sqr = dot(v, v);
  if (len_sqr > 0.0) {
    return v / sqrt(len_sqr);
  }
  else {
    return vec2(1.0, 0.0);
  }
}

vec2 safe_normalize_len(vec2 v, out float len)
{
  len = sqrt(dot(v, v));
  if (len > 0.0) {
    return v / len;
  }
  else {
    return vec2(1.0, 0.0);
  }
}

float stroke_thickness_modulate(float thickness)
{
  /* Modify stroke thickness by object and layer factors.-*/
  thickness *= thicknessScale;
  thickness += thicknessOffset;
  thickness = max(1.0, thickness);

  if (thicknessIsScreenSpace) {
    /* Multiply offset by view Z so that offset is constant in screenspace.
     * (e.i: does not change with the distance to camera) */
    thickness *= gl_Position.w;
  }
  else {
    /* World space point size. */
    thickness *= thicknessWorldScale * ProjectionMatrix[1][1] * sizeViewport.y;
  }
  return thickness;
}

#  ifdef GP_MATERIAL_BUFFER_LEN
void color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex)
{
  /* Mix stroke with other colors. */
  vec4 mixed_col = stroke_col;
  mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * vertexColorOpacity);
  mixed_col.rgb = mix(mixed_col.rgb, layerTint.rgb, layerTint.a);
  mixed_col.a *= vert_strength * layerOpacity;
  /**
   * This is what the fragment shader looks like.
   * out = col * finalColorMul + col.a * finalColorAdd.
   * finalColorMul is how much of the texture color to keep.
   * finalColorAdd is how much of the mixed color to add.
   * Note that we never add alpha. This is to keep the texture act as a stencil.
   * We do however, modulate the alpha (reduce it).
   **/
  /* We add the mixed color. This is 100% mix (no texture visible). */
  finalColorMul = vec4(mixed_col.aaa, mixed_col.a);
  finalColorAdd = vec4(mixed_col.rgb * mixed_col.a, 0.0);
  /* Then we blend according to the texture mix factor.
   * Note that we keep the alpha modulation. */
  finalColorMul.rgb *= mix_tex;
  finalColorAdd.rgb *= 1.0 - mix_tex;
}
#  endif

void stroke_vertex()
{
  int m = ma1.x;
  bool is_dot = false;
  bool is_squares = false;

#  ifdef GP_MATERIAL_BUFFER_LEN
  if (m != -1) {
    is_dot = GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_ALIGNMENT);
    is_squares = !GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_DOTS);
  }
#  endif

  /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */
  if (!is_dot && ma.x == -1 && ma2.x == -1) {
    is_dot = true;
    is_squares = false;
  }

  /* Enpoints, we discard the vertices. */
  if (ma1.x == -1 || (!is_dot && ma2.x == -1)) {
    discard_vert();
    return;
  }

  mat4 model_mat = model_matrix_get();

  /* Avoid using a vertex attrib for quad positioning. */
  float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */
  float y = float(gl_VertexID & 2) - 1.0;       /* [-1..1] */

  bool use_curr = is_dot || (x == -1.0);

  vec3 wpos_adj = transform_point(model_mat, (use_curr) ? pos.xyz : pos3.xyz);
  vec3 wpos1 = transform_point(model_mat, pos1.xyz);
  vec3 wpos2 = transform_point(model_mat, pos2.xyz);

  vec4 ndc_adj = point_world_to_ndc(wpos_adj);
  vec4 ndc1 = point_world_to_ndc(wpos1);
  vec4 ndc2 = point_world_to_ndc(wpos2);

  gl_Position = (use_curr) ? ndc1 : ndc2;
  finalPos = (use_curr) ? wpos1 : wpos2;

  vec2 ss_adj = project_to_screenspace(ndc_adj);
  vec2 ss1 = project_to_screenspace(ndc1);
  vec2 ss2 = project_to_screenspace(ndc2);
  /* Screenspace Lines tangents. */
  float line_len;
  vec2 line = safe_normalize_len(ss2 - ss1, line_len);
  vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2));

  float thickness = abs((use_curr) ? thickness1 : thickness2);
  thickness = stroke_thickness_modulate(thickness);

  finalUvs = vec2(x, y) * 0.5 + 0.5;
  strokeHardeness = decode_hardness(use_curr ? hardness1 : hardness2);

  if (is_dot) {
#  ifdef GP_MATERIAL_BUFFER_LEN
    int alignement = MATERIAL(m).flag & GP_STROKE_ALIGNMENT;
#  endif

    vec2 x_axis;
#  ifdef GP_MATERIAL_BUFFER_LEN
    if (alignement == GP_STROKE_ALIGNMENT_STROKE) {
      x_axis = (ma2.x == -1) ? line_adj : line;
    }
    else if (alignement == GP_STROKE_ALIGNMENT_FIXED) {
      /* Default for no-material drawing. */
      x_axis = vec2(1.0, 0.0);
    }
    else
#  endif
    { /* GP_STROKE_ALIGNMENT_OBJECT */
      vec4 ndc_x = point_world_to_ndc(wpos1 + model_mat[0].xyz);
      vec2 ss_x = project_to_screenspace(ndc_x);
      x_axis = safe_normalize(ss_x - ss1);
    }

    /* Rotation: Encoded as Cos + Sin sign. */
    float uv_rot = decode_uvrot(uvrot1);
    float rot_sin = sqrt(max(0.0, 1.0 - uv_rot * uv_rot)) * sign(uv_rot);
    float rot_cos = abs(uv_rot);
    x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis;

    vec2 y_axis = rotate_90deg(x_axis);

    strokeAspect = decode_aspect(aspect1);

    x *= strokeAspect.x;
    y *= strokeAspect.y;

    /* Invert for vertex shader. */
    strokeAspect = 1.0 / strokeAspect;

    gl_Position.xy += (x * x_axis + y * y_axis) * sizeViewportInv.xy * thickness;

    strokePt1 = ss1;
    strokePt2 = ss1 + x_axis * 0.5;
    strokeThickness = (is_squares) ? 1e18 : (thickness / gl_Position.w);
  }
  else {
    bool is_stroke_start = (ma.x == -1 && x == -1);
    bool is_stroke_end = (ma3.x == -1 && x == 1);

    /* Mitter tangent vector. */
    vec2 miter_tan = safe_normalize(line_adj + line);
    float miter_dot = dot(miter_tan, line_adj);
    /* Break corners after a certain angle to avoid really thick corners. */
    const float miter_limit = 0.5; /* cos(60°) */
    bool miter_break = (miter_dot < miter_limit) || is_stroke_start || is_stroke_end;
    miter_tan = (miter_break) ? line : (miter_tan / miter_dot);

    vec2 miter = rotate_90deg(miter_tan);

    strokePt1.xy = ss1;
    strokePt2.xy = ss2;
    strokeThickness = thickness / gl_Position.w;
    strokeAspect = vec2(1.0);

    vec2 screen_ofs = miter * y;

    /* Reminder: we packed the cap flag into the sign of stength and thickness sign. */
    if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) ||
        miter_break) {
      screen_ofs += line * x;
    }

    gl_Position.xy += screen_ofs * sizeViewportInv.xy * thickness;

    finalUvs.x = (use_curr) ? uv1.z : uv2.z;
#  ifdef GP_MATERIAL_BUFFER_LEN
    finalUvs.x *= MATERIAL(m).stroke_u_scale;
#  endif
  }

#  ifdef GP_MATERIAL_BUFFER_LEN
  vec4 vert_col = (use_curr) ? col1 : col2;
  float vert_strength = abs((use_curr) ? strength1 : strength2);
  vec4 stroke_col = MATERIAL(m).stroke_color;
  float mix_tex = MATERIAL(m).stroke_texture_mix;

  color_output(stroke_col, vert_col, vert_strength, mix_tex);

  matFlag = MATERIAL(m).flag & ~GP_FILL_FLAGS;
#  endif

  if (strokeOrder3d) {
    /* Use the fragment depth (see fragment shader). */
    depth = -1.0;
  }
#  ifdef GP_MATERIAL_BUFFER_LEN
  else if (GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_OVERLAP)) {
    /* Use the index of the point as depth.
     * This means the stroke can overlap itself. */
    depth = (point_id1 + strokeIndexOffset + 1.0) * 0.0000002;
  }
#  endif
  else {
    /* Use the index of first point of the stroke as depth.
     * We render using a greater depth test this means the stroke
     * cannot overlap itself.
     * We offset by one so that the fill can be overlapped by its stroke.
     * The offset is ok since we pad the strokes data because of adjacency infos. */
    depth = (stroke_id1 + strokeIndexOffset + 1.0) * 0.0000002;
  }
}

void fill_vertex()
{
  mat4 model_mat = model_matrix_get();

  vec3 wpos = transform_point(model_mat, pos1.xyz);
  gl_Position = point_world_to_ndc(wpos);
  finalPos = wpos;

#  ifdef GP_MATERIAL_BUFFER_LEN
  int m = ma1.x;

  vec4 fill_col = MATERIAL(m).fill_color;
  float mix_tex = MATERIAL(m).fill_texture_mix;

  /* Special case: We don't modulate alpha in gradient mode. */
  if (GP_FLAG_TEST(MATERIAL(m).flag, GP_FILL_GRADIENT_USE)) {
    fill_col.a = 1.0;
  }

  /* Decode fill opacity. */
  vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0));
  float fill_opacity = fcol1.a - (fcol_decode.a * 10);
  fcol_decode.a /= 10000.0;

  /* Apply opacity. */
  fill_col.a *= fill_opacity;
  /* If factor is > 1 force opacity. */
  if (fill_opacity > 1.0) {
    fill_col.a += fill_opacity - 1.0;
  }

  fill_col.a = clamp(fill_col.a, 0.0, 1.0);

  color_output(fill_col, fcol_decode, 1.0, mix_tex);

  matFlag = MATERIAL(m).flag & GP_FILL_FLAGS;
  matFlag |= m << GP_MATID_SHIFT;

  vec2 loc = MATERIAL(m).fill_uv_offset.xy;
  mat2x2 rot_scale = mat2x2(MATERIAL(m).fill_uv_rot_scale.xy, MATERIAL(m).fill_uv_rot_scale.zw);
  finalUvs = rot_scale * uv1.xy + loc;
#  endif

  strokeThickness = 1e18;
  strokeAspect = vec2(1.0);
  strokePt1 = strokePt2 = vec2(0.0);

  if (strokeOrder3d) {
    /* Use the fragment depth (see fragment shader). */
    depth = -1.0;
    /* We still offset the fills a little to avoid overlaps */
    gl_Position.z += 0.000002;
  }
  else {
    /* Use the index of first point of the stroke as depth. */
    depth = (stroke_id1 + strokeIndexOffset) * 0.0000002;
  }
}

void gpencil_vertex()
{
  /* Trick to detect if a drawcall is stroke or fill.
   * This does mean that we need to draw an empty stroke segment before starting
   * to draw the real stroke segments. */
  bool is_fill = (gl_InstanceID == 0);

  if (!is_fill) {
    stroke_vertex();
  }
  else {
    fill_vertex();
  }
}

#endif