<Use simple linear interpolation to implement interesting curves and animations>
Tags: opengl, 3d, c, linux
1. What should I implement?
For better illustration, let's first take a look at what we want to achieve. Since it is the effect of a graph, naturally it is best to see the graph.
Clarify the problem.
1) curve to be drawn:
./BMP _line/sshot_1_bmp
2) animation to be implemented:
./BMP _line/animated.gif
2. Geometric Analysis
./Lerp.note.bmp
We can see that what we finally want to draw is a red curve, and the red curve is obviously composed
Some line segments are connected. To determine a line segment, it is almost impossible to find its two endpoints.
The question is already clear: how to find the endpoint? We can see that these endpoints are of various diagonal lines and crosslines.
Point of intersection. Therefore, this problem is transformed into: finding the point of intersection of two straight lines.
3. functions related to two straight lines
3.1 calculate the straight line after the known two points
The linear equation is y = k * x + B.
Two points (x1, y1) and (x2, y2) are known, and k and B are obtained:
Y1 = k * x1 + B \/k = (y2-y1)/(x2-x1)
| ==>|
Y2 = k * x2 + B/\ B = y1-k * x1
3.2 calculate the intersection of two straight lines
The linear equation is Y = K * x + B.
Two straight lines are known,
Y = K1 * x + B1
Y = k2 * x + b2
Calculate the intersection of them (x, y ):
Y = K1 * x + B1 \/x = (B2-B1 )/
(K1-K2)
| ==>|
Y = k2 * x + b2/\ y = K1 * x + B1
3.3 code implementation
/* * y = k*x + b * * y1 = k*x1 + b \ / k =(y2 - y1) / (x2 - x1) * | ==> | * y2 = k*x2 + b / \ b = y1 - k*x1 * * */int N3D_lineConstruct(float x1, float y1, float x2, float y2, float *k, float *b){ assert( !(x1 == x2 && y1 == y2) ); if ( x1 == x2 ) { return 0; } *k = (y2 - y1) / (x2 - x1); *b = y1 - (*k) * x1; return 1;}/* * y = k*x + b * * y = k1*x + b1 \ / x =(b2 -b1) / (k1 - k2) * | ==> | * y = k2*x + b2 / \ y = k1*x + b1 * * */int N3D_lineInsertPos(float k1, float b1, float k2, float b2, float *x, float *y){ if ( k1 == k2 ) { return 0; } *x = (b2 - b1) / (k1 - k2); *y = k1 * (*x) + b1; return 1;}
4. Start to implement what we want to implement
4.1 curves (static)
Typedef struct n3d_vertex {float FX; float FY; float FZ; float FS; float ft;} n3d_vertex; typedef struct n3d_godpos {float mftarx; // control axis (black solid line in the figure) the X coordinate float mftary of the bottom end point; // The Y coordinate float mftarw of the bottom end point of the control axis (black solid line in the figure); // The float mftarh of the control axis width; // control axis height int mnframexpend; // Number of frames in the second half of the animation int mndivy; // Number of frames in the first half of the animation n3d_vertex * mvvex; // float * mvhelpx of vertex data; // used for the second half of the secondary animation} n3d_godpos; n3d_godpos g_godpos = {0.0,-1.0, 0.0, 1.0, 10, 20, null, null ,};
First define and initialize a curve we want to implement: g_godpos!
void N3D_godUpdatePos(N3D_GodPos *god){ assert(god != NULL && god->mvVex != NULL && god->mnDivY > 0); int i; float fCcen = god->mfTarH / (float)god->mnDivY; float fEcen = 2.0 / (float)god->mnDivY; float fTexCen = 1.0 / (float)god->mnDivY; for ( i = 0; i <= god->mnDivY; i++ ) { N3D_godSTcalCurvePos(god, i, fCcen, fEcen, fTexCen); }}
Figure:./BMP _point/sshot_1_bmp
First, we implement a function used to calculate the vertex data of the final curve: n3d_godupdatepos, which is calculated by row,
Total (God-> mndivy + 1) rows. The final call is n3d_godstcalcurvepos,
static void N3D_godSTcalCurvePos(N3D_GodPos *god, int i, float fCcen, float fEcen, float fTexCen){ assert(god != NULL && god->mvVex != NULL && god->mnDivY > 0); assert(i >= 0 && i <= god->mnDivY); // Normal Rect: [(-1.0, 1.0), (1.0, -1.0)], size is 2.0 const float rectLTX = -1.0; const float rectLTY = 1.0; const float rectBRX = 1.0; const float rectBRY = -1.0; float Cx1 = god->mfTarX; float Cy1 = god->mfTarY + fCcen * i; float CxL = rectLTX; float CyL = rectLTY; float CxR = rectBRX; float CyR = rectLTY; float Ex1 = -1.0; float Ey1 = rectBRY + fEcen * i; float Ex2 = 0.0; float Ey2 = Ey1; float Ck, Cb, Ek, Eb, insPosXL, insPosYL, insPosXR, insPosYR; N3D_Vertex *pV = god->mvVex; if ( Cx1 == CxL ) CxL += 0.00001; N3D_lineConstruct(Cx1, Cy1, CxL, CyL, &Ck, &Cb); N3D_lineConstruct(Ex1, Ey1, Ex2, Ey2, &Ek, &Eb); if ( Ck == Ek ) Ek += 0.00001; N3D_lineInsertPos(Ck, Cb, Ek, Eb, &insPosXL, &insPosYL); Cx1 += god->mfTarW; if ( Cx1 == CxR ) CxR += 0.00001; N3D_lineConstruct(Cx1, Cy1, CxR, CyR, &Ck, &Cb); if ( Ck == Ek ) Ek += 0.00001; N3D_lineInsertPos(Ck, Cb, Ek, Eb, &insPosXR, &insPosYR); pV[2*i].fX = insPosXL; pV[2*i].fY = insPosYL; pV[2*i].fZ = 0.0; pV[2*i].fS = 0.0; pV[2*i].fT = fTexCen * i; pV[2*i+1].fX = insPosXR; pV[2*i+1].fY = insPosYR; pV[2*i+1].fZ = 0.0; pV[2*i+1].fS = 1.0; pV[2*i+1].fT = fTexCen * i;}
Calculate the intersection of two straight lines and save it to God-> mvvex.
The next step is rendering. With vertex data, rendering is very clear:
void N3D_godDraw(N3D_GodPos *god){ assert(god != NULL && god->mvVex != NULL && god->mvHelpX != NULL && god->mnDivY > 0); int i; N3D_Vertex *pV = god->mvVex; glBegin(GL_TRIANGLE_STRIP); { for ( i = 0; i <= god->mnDivY; i++ ) { glTexCoord2f(pV[2*i].fS, pV[2*i].fT); glVertex3f(pV[2*i].fX, pV[2*i].fY, pV[2*i].fZ); glTexCoord2f(pV[2*i+1].fS, pV[2*i+1].fT); glVertex3f(pV[2*i+1].fX, pV[2*i+1].fY, pV[2*i+1].fZ); } } glEnd();}
4.2 Animation
With our previous analysis and implementation, animation can be easily implemented. Let's take a look at the principle:
The entire process is divided into (God-> mndivy + 1) frame rendering, the first frame draws the first line, the second frame draws the first two
Line ,...
static void N3D_godSTdrawAminDemo(N3D_GodPos *god){ assert(god != NULL && god->mvVex != NULL && god->mvHelpX != NULL && god->mnDivY > 0 && god->mnFramExpend > 0); int j; N3D_godinit(&g_godPos, 0); for ( j = 0; j <= god->mnDivY + god->mnFramExpend; j++ ) { N3D_godClear(); glPushMatrix(); { glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPointSize(3); glColor4f(1.0, 1.0, 0.0, 1.0); glScalef(0.5, 0.5, 0.5); N3D_godDrawAminByLine(god, j); } glPopMatrix(); N3D_godFlush(); usleep(20 * 1000); }}void N3D_godDrawAminByLine(N3D_GodPos *god, int curLine){ assert(god != NULL && god->mvVex != NULL && god->mvHelpX != NULL && god->mnDivY > 0 && god->mnFramExpend > 0); assert(curLine >= 0 && curLine <= god->mnDivY + god->mnFramExpend); if ( curLine <= god->mnDivY ) { N3D_godUpdatePosByLine(god, curLine); } else { N3D_godSTupdateFramExpendPos(god, curLine); } N3D_godDrawByLine(god, curLine);}
5. Other Curves
The advantage of linear interpolation is that it can be drawn without the first known curve equation, and its computing workload is obviously not that
It's big.
N3d_godpos g_godpos = {0.0,-1.0, 0.3, 1.0, 10, 20, null}; // Tree Type
Figure:./animated-tree.gif
N3d_godpos g_godpos = {0.0,-0.4, 0.3, 1.0, 10, 20, null}; // crossover type
Figure:./animated-cross.gif
N3d_godpos g_godpos = {0.0,-1.0, 0.3,-3.0, 10, 20, null}; // Bowl Type
Figure:./animated-bowl.gif
N3D_GodPos g_godPos = {0.0,-0.1, 0.1,-3.0, 10, 20, NULL}; // wine cup type
Figure:./animated-glass.gif