Rendering resolution independent Curves

I don’t see how your fragment shader can work. Without comments, I’m not entirely sure what you are trying to accomplish with it. Fragment shaders are executed after triangles or lines are rasterized, and only for those fragments that are within the triangle or on the line. It looks to me like your fragment shader needs to be executed for every pixel in the framebuffer, which just isn’t how they work.

I haven’t tried using the fragment shader’s builtin derivative approximation functions and I’m not entirely sure how to use them usefully, but I see a few problems with this approach. For one, it appears you have to have your Bezier curve in a texture map to start (also, texture maps are resolution dependent). For another, the derivative approximation functions can’t handle discontinuities. If you need a general solution, then you need to handle discontinuities.

Here’s a thought about accuracy and offset curves: you cannot, in general, exactly offset a curve with a Bezier curve of finite degree. However, you can get very close; in particular, if you are willing to subdivide your base curve, you can get arbitrarily close.

I didn’t think of this until today, but the tesselation control shader is more versatile than I realized. Shaders consume their inputs. Typically, one just copies the inputs to the outputs, but that’s not necessary. In particular, the tesselation control shader can exactly subdivide the input Bezier curve to another Bezier curve that just fills the viewport (this is only an issue assuming the base Bezier curve is so large that only a portion fits within the viewport). That subdivided Bezier curve can then be tesselated with up to 64 subdivisions within the tesselation primitive generator, and all (or most) of those 64 subdivisions will lie within the viewport. In other words, despite the 64 subdivision tesselation limit of the tesselation primitive generator, it is indeed possible to subdivide to an infinitely fine mesh (up to the limits of floating point number representation) with the OpenGL 4.x pipeline. Pretty completely resolution independent.

It’s easy to render cubic Bezier curves with shaders, assuming you’re using OpenGL 4.x. The brains of the process are programmed in the tesselation evaluation shader:


//tesselation evaluation shader for cubic Bezier curves,
//by david_f_knight, 2010-08-30.

#version 400 core
layout (isolines, fractional_odd_spacing) in;


void main (void) {
  float  c1, c2, c3, c4;  //cubic Bezier coefficients.
  float  t;               //parametric value t.
  float  tr;              //reversed parametric value, 1.0 - t.


  t = gl_TessCoord.x;  //copy the parametric value t (stored in x).
  tr = 1.0 - t;
  c1 = tr * tr * tr;
  c2 = 3.0 * t * tr;
  c3 = c2 * t;
  c2 *= tr;
  c4 = t * t * t;

  //evaluate Bezier curve at t:
  gl_Position = c1 * gl_in[0].gl_Position +
                c2 * gl_in[1].gl_Position +
                c3 * gl_in[2].gl_Position +
                c4 * gl_in[3].gl_Position;
}

That’s it. The vertex and fragment shaders are trivial and can be one line programs. The tesselation control shader is optional, and I think the geometry shader is optional. (If the geometry shader isn’t optional, all it has to do is copy its inputs to its outputs. Of course, if your goal is to offset the base curve, you can do that in the geometry shader by offsetting straight line segments, and/or you can fill the space between the base curve and the offset curve with pairs of triangles that form rectangles parallel to the base curve.)

– david_f_knight