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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl')
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl593
1 files changed, 593 insertions, 0 deletions
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
new file mode 100644
index 00000000000..8774b633467
--- /dev/null
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -0,0 +1,593 @@
+
+/* 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).
+ */
+in vec4 ma;
+in vec4 ma1;
+in vec4 ma2;
+in vec4 ma3;
+# define strength1 ma1.y
+# define strength2 ma2.y
+# define stroke_id1 ma1.z
+# define point_id1 ma1.w
+/* 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 */
+# define thickness1 pos1.w
+# define thickness2 pos2.w
+/* xy is UV for fills, z is U of stroke, w is cosine of UV angle with sign of sine. */
+in vec4 uv1;
+in vec4 uv2;
+
+in vec4 col1;
+in vec4 col2;
+
+in vec4 fcol1;
+
+/* hard.x is aspect. */
+in vec2 hard1;
+in vec2 hard2;
+# define aspect1 hard1.x
+# define aspect2 hard2.x
+
+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 = int(ma1.x);
+ bool is_dot = false;
+ bool is_squares = false;
+
+# ifdef GP_MATERIAL_BUFFER_LEN
+ if (m != -1.0) {
+ 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.0 && ma2.x == -1.0) {
+ is_dot = true;
+ is_squares = false;
+ }
+
+ /* Enpoints, we discard the vertices. */
+ if (ma1.x == -1.0 || (!is_dot && ma2.x == -1.0)) {
+ 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 = (use_curr) ? hard1.y : hard2.y;
+
+ 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.0) ? 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 rot_sin = sqrt(1.0 - uv1.w * uv1.w) * sign(uv1.w);
+ float rot_cos = abs(uv1.w);
+ x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis;
+
+ vec2 y_axis = rotate_90deg(x_axis);
+
+ strokeAspect.x = aspect1;
+
+ if (strokeAspect.x > 1.0) {
+ strokeAspect.y = strokeAspect.x;
+ strokeAspect.x = 1.0;
+ }
+ else {
+ strokeAspect.x = 1.0 / strokeAspect.x;
+ strokeAspect.y = 1.0;
+ }
+
+ x /= strokeAspect.x;
+ y /= strokeAspect.y;
+
+ 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.0 && x == -1.0);
+ bool is_stroke_end = (ma3.x == -1.0 && x == 1.0);
+
+ /* 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 = int(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.0f;
+
+ /* Apply opacity. */
+ fill_col.a *= fill_opacity;
+ /* If factor is > 1 force opacity. */
+ if (fill_opacity > 1.0) {
+ fill_col.a += fill_opacity - 1.0f;
+ }
+
+ 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