用簡單線性插值實現有趣的曲線與動畫

來源:互聯網
上載者:User

                <<用簡單線性插值實現有趣的曲線與動畫>>
                Tags: opengl, 3d, c, linux

1. 要實現什麼?

    為了更好說明, 先看一下我們要實現的是什麼, 既然是圖形的效效果, 自然看圖最能
    說明問題, 清楚明了.

    1)  要畫出的曲線:

        ./bmp_line/sshot_20.bmp

                   


    2)  要實現的動畫:

        ./bmp_line/animated.gif

                    


2. 幾何分析

    ./lerp.note.bmp

             

    我們可以看到, 我們最終要畫出的是紅色的曲線, 而該紅色曲線是顯然是由一
    些線段串連而成的. 要確定一條線段不外乎於找出其兩個端點. 好了, 我們現在的問
    題已經很明確了: 找端點. 怎麼找呢? 聯絡, 可知這些端點是各個斜線與橫線的
    交點. 所以這一問題被轉化為: 求兩直線的交點.

3. 兩個直線相關的函數

3.1 已知兩點 求經過這兩點的直線

    直線方程是:     y = k*x + b

    現已知兩點(x1, y1)和(x2, y2), 求k和b:

        y1 = k*x1 + b \       / k = (y2 - y1) /(x2 - x1)
                       | ==> |
        y2 = k*x2 + b /       \ b = y1 - k*x1

3.2 求兩直線的交點

    直線方程是:     y = k*x + b

    現已知兩條直線,
        y = k1*x + b1
        y = k2*x + b2
    求它們的交點(x, y):

        y = k1*x + b1 \      / x = (b2 -b1) /
(k1 - k2)
                       | ==> |
        y = k2*x + b2 /      \ y = k1*x + b1

3.3 代碼實現

/* * 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. 開始實現我們要實現的東東

4.1 曲線(靜態)

typedef struct N3D_Vertex{    float fX;    float fY;    float fZ;    float fS;    float fT;} N3D_Vertex;typedef struct N3D_GodPos{    float mfTarX;       // 控制軸(圖中的黑實線)的底端端點的x座標    float mfTarY;       // 控制軸(圖中的黑實線)的底端端點的y座標    float mfTarW;       // 控制軸寬    float mfTarH;       // 控制軸高    int   mnFramExpend; // 動畫後半部分的幀數    int   mnDivY;       // 動畫前半部分的幀數    N3D_Vertex  *mvVex;     // 頂點資料    float       *mvHelpX;   // 用於輔助動畫的後半部分} N3D_GodPos;N3D_GodPos      g_godPos = {    0.0,    -1.0,    0.0,    1.0,    10,    20,    NULL,    NULL,};

先定義並初始化一個我們要實現的曲線: 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);    }}

    圖: ./bmp_point/sshot_20.bmp

         

我們先實現一個用於求畫出最終曲線的頂點資料的一個函數: N3D_godUpdatePos,按行求,
共(god->mnDivY + 1)行, 其最終調用的是 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;}

用求兩直線的交點的方法求出交點並儲存到god->mvVex中.

接下來, 就是渲染了, 有了頂點資料, 渲染就是很明了的事:

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 動畫

有了我們前面的分析與實現, 動畫也就很容易地實現了. 先看一下原理:

    對整個過程共分(god->mnDivY+1)幀渲染, 第一幀畫最前面一行, 第二幀畫最前面兩
    行, ...

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. 其它曲線

線性插值的好處就是, 不用Crowdsourced Security Testing道曲線的方程, 就能畫出來, 並且其計算量也明顯沒有那
麼大.

N3D_GodPos g_godPos = {  0.0, -1.0,  0.3,  1.0,   10, 20, NULL};  // 樹型

    圖: ./animated-tree.gif

        


N3D_GodPos g_godPos = {  0.0, -0.4,  0.3,  1.0,   10, 20, NULL};  // 交叉型

    圖: ./animated-cross.gif

         


N3D_GodPos g_godPos = {  0.0, -1.0,  0.3, -3.0,   10, 20, NULL};  // 碗型

    圖: ./animated-bowl.gif

        


N3D_GodPos g_godPos = {  0.0, -0.1,  0.1, -3.0,   10, 20, NULL};  // 酒杯型

    圖: ./animated-glass.gif

               


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.