用OpenGL進行曲線、曲面的繪制


實驗目的

  • 理解Bezier曲線、曲面繪制的基本原理;理解OpenGL中一維、二維插值求值器的用法。
  • 掌握OpenGL中曲線、曲面繪圖的方法,對比不同參數下的繪圖效果差異;

 

代碼1:用四個控制點繪制一條三次Bezier曲線

Github地址

#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
#include <GL/glut.h>

//4個控制點的3D坐標——z坐標全為0
GLfloat ctrlpoints[4][3] = {
    { -4, -4, 0 }, { -2, 4, 0 }, { 2, -4, 0 }, { 4, 4, 0 }
};

void init(void)
{
    //背景色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    //將控制點坐標映射為曲線坐標
    //參數1:GL_MAP1_VERTEX_3,3維點坐標
    //參數2和3:控制參數t或u的取值范圍[0, 1]
    //參數4:曲線內插值點間的步長3————3維坐標
    //參數5:曲線間的補償為頂點數4個————總步長為12
    //參數6:控制點二維數組首元素地址
    //注意: 若是在這里設置了相關參數,后續對ctrlpoints內容更改曲線不變
    glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
    //打開開關——允許3維坐標控制點到參數點轉換開關
    glEnable(GL_MAP1_VERTEX_3);
    glShadeModel(GL_FLAT);

    //代碼開關2:去掉本注釋,可啟用反走樣
    /*
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);  //允許直線反走樣
    glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);  // Antialias the lines
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    */
}

void display(void)
{
    int i;
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);

    //代碼開關1:去掉本注釋,查看動態的曲線繪圖效果:動態更新控制點坐標
    /*
    for(int t = 0; t < 4; t++) {
    for(int j = 0; j < 3; j++)
    ctrlpoints[t][j] = (rand() % 1024 / 1024.0 - 0.5) * 10;
    }
    //動態映射
    glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &ctrlpoints[0][0]);
    */
    glLoadIdentity();
    glColor3f(1.0, 0.0, 0.0);
    //繪制連續線段
    glBegin(GL_LINE_STRIP);
    //參數t或u取值為i/30,共計31個點
    for (i = 0; i <= 30; i++)
        glEvalCoord1f((GLfloat)i / 30.0);   //根據4個控制點坐標的參數化插值
    glEnd();
    /* 顯示控制點 */
    glPointSize(5.0);
    glBegin(GL_POINTS);
    for (i = 0; i < 4; i++)
        glVertex3fv(&ctrlpoints[i][0]);
    glEnd();

    glTranslatef(-0.1f, 0.1f, 0.0f);
    glColor3f(0.0, 1.0, 0.0);
    //glLineWidth(2.0);
    //繪制連續線段——線段數越多,曲線越光滑
    glBegin(GL_LINE_STRIP);
    //設置參數t或u取值為i/60,共計61個點
    //實驗:若讓t從-2變化到+2,可看到什么效果
    for (i = 0; i <= 60; i++)
        glEvalCoord1f((GLfloat)i / 60.0);  //根據4個控制點坐標的參數化插值
    glEnd();

    glTranslatef(-0.1f, 0.1f, 0.0f);
    glColor3f(1.0, 1.0, 1.0);
    //繪制連續線段
    glBegin(GL_LINE_STRIP);
    //設置參數t或u取值為i/60,共計61個點
    //實驗:若讓t從-2變化到+2,可看到什么效果
    for (i = 0; i <= 100; i++)
        glEvalCoord1f((GLfloat)i / 100.0);
    glEnd();

    glutSwapBuffers();
}

//3D空間中繪制2D效果,采用正交投影
void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0);
    else
        glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
    case 'X':
    case 27:   //ESC鍵
        exit(0);
        break;
    default:
        break;
    }
}

int main(int argc, char** argv)
{
    srand((unsigned int)time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//使用雙緩存模式和深度緩存
    glutInitWindowSize(800, 800);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("2D Bezier曲線");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutIdleFunc(display);//設置空閑時調用的函數
    glutMainLoop();
    return 0;
}

 

此時我們打開代碼開關1,查看動態Bezier曲線繪制效果:

 

 

 

 

 

關閉代碼開關1,打開代碼開關2,查看直線反走樣效果:

 

 對比剛開始的效果圖,我們發現,使用了直線反走樣后,繪制出的曲線很光滑,看着很舒服。

 

代碼2:4*4個控制點繪制一個三次Bezier曲面線框模型

 Github地址

#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
#include <GL/glut.h>

/* 控制點的坐標 */
GLfloat ctrlpoints[4][4][3] = {
    { { -1.5, -1.5, 2.0 },
    { -0.5, -1.5, 2.0 },
    { 0.5, -1.5, -1.0 },
    { 1.5, -1.5, 2.0 }
    },

    { { -1.5, -0.5, 1.0 },
    { -0.5, 1.5, 2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, -0.5, -1.0 } },

    { { -1.5, 0.5, 2.0 },
    { -0.5, 0.5, 1.0 },
    { 0.5, 0.5, 3.0 },
    { 1.5, -1.5, 1.5 } },

    { { -1.5, 1.5, -2.0 },
    { -0.5, 1.5, -2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, 1.5, -1.0 } } };

void init(void)
{
    //背景色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    //將控制點坐標映射為曲面坐標
    //參數1:GL_MAP1_VERTEX_3,3維點坐標
    //參數2和3:控制參數u的取值范圍[0, 1]
    //參數4:x方向元素間的步長為3個GLfloat
    //參數5:x方向曲線間的步長為4個控制點——曲線由4個控制點確定
    //參數6-7:控制參數v的取值范圍[0, 1]
    //參數8:y方向元素間的步長為12個GLfloat元素
    //參數9:y方向每條曲線的控制點數量為4
    //注意: 若是在這里設置了相關參數,后續對ctrlpoints內容更改曲線不變
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
    //允許二維映射
    glEnable(GL_MAP2_VERTEX_3);
    //二維映射:x、y方向U和V的參數[0, 1],且中間插值數量為各20個
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    //允許深度測試
    glEnable(GL_DEPTH_TEST);

    //代碼開關2:啟用反走樣
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);  // Antialias the lines
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 1.0, 0.0);
    glPushMatrix();
    //代碼開關1:去掉注釋查看效果;更改旋轉角度參數,查看效果
    //glRotatef(0.1, 1.0, 1.0, 1.0);
    int i, j;
    //生成2D網格坐標,以從控制點參數插值確定網格點所對應的點集所對應的坐標
    for (j = 0; j <= 8; j++)     {
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 30; i++)
            glEvalCoord2f((GLfloat)i / 30.0, (GLfloat)j / 8.0);  //固定y坐標時x方向的網格坐標
        glEnd();

        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 30; i++)
            glEvalCoord2f((GLfloat)j / 8.0, (GLfloat)i / 30.0);  //固定x坐標時y方向的網格坐標     
        glEnd();
    }

    //查看網格所確定的插值點(u, v)的位置
    glColor3f(1, 0, 0);
    glBegin(GL_POINTS);
    for (j = 0; j <= 8; j++)     {
        for (i = 0; i <= 30; i++)
            glVertex3f((GLfloat)i / 30.0, (GLfloat)j / 8.0, 0);
        for (i = 0; i <= 30; i++)
            glVertex3f((GLfloat)j / 8.0, (GLfloat)i / 30.0, 0);
    }
    glEnd();
    glPopMatrix();
    glutSwapBuffers();
}

void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0);
    else
        glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
    case 'X':
    case 27:   //ESC鍵
        exit(0);
        break;
    default:
        break;
    }
}

int main(int argc, char** argv)
{
    srand((unsigned int)time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//使用雙緩存模式和深度緩存
    glutInitWindowSize(800, 800);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Bezier曲面線框模型");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutIdleFunc(display);//設置空閑時調用的函數
    glutMainLoop();
    return 0;
}

 

打開代碼開關1后的效果:

 

代碼3:用4*4個控制點繪制一個三次Bezier曲面並添加光照效果

Github地址

#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
#include <GL/glut.h>
/* 控制點的坐標 */
GLfloat ctrlpoints[4][4][3] = {
    { { -1.5, -1.5, 2.0 },
    { -0.5, -1.5, 2.0 },
    { 0.5, -1.5, -1.0 },
    { 1.5, -1.5, 2.0 } },

    { { -1.5, -0.5, 1.0 },
    { -0.5, 1.5, 2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, -0.5, -1.0 } },

    { { -1.5, 0.5, 2.0 },
    { -0.5, 0.5, 1.0 },
    { 0.5, 0.5, 3.0 },
    { 1.5, -1.5, 1.5 } },

    { { -1.5, 1.5, -2.0 },
    { -0.5, 1.5, -2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, 1.5, -1.0 } } };

void init(void)
{
    //背景色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    //將控制點坐標映射為曲面坐標
    //參數1:GL_MAP1_VERTEX_3,3維點坐標
    //參數2和3:控制參數u的取值范圍[0, 1]
    //參數4:x方向元素間的步長為3個GLfloat
    //參數5:x方向曲線間的步長為4個控制點——曲線由4個控制點確定
    //參數6-7:控制參數v的取值范圍[0, 1]
    //參數8:y方向元素間的步長為12個GLfloat元素
    //參數9:y方向每條曲線的控制點數量為4
    //note: 若是在這里設置了相關參數,后續對ctrlpoints內容更改曲線不變
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
    //允許二維映射
    glEnable(GL_MAP2_VERTEX_3);
    //二維映射:x、y方向U和V的參數[0, 1],且中間插值數量為各20個
    glMapGrid2f(20, 0.0, 1.0, 20, 0.0, 1.0);
    //允許深度測試
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    //代碼開關4:取消下面兩行代碼,查看曲面顯示效果差異
    //打開自動法矢量開關
    //glEnable(GL_AUTO_NORMAL);
    //允許正則化法矢量
    //glEnable(GL_NORMALIZE);

    //代碼開關3:設置材質與光源
    GLfloat ambient[] = { 0.4, 0.6, 0.2, 1.0 };
    GLfloat position[] = { 0.0, 1.0, 3.0, 1.0 };
    GLfloat mat_diffuse[] = { 0.2, 0.4, 0.8, 1.0 };
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 80.0 };
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 1.0, 0.0);
    //如果不希望旋轉,則啟用push和pop矩陣命令,並注釋掉glRotatef行
    //glPushMatrix();
    //代碼開關1:去掉注釋查看效果;更改旋轉角度參數,查看效果
    glRotatef(1.0, 1.0, 1.0, 1.0);
    glEvalMesh2(GL_FILL, 0, 20, 0, 20);
    //glPopMatrix();
    glutSwapBuffers();
}

void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0);
    else
        glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
    case 'X':
    case 27:   //ESC鍵
        exit(0);
        break;
    default:
        break;
    }
}

int main(int argc, char** argv)
{
    srand((unsigned int)time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//使用雙緩存模式和深度緩存
    glutInitWindowSize(800, 800);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Bezier曲面");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutIdleFunc(display);//設置空閑時調用的函數
    glutMainLoop();
    return 0;
}

 

 

 

說明

從貝塞爾到B樣條

  貝塞爾曲線由起點、終點和其他控制點來影響曲線的形狀。在二次貝塞爾曲線和三次貝塞爾曲線中,可以通過調整控制點的位置而得到很好的平滑性(C2級連續性 曲率級)的曲線。當增加更多的控制點的時候,這種平滑性就被破壞了。如下圖所示,前兩個曲線很平滑(曲率級的連續性),第三個曲線在增加了一個控制點之后,曲線被拉伸了,其平滑性遭到了破壞。

  B樣條的工作方式類似於貝塞爾曲線,但不同的是曲線被分成很多段。每段曲線的形狀只受到最近的四個控制點的影響,這樣曲線就像是4階的貝塞爾曲線拼接起來的。這樣很長的有很多控制點的曲線就會有固定的連續性,平滑性(每一段都是c2級的連續性)。

 

結點

  NURBS(非均勻有理B樣條)的真正威力在於,可以調整任意一段曲線中的四個控制點的影響力,來產生較好的平滑性。這是通過一系列結點來控制的。每個控制點都定義了兩個結點的值。結點的取值范圍是uv的定義域,而且必須是非遞減的。

  結點的值決定了落在uv參數定義域內的控制點的影響力。下圖的曲線表示控制點對一條在u參數定義域內的具有四個單位的曲線的影響。下圖表示中間點對曲線的影響更大,而且只有在[0,3]范圍內的控制點才會對曲線產生影響。

 在uv參數定義域內的控制點對曲線的形狀會有有影響,而且我們可以通過結點來控制控制點的影響力。非均勻性就是指一個控制點的影響力的范圍是可以改變的。

    節點 ( Knot ) 是一個 ( 階數 + N - 1 ) 的數字列表,N 代表控制點數目。有時候這個列表上的數字也稱為節點矢量 ( Knot Vector ),這里的矢量並不是指 3D 方向。

    節點列表上的數字必須符合幾個條件,確定條件是否符合的標准方式是在列表序列中,數字必需維持不變或變大,而且數字重復的次數不可以比階數大。例如,階數 3 15 個控制點的 NURBS 曲線,列表數字為 0,0,0,1,2,2,2,3,7,7,9,9,9 是一個符合條件的節點列表。列表數字為 0,0,0,1,2,2,2,2,7,7,9,9,9 則不符合,因為此列表中有四個 2,而四比階數大 ( 階數為 3 )

節點值重復的次數稱為節點的重數 ( Multiplicity ),在上面例子中符合條件的節點列表中,節點值 0 的重數值為三;節點值 1 的重數值為一;節點值 2 的重數為三;節點值 7 的重數值為二;節點值 9 的重數值為三。

如果節點值重復的次數和階數一樣,該節點值稱為全復節點 ( Full-Multiplicity Knot )。在上面的例子中,節點值 029 有完整的重數,只出現一次的節點值稱為單純節點 ( Simple Knot ),節點值 1 3 為單純節點。

    如果在節點列表中是以全復節點開始,接下來是單純節點,再以全復節點結束,而且節點值為等差,稱為均勻 ( Uniform )。例如,如果階數為 3 7 個控制點的 NURBS 曲線,其節點值為 0,0,0,1,2,3,4,4,4,那么該曲線有均勻的節點。如果節點值是 0,0,0,1,2,5,6,6,6 不是均勻的,稱為非均勻 ( Non-Uniform )。在 NURBS NU 代表非均勻,意味着在一條 NURBS 曲線中節點可以是非均勻的。

在節點值列表中段有重復節點值的 NURBS 曲線比較不平滑,最不平滑的情形是節點列表中段出現全復節點,代表曲線有銳角。因此,有些設計師喜歡在曲線插入或移除節點,然后調整控制點,使曲線的造型變得平滑或尖銳。因為節點數等於 ( N + 階數 - 1 )N 代表控制點的數量,所以插入一個節點會增加一個控制點,移除一個節點也會減少一個控制點。插入節點時可以不改變 NURBS 曲線的形狀,但通常移除節點必定會改變 NURBS 曲線的形狀。

節點(Knot)與控制點關系:控制點和節點是一對一成對的是常見的錯誤概念,這種情形只發生在 1 階的 NURBS ( 多重直線 )。較高階數的 NURBS 的每 ( 2 x 階數 ) 個節點是一個群組,每 ( 階數 + 1 ) 個控制點是一個群組。例如,一條 3 7 個控制點的 NURBS 曲線,節點是 0,0,0,1,2,5,8,8,8,前四個控制點是對應至前六個節點;第二至第五個控制點是對應至第二至第七個節點 0,0,1,2,5,8;第三至第六個控制點是對應至第三至第八個節點 0,1,2,5,8,8;最后四個控制點是對應至最后六個節點

重要:NURB曲面上的裁剪、細分、鑲嵌效果,查看網頁 https://my.oschina.net/sweetdark/blog/184313

 

代碼4:用4*4個控制點繪制一個NURBS曲面並添加光照效果

Github地址

#include "stdafx.h"
#include <stdlib.h>
#include <time.h>
#include <GL/glut.h>

/* 控制點的坐標 */
GLfloat ctrlpoints[4][4][3] = {
    { { -1.5, -1.5, 2.0 },
    { -0.5, -1.5, 2.0 },
    { 0.5, -1.5, -1.0 },
    { 1.5, -1.5, 2.0 } },

    { { -1.5, -0.5, 1.0 },
    { -0.5, 1.5, 2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, -0.5, -1.0 } },

    { { -1.5, 0.5, 2.0 },
    { -0.5, 0.5, 1.0 },
    { 0.5, 0.5, 3.0 },
    { 1.5, -1.5, 1.5 } },

    { { -1.5, 1.5, -2.0 },
    { -0.5, 1.5, -2.0 },
    { 0.5, 0.5, 1.0 },
    { 1.5, 1.5, -1.0 } } };

GLUnurbsObj *theNurb; // 指向一個NURBS曲面對象的指針

void init(void)
{
    //背景色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    //代碼開關3:設置材質與光源
    GLfloat ambient[] = { 0.4, 0.6, 0.2, 1.0 };
    GLfloat position[] = { 1.0, 1.0, 3.0, 1.0 };
    GLfloat mat_diffuse[] = { 0.8, 0.6, 0.3, 1.0 };
    GLfloat mat_specular[] = { 0.8, 0.6, 0.3, 1.0 };
    GLfloat mat_shininess[] = { 45.0 };

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

    //允許深度測試
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    //代碼開關4:取消下面兩行代碼,查看曲面顯示效果差異
    //打開自動法矢量開關
    glEnable(GL_AUTO_NORMAL);
    //允許正則化法矢量
    glEnable(GL_NORMALIZE);
    theNurb = gluNewNurbsRenderer(); // 創建一個NURBS曲面對象  
    //修改NURBS曲面對象的屬性——glu庫函數
    ////采樣sampling容錯torerance
    gluNurbsProperty(theNurb, GLU_SAMPLING_TOLERANCE, 5.0);
    gluNurbsProperty(theNurb, GLU_DISPLAY_MODE, GLU_FILL);
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 1.0, 0.0);
    //各控制點影響力參數設置
    GLfloat knots[8] = { 0.0, 0.0, 0.0, 0.0,
        1.0, 1.0, 1.0, 1.0 }; // NURBS曲面的控制向量      
    glRotatef(1.0, 0.7, -0.6, 1.0); // 旋轉變換  
    gluBeginSurface(theNurb); // 開始曲面繪制 
    //網絡查詢:參數GL_MAP2_VERTEX_3的作用?
    //
    gluNurbsSurface(theNurb, 8, knots, 8, knots, 4 * 3, 3, &ctrlpoints[0][0][0], 4, 4, GL_MAP2_VERTEX_3); // 定義曲面的數學模型,確定其形狀  
    gluEndSurface(theNurb); // 結束曲面繪制  
    glutSwapBuffers();
}

void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-5.0, 5.0, -5.0*(GLfloat)h / (GLfloat)w, 5.0*(GLfloat)h / (GLfloat)w, -5.0, 5.0);
    else
        glOrtho(-5.0*(GLfloat)w / (GLfloat)h, 5.0*(GLfloat)w / (GLfloat)h, -5.0, 5.0, -5.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
    case 'X':
    case 27:   //ESC鍵
        exit(0);
        break;
    default:
        break;
    }
}

int main(int argc, char** argv)
{
    srand((unsigned int)time(0));
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//使用雙緩存模式和深度緩存
    glutInitWindowSize(800, 800);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Bezier曲面");

    init();

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutIdleFunc(display);//設置空閑時調用的函數

    glutMainLoop();
    return 0;
}

 

 

 

寫在最后

此文是源自學校圖形學課程的實驗教學內容,我很喜歡這節內容,於是將其源代碼和原理整理了出來供感興趣的人一起學習探討。

圖形學的課程結束后,我的內心卻久久不能平靜,因為這次課程我似乎找到了我感興趣的方向——圖形學,它給我的感覺就像是技術和藝術的結合。

記得剛開始上圖形學課程,老師一直在推導公式,講解每一個算法中所蘊含的數學原理,使我不禁感覺在上一堂數學課,不過也正因為如此,我才逐漸體會到高等數學和線性代數的作用,為此更加激起了我學習數學的興趣。

我一直相信——學習和做事的本質是相通的:熟能生巧,勤能補拙,念念不忘,必有回響。^_^

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM