Bezier curves are certainly familiar to everyone, and there are many excellent articles and dynamic diagrams on the web that introduce and understand bezier curves.
The following two are classic giFs.
Second-order Bezier curve:
Third-order Bezier curve:
Since I often have to deal with Bessel curves in my work, I would like to briefly explain my own understanding:
Now suppose we want to draw a line in the coordinate system, the equation of the line is very simple, y=x, it is easy to get the following:
Now, if we restrict x to a closed interval between 0 and 1, then we know that y is also between 0 and 1.
How many numbers can x take from 0 to 1? The answer, of course, is infinite.
And similarly, there’s an infinite number of values of y. Every x has a unique y corresponding to it, an (x,y) is a point on the coordinate system.
So the resulting line segment between 0 and 1 is actually composed of an infinite number of points.
So how long is this line segment? The length is determined by the range of x, so if x is between 0 and 2, then the line segment is twice as long.
In addition, if the range of x is not infinite, but increases from 0 to 1 at 0.05 intervals, the result is a series of points.
Because a point is an ideal description, it has no width, height and area in mathematics.
However, if you draw a dot on scratch paper, no matter whether you use a pencil, brush, fountain pen or brush, a dot will always take up space.
The area of a point drawn with a brush may require dozens of points drawn with a pencil.
In real life, if you were to plot a series of points between 0 and 1 in the first coordinate system diagram with 0.05 intervals, the end result would be no different from drawing a line segment.
That’s the difference between reality and ideal. Ideal is a string of points, reality is a line.
Let’s put this logic on the phone screen.
The smallest unit of display on a phone screen is the pixel, and a 1920 x 1080 screen is the number of pixels in each direction.
If you draw a line segment as wide as the screen, the smallest point is a pixel, and the most is 1080 points.
The more pixels a point takes up, the fewer points it actually needs to draw, which is a potential optimization.
So let’s go back to the Bezier curve.
Curves and lines all have one thing in common, they all have their own specific equations, but the line we’re going to use is a simpler one, y equals x, and you can see it at a glance.
The equation of a line y = x can be mathematically described as follows: y is a function of x, that is, y = F(x), where x determines the length of the line.
According to the above understanding, the line of this length is actually composed of an infinite number of points within the range of x.
In contrast, bezier curve equation and corresponding graph are as follows:
- Second-order Bezier curve:
Where P0 and P2 are the starting points and P1 is the control point.
- Third order Bezier curve
Where P0 and P3 are the starting points, and P1 and P2 are the control points.
It’s easy to understand, if we’re going to draw a curve, we have to have a starting point and an ending point to specify the range of the curve.
The control point is to specify the radian of the curve, or to specify the bending direction of the curve, different control points to draw the curve is not the same.
In addition, it can be observed that no matter how many order Bezier curves there are, there are parameters t and the range of values of T.
If t is in a closed interval between 0 and 1, then there are actually an infinite number of values of t, and then t can be interpreted as x in the line above.
In this way, the starting point and control point can be fixed parameters, then the calculation formula of Bezier curve becomes B = F(t), B is a function of T, and the value range of T is 0~1 closed interval.
In other words, the Bezier curve, with selected starting points and control points, can still be regarded as consisting of an infinite number of points corresponding to t in the closed interval between 0 and 1.
With the above explanation, it is not difficult to understand how bezier curves are used from the perspective of the Work (Ban) process (Zhuan).
Android draws bezier curves
Android comes with bezier curve drawing API, which can be drawn using quadTo and cubicTo methods of the Path class.
// Build a path
path.reset();
path.moveTo(p0x, p0y);
// Draw the second-order Bezier curve
path.quadTo(p1x, p1y, p2x, p2y);
path.moveTo(p0x, p0y);
path.close();
// The final draw operation
canvas.drawPath(path, paint);
Copy the code
In fact, the drawing here is the bezier curve calculation equation handed over to the Android system to complete, the parameter transfer only transfer starting point and control point.
We can calculate this equation in our own code to gain more control over the logic, that is, divide the curve into many points, if the point size is larger, we can even reduce the number of points to achieve the same effect, for the purpose of rendering optimization.
OpenGL rendering
Through OpenGL we can achieve the above scheme, the curve is divided into multiple points. This scheme requires us to calculate the Bezier curve equation on the CPU, according to each value of t, calculate a Bezier point, with OpenGL to draw this point.
So this point can be drawn using the form of Triangles, GL_TRIANGLES, using OpenGL, so that we can use textures for this point, but there are a few cranners, and the starting points and control points are dynamically variable at run time so it’s harder to do than say they’re constant.
Here we first introduce another scheme, which is relatively simple to implement and can achieve optimization effect. We can give the calculation equation of Bessel curve to GPU and complete it in OpenGL Shader.
In this way, we give the starting point and the control point, and the process of calculating bezier curves to fill in the points is left to Shader.
In addition, by controlling the number of t, we can control the density of Bezier point filling.
The larger t is, the more points will be filled. When the value exceeds a certain threshold, the drawing effect will not be improved, but the performance will be affected.
The smaller the t is, the smaller the Bezier curve is reduced to a bunch of points. Therefore, the value range of T can also play an optimization role in drawing.
The drawing effect is as follows:
The following is the actual code part, about the basic theory of OpenGL can refer to the previous article and public number, not elaborated.
Define a function in Shader that implements Bessel’s equation:
vec2 fun(in vec2 p0, in vec2 p1, in vec2 p2, in vec2 p3, in float t){
float tt = (1.0 - t) * (1.0 -t);
return tt * (1.0 -t) *p0
+ 3.0 * t * tt * p1
+ 3.0 * t *t *(1.0 -t) *p2
+ t *t *t *p3;
}
Copy the code
This equation can be optimized for a wave using the built-in function in the Shader:
vec2 fun2(in vec2 p0, in vec2 p1, in vec2 p2, in vec2 p3, in float t)
{
vec2 q0 = mix(p0, p1, t);
vec2 q1 = mix(p1, p2, t);
vec2 q2 = mix(p2, p3, t);
vec2 r0 = mix(q0, q1, t);
vec2 r1 = mix(q1, q2, t);
return mix(r0, r1, t);
}
Copy the code
Next comes the specific vertex shader:
// Corresponding t data transfer
attribute float aData;
// Corresponding to the start and end points
uniform vec4 uStartEndData;
// Corresponding control point
uniform vec4 uControlData;
/ / the MVP matrix
uniform mat4 u_MVPMatrix;
void main(a) {
vec4 pos;
pos.w = 1.0;
// Select start point, end point, control point
vec2 p0 = uStartEndData.xy;
vec2 p3 = uStartEndData.zw;
vec2 p1 = uControlData.xy;
vec2 p2 = uControlData.zw;
// Retrieve t
float t = aData;
// Calculate the function call of the Bezier point
vec2 point = fun2(p0, p1, p2, p3, t);
// Define the x and y coordinates of a point
pos.xy = point;
// The position to draw
gl_Position = u_MVPMatrix * pos;
// Define the size of the point
gl_PointSize = 20.0;
}
Copy the code
UStartEndData in the code corresponds to the start and end points, and uControlData corresponds to two control points.
The data transfer of these two variables is done using the glUniform4f method:
mStartEndHandle = glGetUniformLocation(mProgram, "uStartEndData");
mControlHandle = glGetUniformLocation(mProgram, "uControlData");
// Pass data as a fixed value
glUniform4f(mStartEndHandle,
mStartEndPoints[0],
mStartEndPoints[1],
mStartEndPoints[2],
mStartEndPoints[3]);
glUniform4f(mControlHandle,
mControlPoints[0],
mControlPoints[1],
mControlPoints[2],
mControlPoints[3]);
Copy the code
Another important variable is the aData variable, which corresponds to the number of partitions of t between 0 and 1.
private float[] genTData() {
float[] tData = new float[Const.NUM_POINTS];
for (int i = 0; i < tData.length; i ++) {
float t = (float) i / (float) tData.length;
tData[i] = t;
}
return tData;
}
Copy the code
The above function divides t into conconst.NUM_POINTS sections between 0 and 1. The values of each section are stored in the tData array and passed to Shader via glVertexAttribPointer.
In the end, when we actually draw, we’ll just use GL_POINTS.
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, Const.NUM_POINTS );
Copy the code
So that’s how OpenGL draws bezier curves.
The specific code part can refer to my project:
Github.com/glumes/Andr…
In The reference, there is also an example of Using OpenGL to draw A Bessel curve, but using A Bessel curve plane, using the form of GL_TRIANGLES, and the construction of the tData array is a little different, but they are all very similar, so you can understand the reference article if you use this example.
For OpenGL related articles, please refer to the content of the public account I wrote before:
-
OpenGL series – basic drawing process
-
OpenGL learning series – basic shape drawing
-
OpenGL learning series – Coordinate system
-
OpenGL Learning series – Projection matrix
-
OpenGL Learning series – Texture
-
OpenGL Learning series – Observation matrix
-
OpenGL ES Learning resource sharing
-
Replace the video frame content with OpenGL
Welcome to wechat public account, timely push more graphics, images, multimedia related articles ~~
reference
- Yalantis.com/blog/how-we…
Scan code attention, read more wonderful ~~~