Only compute xyz-to-uv matrix in non-linear case

This commit is contained in:
Grant Sanderson 2023-01-19 20:24:32 -08:00
parent 1c2ec03f7d
commit 909e515a2f
3 changed files with 57 additions and 41 deletions

View file

@ -65,38 +65,40 @@ mat4 map_onto_x_axis(vec3 src0, vec3 src1){
if(vect.x > 1 - 1e-6) return shift;
// Equivalent to cross(vect, vec3(1, 0, 0))
vec3 axis = vec3(0.0, vect.z, -vect.y);
mat4 rotate = rotation(normalize(axis), vect.x);
vec3 axis = normalize(vec3(0.0, vect.z, -vect.y));
mat4 rotate = rotation(axis, vect.x);
return rotate * shift;
}
mat4 get_xyz_to_uv(vec3 b0, vec3 b1, vec3 b2, float temp_is_linear, out float is_linear){
mat4 get_xyz_to_uv(
vec3 b0, vec3 b1, vec3 b2,
float threshold,
out bool exceeds_threshold
){
/*
Returns a matrix for an affine transformation which maps a set of quadratic
bezier controls points into a new coordinate system such that the bezier curve
coincides with y = x^2, or in the case of a linear curve, it's mapped to the x-axis.
Populates the matrix `result` with an affine transformation which maps a set of
quadratic bezier controls points into a new coordinate system such that the bezier
curve coincides with y = x^2.
If the x-range under this part of the curve exceeds `threshold`, this returns false
and populates result a matrix mapping b0 and b2 onto the x-axis
*/
is_linear = temp_is_linear;
vec2 xs = xs_on_clean_parabola(b0, b1, b2);
float x0 = xs[0];
float x1 = 0.5 * (xs[0] + xs[1]);
float x2 = xs[1];
// Portions of the parabola y = x^2 where abs(x) exceeds
// this value are treated as straight lines.
float thresh = 2.0;
if (!bool(is_linear)){
vec2 xs = xs_on_clean_parabola(b0, b1, b2);
float x0 = xs.x;
float x2 = xs.y;
if((x0 > thresh && x2 > thresh) || (x0 < -thresh && x2 < -thresh)){
is_linear = 1.0;
}else{
// This triangle on the xy plane should be isometric
// to (b0, b1, b2), and it should define a quadratic
// bezier segment aligned with y = x^2
vec3 dst0 = vec3(x0, x0 * x0, 0.0);
vec3 dst1 = vec3(0.5 * (x0 + x2), x0 * x2, 0.0);
vec3 dst2 = vec3(x2, x2 * x2, 0.0);
return map_triangles(b0, b1, b2, dst0, dst1, dst2);
}
exceeds_threshold = (min(x0, x2) > threshold || max(x0, x2) < -threshold);
if(exceeds_threshold){
return map_onto_x_axis(b0, b2);
}
// Only lands here if is_linear is 1.0
return map_onto_x_axis(b0, b2);
// This triangle on the xy plane should be isometric
// to (b0, b1, b2), and it should define a quadratic
// bezier segment aligned with y = x^2
vec3 dst0 = vec3(x0, x0 * x0, 0.0);
vec3 dst1 = vec3(x1, x0 * x2, 0.0);
vec3 dst2 = vec3(x2, x2 * x2, 0.0);
return map_triangles(b0, b1, b2, dst0, dst1, dst2);
}

View file

@ -79,11 +79,13 @@ void emit_pentagon(
}
// Compute xy_to_uv matrix, and potentially re-evaluate bezier degree
mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, is_linear, is_linear);
bool too_steep;
mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, 10.0, too_steep);
if(too_steep) is_linear = 1.0;
uv_anti_alias_width = aaw * length(xyz_to_uv[0].xyz);
for(int i = 0; i < 5; i++){
int j = int(sign(i - 1) + 1); // Maps i = [0, 1, 2, 3, 4] onto j = [0, 0, 1, 2, 2]
int j = int[5](0, 0, 1, 2, 2)[i];
vec3 corner = corners[i];
uv_coords = (xyz_to_uv * vec4(corner, 1.0)).xy;
emit_vertex_wrapper(corner, j, unit_normal);

View file

@ -49,7 +49,7 @@ vec3 get_joint_unit_normal(vec4 joint_product){
result = v_joint_product[1].xyz;
}
float norm = length(result);
return (norm > 1e-10) ? result / norm : vec3(0.0, 0.0, 1.0);
return (norm > 1e-5) ? result / norm : vec3(0.0, 0.0, 1.0);
}
@ -171,28 +171,40 @@ void main() {
float cos_angle = v_joint_product[1].w;
is_linear = float(cos_angle > COS_THRESHOLD);
// If the curve is flat, put the middle control in the midpoint
if (bool(is_linear)) p1 = 0.5 * (p0 + p2);
// We want to change the coordinates to a space where the curve
// coincides with y = x^2, between some values x0 and x2. Or, in
// the case of a linear curve (bezier degree 1), just put it on
// the x-axis
mat4 xyz_to_uv = get_xyz_to_uv(p0, p1, p2, is_linear, is_linear);
// the case of a linear curve just put it on the x-axis
mat4 xyz_to_uv;
float uv_scale_factor;
if(!bool(is_linear)){
bool too_steep;
xyz_to_uv = get_xyz_to_uv(p0, p1, p2, 2.0, too_steep);
if(too_steep) is_linear = 1.0;
uv_scale_factor = length(xyz_to_uv[0].xyz);
}
float uv_scale_factor = length(xyz_to_uv[0].xyz);
float scaled_aaw = anti_alias_width * pixel_size;
uv_anti_alias_width = uv_scale_factor * scaled_aaw;
vec3 corners[6];
get_corners(p0, p1, p2, v01, v12, scaled_aaw, corners);
// Emit each corner
for(int i = 0; i < 6; i++){
int vert_index = i / 2;
uv_coords = (xyz_to_uv * vec4(corners[i], 1)).xy;
uv_stroke_width = uv_scale_factor * v_stroke_width[vert_index];
color = finalize_color(v_color[vert_index], corners[i], unit_normal);
float stroke_width = v_stroke_width[i / 2];
if(bool(is_linear)){
float sign = vec2(-1, 1)[i % 2];
// In this case, we only really care about
// the v coordinate
uv_coords = vec2(0, sign * (0.5 * stroke_width + scaled_aaw));
uv_anti_alias_width = scaled_aaw;
uv_stroke_width = stroke_width;
}else{
uv_coords = (xyz_to_uv * vec4(corners[i], 1.0)).xy;
uv_stroke_width = uv_scale_factor * stroke_width;
uv_anti_alias_width = uv_scale_factor * scaled_aaw;
}
color = finalize_color(v_color[i / 2], corners[i], unit_normal);
gl_Position = get_gl_Position(corners[i]);
EmitVertex();
}