Grease Pencil: Improve cyclical end cap rendering
This improves the rendering of cyclical strokes in Grease Pencil by connection the start and end lines segments together. To make this possible the Vertex Shader needs to know if the curve is cyclical and the start and end of the current stroke. This PR stores the cyclical in the sign of `point index`. All points already know the start of the curve as `stroke_id`, so all we need is the end of the curve. Grease Pencil already uses a point at the start and end of the stroke as padding. So if the first buffer point stored the index to the last buffer point, any other point and just go to the first point then to the last. Pull Request: https://projects.blender.org/blender/blender/pulls/143976
This commit is contained in:
committed by
Falk David
parent
fb6f191981
commit
fbbd79aa50
@@ -39,7 +39,8 @@ void main()
|
||||
float3 vert_N;
|
||||
|
||||
int4 ma1 = floatBitsToInt(texelFetch(gp_pos_tx, gpencil_stroke_point_id() * 3 + 1));
|
||||
gpMaterial gp_mat = gp_materials[ma1.x + gp_material_offset];
|
||||
PointData point_data1 = decode_ma(ma1);
|
||||
gpMaterial gp_mat = gp_materials[point_data1.mat + gp_material_offset];
|
||||
gpMaterialFlag gp_flag = gpMaterialFlag(floatBitsToUint(gp_mat._flag));
|
||||
|
||||
gl_Position = gpencil_vertex(float4(viewport_size, 1.0f / viewport_size),
|
||||
@@ -77,8 +78,7 @@ void main()
|
||||
else if (flag_test(gp_flag, GP_STROKE_OVERLAP)) {
|
||||
/* Use the index of the point as depth.
|
||||
* This means the stroke can overlap itself. */
|
||||
float point_index = float(ma1.z);
|
||||
gp_interp_flat.depth = (point_index + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
gp_interp_flat.depth = (point_data1.point_id + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
}
|
||||
else {
|
||||
/* Use the index of first point of the stroke as depth.
|
||||
@@ -86,8 +86,7 @@ void main()
|
||||
* 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. */
|
||||
float stroke_index = float(ma1.y);
|
||||
gp_interp_flat.depth = (stroke_index + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
gp_interp_flat.depth = (point_data1.stroke_id + gp_stroke_index_offset + 2.0f) * 0.0000002f;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -123,7 +122,7 @@ void main()
|
||||
gpencil_color_output(fill_col, fcol_decode, 1.0f, gp_mat._fill_texture_mix);
|
||||
|
||||
gp_interp_flat.mat_flag = gp_flag & GP_FILL_FLAGS;
|
||||
gp_interp_flat.mat_flag |= uint(ma1.x + gp_material_offset) << GPENCIl_MATID_SHIFT;
|
||||
gp_interp_flat.mat_flag |= uint(point_data1.mat + gp_material_offset) << GPENCIl_MATID_SHIFT;
|
||||
|
||||
gp_interp.uv = float2x2(gp_mat.fill_uv_rot_scale.xy, gp_mat.fill_uv_rot_scale.zw) * uv1.xy +
|
||||
gp_mat._fill_uv_offset;
|
||||
@@ -134,8 +133,7 @@ void main()
|
||||
}
|
||||
else {
|
||||
/* Use the index of first point of the stroke as depth. */
|
||||
float stroke_index = float(ma1.y);
|
||||
gp_interp_flat.depth = (stroke_index + gp_stroke_index_offset + 1.0f) * 0.0000002f;
|
||||
gp_interp_flat.depth = (point_data1.stroke_id + gp_stroke_index_offset + 1.0f) * 0.0000002f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1251,6 +1251,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
|
||||
int point_i,
|
||||
int idx,
|
||||
float u_stroke,
|
||||
bool cyclic,
|
||||
const float4x2 &texture_matrix,
|
||||
GreasePencilStrokeVert &s_vert,
|
||||
GreasePencilColorVert &c_vert) {
|
||||
@@ -1262,8 +1263,11 @@ static void grease_pencil_geom_batch_ensure(Object &object,
|
||||
((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
|
||||
s_vert.opacity = opacities[point_i] *
|
||||
((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
|
||||
s_vert.point_id = verts_range[idx];
|
||||
|
||||
/* Store if the curve is cyclic in the sign of the point index. */
|
||||
s_vert.point_id = cyclic ? -verts_range[idx] : verts_range[idx];
|
||||
s_vert.stroke_id = verts_range.first();
|
||||
|
||||
/* The material index is allowed to be negative as it's stored as a generic attribute. To
|
||||
* ensure the material used by the shader is valid this needs to be clamped to zero. */
|
||||
s_vert.mat = std::max(materials[curve_i], 0) % GPENCIL_MATERIAL_BUFFER_LEN;
|
||||
@@ -1299,6 +1303,8 @@ static void grease_pencil_geom_batch_ensure(Object &object,
|
||||
|
||||
/* First vertex is not drawn. */
|
||||
verts_slice.first().mat = -1;
|
||||
/* The first vertex will have the index of the last vertex. */
|
||||
verts_slice.first().stroke_id = verts_range.last();
|
||||
|
||||
/* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
|
||||
if (points.size() >= 3) {
|
||||
@@ -1325,6 +1331,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
|
||||
points[i],
|
||||
idx,
|
||||
u_stroke,
|
||||
is_cyclic,
|
||||
texture_matrix,
|
||||
verts_slice[idx],
|
||||
cols_slice[idx]);
|
||||
@@ -1341,6 +1348,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
|
||||
points[0],
|
||||
idx,
|
||||
u_stroke,
|
||||
is_cyclic,
|
||||
texture_matrix,
|
||||
verts_slice[idx],
|
||||
cols_slice[idx]);
|
||||
|
||||
@@ -51,6 +51,27 @@ float gpencil_stroke_round_cap_mask(
|
||||
}
|
||||
#endif
|
||||
|
||||
struct PointData {
|
||||
bool cyclical;
|
||||
int mat, stroke_id, point_id, packed_data;
|
||||
};
|
||||
|
||||
PointData decode_ma(int4 ma)
|
||||
{
|
||||
PointData data;
|
||||
|
||||
data.mat = ma.x;
|
||||
data.stroke_id = ma.y;
|
||||
/* Take the absolute because the sign is for cyclical. */
|
||||
data.point_id = abs(ma.z);
|
||||
/* Aspect, UV Rotation and Hardness. */
|
||||
data.packed_data = ma.w;
|
||||
/* Cyclical is stored in the sign of the point index. */
|
||||
data.cyclical = ma.z < 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
float2 gpencil_decode_aspect(int packed_data)
|
||||
{
|
||||
float asp = float(uint(packed_data) & 0x1FFu) * (1.0f / 255.0f);
|
||||
@@ -166,11 +187,6 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
# 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
|
||||
|
||||
float4 out_ndc;
|
||||
|
||||
@@ -178,8 +194,32 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
bool is_dot = flag_test(material_flags, GP_STROKE_ALIGNMENT);
|
||||
bool is_squares = !flag_test(material_flags, GP_STROKE_DOTS);
|
||||
|
||||
bool is_first = (ma.x == -1);
|
||||
bool is_last = (ma3.x == -1);
|
||||
bool is_single = is_first && (ma2.x == -1);
|
||||
|
||||
PointData point_data1 = decode_ma(ma1);
|
||||
PointData point_data2 = decode_ma(ma2);
|
||||
|
||||
/* Join the first and last point if the curve is cyclical. */
|
||||
if (point_data1.cyclical && !is_single) {
|
||||
if (is_first) {
|
||||
/* The first point will have the index of the last point. */
|
||||
PointData point_data = decode_ma(ma);
|
||||
int last_stroke_id = point_data.stroke_id;
|
||||
ma = floatBitsToInt(texelFetch(gp_pos_tx, (last_stroke_id - 2) * 3 + 1));
|
||||
pos = texelFetch(gp_pos_tx, (last_stroke_id - 2) * 3 + 0);
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
int first_stroke_id = point_data1.stroke_id;
|
||||
ma3 = floatBitsToInt(texelFetch(gp_pos_tx, (first_stroke_id + 2) * 3 + 1));
|
||||
pos3 = texelFetch(gp_pos_tx, (first_stroke_id + 2) * 3 + 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */
|
||||
if (!is_dot && ma.x == -1 && ma2.x == -1) {
|
||||
if (!is_dot && is_single) {
|
||||
is_dot = true;
|
||||
is_squares = false;
|
||||
}
|
||||
@@ -240,13 +280,14 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
float clamped_thickness = max(0.0f, thickness);
|
||||
|
||||
out_uv = float2(x, y) * 0.5f + 0.5f;
|
||||
out_hardness = gpencil_decode_hardness(use_curr ? hardness1 : hardness2);
|
||||
out_hardness = gpencil_decode_hardness(use_curr ? point_data1.packed_data :
|
||||
point_data2.packed_data);
|
||||
|
||||
if (is_dot) {
|
||||
uint alignment_mode = material_flags & GP_STROKE_ALIGNMENT;
|
||||
|
||||
/* For one point strokes use object alignment. */
|
||||
if (alignment_mode == GP_STROKE_ALIGNMENT_STROKE && ma.x == -1 && ma2.x == -1) {
|
||||
if (alignment_mode == GP_STROKE_ALIGNMENT_STROKE && is_single) {
|
||||
alignment_mode = GP_STROKE_ALIGNMENT_OBJECT;
|
||||
}
|
||||
|
||||
@@ -265,7 +306,7 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
}
|
||||
|
||||
/* Rotation: Encoded as Cos + Sin sign. */
|
||||
float uv_rot = gpencil_decode_uvrot(uvrot1);
|
||||
float uv_rot = gpencil_decode_uvrot(point_data1.packed_data);
|
||||
float rot_sin = sqrt(max(0.0f, 1.0f - uv_rot * uv_rot)) * sign(uv_rot);
|
||||
float rot_cos = abs(uv_rot);
|
||||
/* TODO(@fclem): Optimize these 2 matrix multiply into one by only having one rotation angle
|
||||
@@ -276,7 +317,7 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
/* Rotate 90 degrees counter-clockwise. */
|
||||
float2 y_axis = float2(-x_axis.y, x_axis.x);
|
||||
|
||||
out_aspect = gpencil_decode_aspect(aspect1);
|
||||
out_aspect = gpencil_decode_aspect(point_data1.packed_data);
|
||||
|
||||
x *= out_aspect.x;
|
||||
y *= out_aspect.y;
|
||||
@@ -356,10 +397,6 @@ float4 gpencil_vertex(float4 viewport_res,
|
||||
# undef thickness2
|
||||
# undef strength1
|
||||
# undef strength2
|
||||
# undef hardness1
|
||||
# undef hardness2
|
||||
# undef uvrot1
|
||||
# undef aspect1
|
||||
|
||||
return out_ndc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user