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:
-rw-r--r--source/blender/draw/intern/shaders/common_gpencil_lib.glsl409
-rw-r--r--source/blender/draw/intern/shaders/draw_object_infos_info.hh1
-rw-r--r--source/blender/draw/intern/shaders/draw_view_info.hh24
3 files changed, 434 insertions, 0 deletions
diff --git a/source/blender/draw/intern/shaders/common_gpencil_lib.glsl b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl
new file mode 100644
index 00000000000..8d0ba713616
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_gpencil_lib.glsl
@@ -0,0 +1,409 @@
+
+#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
+
+/* 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. */
+# define GPENCIL_IS_STROKE_VERTEX (gl_InstanceID != 0)
+
+/**
+ * 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(ivec4 ma,
+ ivec4 ma1,
+ ivec4 ma2,
+ ivec4 ma3,
+ vec4 pos,
+ vec4 pos1,
+ vec4 pos2,
+ vec4 pos3,
+ vec4 uv1,
+ vec4 uv2,
+ vec4 col1,
+ vec4 col2,
+ vec4 fcol1,
+ 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)
+{
+# 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_ALIGNMENT);
+
+ /* 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;
+ }
+
+ /* Endpoints, we discard the vertices. */
+ if (ma1.x == -1 || (!is_dot && ma2.x == -1)) {
+ /* We set the vertex at the camera origin to generate 0 fragments. */
+ out_ndc = vec4(0.0, 0.0, -3e36, 0.0);
+ return out_ndc;
+ }
+
+ /* 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));
+ out_color.a /= 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(ivec4 ma,
+ ivec4 ma1,
+ ivec4 ma2,
+ ivec4 ma3,
+ vec4 pos,
+ vec4 pos1,
+ vec4 pos2,
+ vec4 pos3,
+ vec4 uv1,
+ vec4 uv2,
+ vec4 col1,
+ vec4 col2,
+ vec4 fcol1,
+ 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(ma,
+ ma1,
+ ma2,
+ ma3,
+ pos,
+ pos1,
+ pos2,
+ pos3,
+ uv1,
+ uv2,
+ col1,
+ col2,
+ fcol1,
+ viewport_size,
+ GP_STROKE_ALIGNMENT_OBJECT,
+ vec2(1.0, 0.0),
+ out_P,
+ out_N,
+ out_color,
+ out_strength,
+ out_uv,
+ out_sspos,
+ out_aspect,
+ out_thickness,
+ out_hardness);
+}
+
+#endif
diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
index 11b33dec0d4..392b016fc3b 100644
--- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh
+++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
@@ -4,4 +4,5 @@
GPU_SHADER_CREATE_INFO(draw_object_infos)
.typedef_source("draw_shader_shared.h")
+ .define("OBINFO_LIB")
.uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH);
diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh
index 4a148901d66..a699b9013ef 100644
--- a/source/blender/draw/intern/shaders/draw_view_info.hh
+++ b/source/blender/draw/intern/shaders/draw_view_info.hh
@@ -103,4 +103,28 @@ GPU_SHADER_CREATE_INFO(draw_pointcloud)
GPU_SHADER_CREATE_INFO(draw_volume).additional_info("draw_modelmat", "draw_resource_id_uniform");
+GPU_SHADER_CREATE_INFO(draw_gpencil)
+ .typedef_source("gpencil_shader_shared.h")
+ .define("DRW_GPENCIL_INFO")
+ .vertex_in(0, Type::IVEC4, "ma")
+ .vertex_in(1, Type::IVEC4, "ma1")
+ .vertex_in(2, Type::IVEC4, "ma2")
+ .vertex_in(3, Type::IVEC4, "ma3")
+ .vertex_in(4, Type::VEC4, "pos")
+ .vertex_in(5, Type::VEC4, "pos1")
+ .vertex_in(6, Type::VEC4, "pos2")
+ .vertex_in(7, Type::VEC4, "pos3")
+ .vertex_in(8, Type::VEC4, "uv1")
+ .vertex_in(9, Type::VEC4, "uv2")
+ .vertex_in(10, Type::VEC4, "col1")
+ .vertex_in(11, Type::VEC4, "col2")
+ .vertex_in(12, Type::VEC4, "fcol1")
+ /* Per Object */
+ .push_constant(Type::FLOAT, "gpThicknessScale") /* TODO(fclem): Replace with object info. */
+ .push_constant(Type::FLOAT, "gpThicknessWorldScale") /* TODO(fclem): Same as above. */
+ .define("gpThicknessIsScreenSpace", "(gpThicknessWorldScale < 0.0)")
+ /* Per Layer */
+ .push_constant(Type::FLOAT, "gpThicknessOffset")
+ .additional_info("draw_modelmat", "draw_resource_id_uniform", "draw_object_infos");
+
/** \} */