OpenGl中的Nurbs B樣條曲面繪制


NURBS

貝塞爾曲線的缺點是當我們增加很多控制點的時候,曲線變得不可控,其連續性會變差差。如果控制點很多(高階曲線),當我們調整一個控制點的位置,對 整個曲線的影響是很大的。要獲得更高級的控制,可以使用GLU庫提供的NURBS(非均勻有理B樣條)。通過這些函數我們可以在求值器中調整控制點的影響 力,在有大量控制點的情況下,依然可以產生平滑的曲線。

從貝塞爾到B樣條

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

  image

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

結點

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

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

image

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

以下內容及節選自 http://www.rhino3d.com/cn/nurbs

節點 ( 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 )。在上面的例子中,節點值 0、2、9 有完整的重數,只出現一次的節點值稱為單純節點 ( 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;最后四個控制點是對應至最后六個節點

創建NURBS表面

GLU庫中提供了易用高級的繪制NURBS表面的函數。我們不需要顯示地調用求值函數或建立網格。渲染一個NURBS表面的步驟如下:

  1. 創建一個NURBS渲染對象 gluNewNurbsRenderer()
  2. 調用相關的NURBS函數來修改曲線或曲面的外觀 gluNurbsProperty
  3. 定義表面,渲染NURBS gluBeginSurface gluNurbsSurface gluEndSurface
  4. 銷毀NURBS渲染對象 gluDeleteNurbsRenderer();

我們通過gluNewNurbsRenderer函數來為NURBS創建一個渲染器對象,在最后使用gluDeleteNurbsRenderer銷毀它。代碼如下:

// NURBS 對象指針 GLUnurbsObj * pNurb = NULL; ... ... // 創建NURBS對象 pNurb = gluNewNurbsRenderer(); ... if (pNurb) gluDeleteNurbsRenderer(pNurb);

NURBS屬性

在創建了NURBS渲染器之后,我們需要設置NURBS的屬性。

//設置采樣容差

gluNurbsProperty(pNurb, GLU_SAMPLING_TOLERANCE, 25.0f);

//填充一個實體的表面

gluNurbsProperty(pNurb, GLU_DISPLAY_MODE, (GLfloat)GLU_FILL);

GLU_SAMPLING_TOLERANCE決定了網格的精細程度。GLU_FILL表示使用填充模式,相應的GLU_OUTLINE_POLYGON是線框模式。

定義表面

表面通過一組控制點和一個結點序列來定義。使用gluNurbsSurface函數來定義表面,這個函數要在gluBeginSurface和gluEndSurface中間:

// 渲染NURB // 開始NURB表面的定義 gluBeginSurface(pNurb); gluNurbsSurface(pNurb, // 指針指向NURBS渲染器 8 , Knots, // u定義域內的結點個數,以及結點序列 8 , Knots, // v定義域內的結點個數,以及結點序列 4 * 3 , // u方向上控制點的間隔 3 , // v方向上控制點的間隔 & ctrlPoints[ 0 ][ 0 ][ 0 ], // 控制點數組 4 , 4 , // u,v 的次數 GL_MAP2_VERTEX_3); // 表面的類型 // 完成定義 gluEndSurface(pNurb);
 
程序如下:
 1     float ctrlpoints[4][4][3]={   
 2         {{100.0,270.0,0.0},//p00   
 3         {105.0,180.0,0.0},//p01   
 4         {110.0,160.0,0.0},//p02   
 5         {155.0,100.0,0.0}},//p03   
 6         {{180.0,200.0,0.0},//p10   
 7         {190.0,130.0,0.0},//p11   
 8         {200.0,110.0,0.0},//p12   
 9         {240.0,70.0,0.0}},//p13   
10         {{310.0,200.0,0.0},//p20   
11         {320.0,130.0,0.0},//p21   
12         {330.0,110.0,0.0},//p22   
13         {370.0,70.0,0.0}},//p23   
14         {{420.0,270.0,0.0},//p30   
15         {430.0,180.0,0.0},//p31   
16         {440.0,160.0,0.0},//p32   
17         {490.0,120.0,1.0}}//p33   
18     };  
19 
20     glPushMatrix();
21     //繪制控制點與控制線
22     glScaled(0.2, 0.2, 0.2);
23 
24     glPointSize(4.0f); 
25     glColor3f(0.0, 0.0, 1.0);
26     glColor3f(0, 0, 1);
27     glBegin(GL_POINTS);
28     for (int i = 0; i < 4; i++)
29     {
30         for (int j = 0; j < 4; j++)
31             glVertex3fv(ctrlpoints[i][j]);
32 
33     }
34     glEnd();
35     //繪制控制線
36     glLineWidth(1.5f);
37     glColor3f(0.0,1.0,1.0); 
38     for (int i = 0; i < 4; i++)
39     {
40         glBegin(GL_LINE_STRIP);
41         for (int j = 0; j < 4; j++)
42             glVertex3fv(ctrlpoints[i][j]);
43         glEnd();
44 
45         glBegin(GL_LINE_STRIP);
46         for (int j = 0; j < 4; j++)
47             glVertex3fv(ctrlpoints[j][i]);
48         glEnd();
49     }
50     //繪制B樣條控制曲面
51      GLfloat knots[8]={0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0}; //B樣條控制向量
52     glLineWidth(1.0f);
53     glColor3f(0.0, 0.0, 0.0);
54     
55     gluNurbsProperty(pNurb,GLU_SAMPLING_TOLERANCE,25.0); //設置屬性
56     gluNurbsProperty(pNurb,GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON); 
57     gluBeginSurface(pNurb);//開始繪制
58     gluNurbsSurface(pNurb, 
59         8,knots, 
60         8,knots, 
61         4*3, 
62         3, 
63         &ctrlpoints[0][0][0], 
64         4, 4, 
65         GL_MAP2_VERTEX_3);
66 
67     gluEndSurface(pNurb); //結束繪制
68 
69 for (int j = 0; j <= 8; j++) 70 { 71 glBegin(GL_LINE_STRIP); 72 for (int i = 0; i <= 30; i++) 73 glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0); 74 glEnd(); 75 glBegin(GL_LINE_STRIP); 76 for (int i = 0; i <= 30; i++) 77 glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0); 78 glEnd(); 79 } 80 glPopMatrix();

  程序運行效果如下:

  


免責聲明!

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



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