Search code examples
mathgraphicslanguage-agnosticbezier

Splitting rational bezier


According to someone a rational bezier can be split by projecting its control points from 2d to 3d (or from 3d to 4d and so on), splitting the resulting non-rational curve with De Casteljau, and then projecting the two curves back to 2d which makes them rational again.

I have this Shadertoy prototype where I tried to put the weights directly in place of the z value, before realizing there would need to be some kind of transformation on the xy coordinates for the De Casteljau calculated midpoint (m) to align with the weighted curve. A projection matrix can have a field of view and an aspect ratio attribute from what I've seen.

What kind of matrix do I need to get the points and weights to align correctly, and what should I do with the weights to begin with to turn them into z coordinates?

vec3[6] split(float[3] r, vec2[3] p, float t)
{
    vec3 a = vec3(p[0],r[0]);
    vec3 b = vec3(p[1],r[1]);
    vec3 c = vec3(p[2],r[2]);
    vec3 ab = mix(a, b, t);
    vec3 bc = mix(b, c, t);
    vec3 m = mix(ab, bc, t);
    
    vec3[3] l = vec3[3](a,ab,m);
    vec3[3] R = vec3[3](m,bc,c);
    return vec3[6](l[0],l[1],l[2],R[0],R[1],R[2]);
}

Solution

  • In degree 2, for example, a Bezier is f(t) = (1-t)2P0 + 2t(1-t)P1 + t2P2

    A rational Bezier is (1-t)2P0w0 + 2t(1-t)P1w1 + t2P2w2 / (1-t)2w0 + 2t(1-t)w1 + t2w2

    If your perspective transform maps (x,y,z) to (x/z, y/z), then you can move the weights directly into the z coordinate to get the correct denominator, but you also have to multiply the corresponding points to make the new points map to the same (x,y).

    When you do both, it simplifies, and your new points are:

    P0' = w0(P0,1)

    P1' = w1(P1,1)

    P2' = w2(P2,1)

    The regular 3D Bezier will then map to the same (x,y) curve under perspective transformation:

    f(t) = (1-t)2P0' + 2t(1-t)P1' + t2P2'