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

common_gpencil_lib.glsl « shaders « intern « draw « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: def841b07aa3526d7e1a39d214cbbc481afbfb80 (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

#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)

#ifndef DRW_GPENCIL_INFO
#  error Missing additional info draw_gpencil
#endif

#ifdef GPU_FRAGMENT_SHADER
float gpencil_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

vec2 gpencil_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 gpencil_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 gpencil_decode_hardness(int packed_data)
{
  return float((uint(packed_data) & 0x3FC0000u) >> 18u) * (1.0 / 255.0);
}

vec2 gpencil_project_to_screenspace(vec4 v, vec4 viewport_size)
{
  return ((v.xy / v.w) * 0.5 + 0.5) * viewport_size.xy;
}

float gpencil_stroke_thickness_modulate(float thickness, vec4 ndc_pos, vec4 viewport_size)
{
  /* Modify stroke thickness by object and layer factors. */
  thickness = max(1.0, thickness * gpThicknessScale + gpThicknessOffset);

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

float gpencil_clamp_small_stroke_thickness(float thickness, vec4 ndc_pos)
{
  /* To avoid aliasing artifacts, we clamp the line thickness and
   * reduce its opacity in the fragment shader. */
  float min_thickness = ndc_pos.w * 1.3;
  thickness = max(min_thickness, thickness);

  return thickness;
}

#ifdef GPU_VERTEX_SHADER

int gpencil_stroke_point_id()
{
  return (gl_VertexID & ~GP_IS_STROKE_VERTEX_BIT) >> GP_VERTEX_ID_SHIFT;
}

bool gpencil_is_stroke_vertex()
{
  return flag_test(gl_VertexID, GP_IS_STROKE_VERTEX_BIT);
}

/**
 * Returns value of gl_Position.
 *
 * To declare in vertex shader.
 * in ivec4 ma, ma1, ma2, ma3;
 * in vec4 pos, pos1, pos2, pos3, uv1, uv2, col1, col2, fcol1;
 *
 * All of these attributes 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).
 *
 * Material : x is material index, y is stroke_id, z is point_id,
 *            w is aspect & rotation & hardness packed.
 * Position : contains thickness in 4th component.
 * UV : xy is UV for fills, z is U of stroke, w is strength.
 *
 *
 * WARNING: Max attribute count is actually 14 because OSX OpenGL implementation
 * considers gl_VertexID and gl_InstanceID as vertex attribute. (see T74536)
 */
vec4 gpencil_vertex(vec4 viewport_size,
                    gpMaterialFlag material_flags,
                    vec2 alignment_rot,
                    /* World Position. */
                    out vec3 out_P,
                    /* World Normal. */
                    out vec3 out_N,
                    /* Vertex Color. */
                    out vec4 out_color,
                    /* Stroke Strength. */
                    out float out_strength,
                    /* UV coordinates. */
                    out vec2 out_uv,
                    /* Screen-Space segment endpoints. */
                    out vec4 out_sspos,
                    /* Stroke aspect ratio. */
                    out vec2 out_aspect,
                    /* Stroke thickness (x: clamped, y: unclamped). */
                    out vec2 out_thickness,
                    /* Stroke hardness. */
                    out float out_hardness)
{
  int stroke_point_id = (gl_VertexID & ~GP_IS_STROKE_VERTEX_BIT) >> GP_VERTEX_ID_SHIFT;

  /* Attribute Loading. */
  vec4 pos = texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 0);
  vec4 pos1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 0);
  vec4 pos2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 0);
  vec4 pos3 = texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 0);
  ivec4 ma = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id - 1) * 3 + 1));
  ivec4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 1));
  ivec4 ma2 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 1));
  ivec4 ma3 = floatBitsToInt(texelFetch(gp_pos_tx, (stroke_point_id + 2) * 3 + 1));
  vec4 uv1 = texelFetch(gp_pos_tx, (stroke_point_id + 0) * 3 + 2);
  vec4 uv2 = texelFetch(gp_pos_tx, (stroke_point_id + 1) * 3 + 2);

  vec4 col1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 0);
  vec4 col2 = texelFetch(gp_col_tx, (stroke_point_id + 1) * 2 + 0);
  vec4 fcol1 = texelFetch(gp_col_tx, (stroke_point_id + 0) * 2 + 1);

#  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

  vec4 out_ndc;

  if (gpencil_is_stroke_vertex()) {
    bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT);
    bool is_squares = !flag_test(material_flags, GP_STROKE_DOTS);

    /* 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;
    }

    /* Avoid using a vertex attribute 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(ModelMatrix, (use_curr) ? pos.xyz : pos3.xyz);
    vec3 wpos1 = transform_point(ModelMatrix, pos1.xyz);
    vec3 wpos2 = transform_point(ModelMatrix, pos2.xyz);

    vec3 T;
    if (is_dot) {
      /* Shade as facing billboards. */
      T = ViewMatrixInverse[0].xyz;
    }
    else if (use_curr && ma.x != -1) {
      T = wpos1 - wpos_adj;
    }
    else {
      T = wpos2 - wpos1;
    }
    T = safe_normalize(T);

    vec3 B = cross(T, ViewMatrixInverse[2].xyz);
    out_N = normalize(cross(B, T));

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

    out_ndc = (use_curr) ? ndc1 : ndc2;
    out_P = (use_curr) ? wpos1 : wpos2;
    out_strength = abs((use_curr) ? strength1 : strength2);

    vec2 ss_adj = gpencil_project_to_screenspace(ndc_adj, viewport_size);
    vec2 ss1 = gpencil_project_to_screenspace(ndc1, viewport_size);
    vec2 ss2 = gpencil_project_to_screenspace(ndc2, viewport_size);
    /* 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 = gpencil_stroke_thickness_modulate(thickness, out_ndc, viewport_size);
    float clamped_thickness = gpencil_clamp_small_stroke_thickness(thickness, out_ndc);

    out_uv = vec2(x, y) * 0.5 + 0.5;
    out_hardness = gpencil_decode_hardness(use_curr ? hardness1 : hardness2);

    if (is_dot) {
      uint alignement_mode = material_flags & GP_STROKE_ALIGNMENT;

      /* For one point strokes use object alignment. */
      if (alignement_mode == GP_STROKE_ALIGNMENT_STROKE && ma.x == -1 && ma2.x == -1) {
        alignement_mode = GP_STROKE_ALIGNMENT_OBJECT;
      }

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

      /* Rotation: Encoded as Cos + Sin sign. */
      float uv_rot = gpencil_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);
      /* TODO(@fclem): Optimize these 2 matrix mul into one by only having one rotation angle and
       * using a cosine approximation. */
      x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis;
      x_axis = mat2(alignment_rot.x, -alignment_rot.y, alignment_rot.y, alignment_rot.x) * x_axis;
      /* Rotate 90° Counter-Clockwise. */
      vec2 y_axis = vec2(-x_axis.y, x_axis.x);

      out_aspect = gpencil_decode_aspect(aspect1);

      x *= out_aspect.x;
      y *= out_aspect.y;

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

      out_ndc.xy += (x * x_axis + y * y_axis) * viewport_size.zw * clamped_thickness;

      out_sspos.xy = ss1;
      out_sspos.zw = ss1 + x_axis * 0.5;
      out_thickness.x = (is_squares) ? 1e18 : (clamped_thickness / out_ndc.w);
      out_thickness.y = (is_squares) ? 1e18 : (thickness / out_ndc.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);
      miter_tan = (miter_break || is_stroke_start || is_stroke_end) ? line :
                                                                      (miter_tan / miter_dot);
      /* Rotate 90° Counter-Clockwise. */
      vec2 miter = vec2(-miter_tan.y, miter_tan.x);

      out_sspos.xy = ss1;
      out_sspos.zw = ss2;
      out_thickness.x = clamped_thickness / out_ndc.w;
      out_thickness.y = thickness / out_ndc.w;
      out_aspect = vec2(1.0);

      vec2 screen_ofs = miter * y;

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

      out_ndc.xy += screen_ofs * viewport_size.zw * clamped_thickness;

      out_uv.x = (use_curr) ? uv1.z : uv2.z;
    }

    out_color = (use_curr) ? col1 : col2;
  }
  else {
    /* Fill vertex. */
    out_P = transform_point(ModelMatrix, pos1.xyz);
    out_ndc = point_world_to_ndc(out_P);
    out_uv = uv1.xy;
    out_thickness.x = 1e18;
    out_thickness.y = 1e20;
    out_hardness = 1.0;
    out_aspect = vec2(1.0);
    out_sspos = vec4(0.0);

    /* Flat normal following camera and object bounds. */
    vec3 V = cameraVec(ModelMatrix[3].xyz);
    vec3 N = normal_world_to_object(V);
    N *= OrcoTexCoFactors[1].xyz;
    N = normal_object_to_world(N);
    out_N = safe_normalize(N);

    /* Decode fill opacity. */
    out_color = vec4(fcol1.rgb, floor(fcol1.a / 10.0) / 10000.0);

    /* We still offset the fills a little to avoid overlaps */
    out_ndc.z += 0.000002;
  }

#  undef thickness1
#  undef thickness2
#  undef strength1
#  undef strength2
#  undef hardness1
#  undef hardness2
#  undef uvrot1
#  undef aspect1

  return out_ndc;
}

vec4 gpencil_vertex(vec4 viewport_size,
                    out vec3 out_P,
                    out vec3 out_N,
                    out vec4 out_color,
                    out float out_strength,
                    out vec2 out_uv,
                    out vec4 out_sspos,
                    out vec2 out_aspect,
                    out vec2 out_thickness,
                    out float out_hardness)
{
  return gpencil_vertex(viewport_size,
                        0u,
                        vec2(1.0, 0.0),
                        out_P,
                        out_N,
                        out_color,
                        out_strength,
                        out_uv,
                        out_sspos,
                        out_aspect,
                        out_thickness,
                        out_hardness);
}

#endif