https://vicrucann.github.io/tutorials/bezier-shader/
GLSL shader that draws a Bezier line given four control points
Context
his post is a continuation of one of the previous examples on how to draw thick and smooth lines in 3D space. Now we want to be able to not just draw a straight line, but a curve. As an example, the curve can be represented by a set of Bezier curves which were obtained by using a curve fitting algorithm. So the main purpose of this post is to provide an example code snippet of a GLSL shader that is being able to:
GL_LINE_STRIP_ADJACENCY
into triangular stripIn this post it will be shown how we can use the code from the point 1 and extend it to drawing curves in 3D instead of lines and polylines.
The Bezier curve is represented by two endpoints and two control points. Therefore, in order to pass to the shader a Bezier curve (or a set of the curves), we have to provide all the control points.
The modification of the given shader is straightforward given the cubic Bezier curve formula:
B(t)=(1−t)3P0+3(1−t)2tP1+3(1−t)t2P2+t3P3B(t)=(1−t)3P0+3(1−t)2tP1+3(1−t)t2P2+t3P3
Where PiPi is one of the four control points for a given Bezier curve.
We incorporate the given formula into the functions to use inside our GLSL code:
vec4 toBezier(float delta, int i, vec4 P0, vec4 P1, vec4 P2, vec4 P3)
{
float t = delta * float(i);
float t2 = t * t;
float one_minus_t = 1.0 - t;
float one_minus_t2 = one_minus_t * one_minus_t;
return (P0 * one_minus_t2 * one_minus_t + P1 * 3.0 * t * one_minus_t2 + P2 * 3.0 * t2 * one_minus_t + P3 * t2 * t);
}
Most of the code stays intact, with the exception of the loop part when we go through all the passed vertices. Now we haven to take into account how many segments there should be in the curve. The introduced variable nSegments
can be passed to the shader by means of uniforms or set to constant values inside the shader. The pseudo-code of the loop of the main function will look like following:
for (int i=0; i<=nSegments; ++i){
// Sample the control points to the curve points
Points[i] = toBezier(delta, i, ...);
// interpolate the colors
colors[i] = ... ;
// transform to the screen coordinate space
points[i] = toScreenSpace(Points[i]);
// extract z-values so that the drawing order remains correct
zValues[i] = toZValue(Points[i]);
// finally send all the info for drawing procedure
drawSegment(points, color, zValues);
}
A few words on the color interpolation. For that part the main idea is to define to which of the three Bezier fragment the current point belongs to, and then interpolate between the two colors of endpoints of that Bezier segment based on the location of the point to the endpoints of the segment. Refer to the source code for concrete example.
shader-3dcurve github repository contains the minimalistic OpenSceneGraph scene and the GLSL shaders that help to generate two curves located in different 3D planes: