如果想理解貝塞爾曲面沒有對其數學基本的認識是很難的,如果你不願意讀這一部分或者你已經知道了關於她的數學知識你可以跳過。首先我會描述貝塞爾曲線再介紹生成貝塞爾曲面。
奇怪的是,如果你用過乙個圖形程式,你就已經熟悉了貝塞爾曲線,也許你接觸的是另外的名稱。它們是畫曲線的最基本的方法,而且通常被表示成一系列點,其中有兩個點與兩端點表示左右兩端的切線。下圖展示了乙個例子。
這是最基礎的貝塞爾曲線(長點的由很多點在一起(多到你都沒發現))。這個曲線由4個點定義,有2個端點和2個中間控制點。對計算機而言這些點都是一樣 的,但是特意的我們通常把前後兩對點分別連線,因為他們的連線與短點相切。曲線是乙個引數化曲線,畫的時候從曲線上平均找幾點連線。這樣你可以控制曲線曲 面的精度(和計算量)。最通常的方法是遠距離少細分近距離多細分,對視點,看上去總是很完好的曲面而對速度的影響總是最小。
貝塞爾曲面基於乙個基本方程,其他複雜的都是基於此。方程為:
t + (1 - t) = 1
看起來很簡單不是?的確是的,這是最基本的貝塞爾曲線,乙個一維的曲線。你也許從術語中猜到,貝塞爾曲線是多項式形式的。從線性代數知,乙個一維的多項式 是一條直線,沒多大意思。好,因為基本方程對所有t都成立,我們可以平方,立方兩邊,怎麼都行,等式都是成立的,對吧?好,我們試試立方。
(t + (1-t))^3 = 1^3
t^3 + 3*t^2*(1-t) + 3*t*(1-t)^2 + (1-t)^3 = 1
這是我們最常用的計算貝塞爾曲面的方程,a)她是最低維的不需要在乙個平面內的多項式(有4個控制點),而且b)兩邊的切線互相沒有聯絡(對於2維的只有3個控制點)。那麼你看到了貝塞爾曲線了嗎?呵呵,我們都沒有,因為我還要加乙個東西。
好,因為方程左邊等於1,可以肯定如果你把所有項加起來還是等於1。這是否意味著在計算曲線上一點時可以以此決定該用每個控制點的多少呢?(答案是肯定 的)你對了!當我們要計算曲線上一點的值我們只需要用控制點(表示為向量)乘以每部分再加起來。基本上我們要用0<=t<=1,但不是必要 的。不明白了把?這裡有函式:
p1*t^3 + p2*3*t^2*(1-t) + p3*3*t*(1-t)^2 + p4*(1-t)^3 = pnew
因為多項式是連續的,有乙個很好的辦法在4個點間插值。曲線僅經過p1,p4,分別當t=1,0。
好,一切都很好,但現在我怎麼把這個用在3d裡呢?其實很簡單,為了做乙個貝塞爾曲面,你需要16個控制點,(4*4),和2個變數t,v。你要做的是計 算在分量v的沿4條平行曲線的點,再用這4個點計算在分量t的點。計算了足夠的這些點,我們可以用三角帶連線他們,畫出貝塞爾曲面。
恩,我認為現在已經有足夠的數學背景了,看**把!
#include // 數學庫
#include // 標準輸入輸出庫
#include // 標準庫
typedef struct point_3d point_3d;
typedef struct bpatch bezier_patch;
bezier_patch mybezier; // 建立乙個貝塞爾曲面結構
bool showcpoints=true; // 是否顯示控制點
int divs = 7; // 細分精度,控制曲面的顯示精度
以下是一些簡單的向量數學的函式。如果你是c++愛好者你可以用乙個頂點類(保證其為3d的)。
// 兩個向量相加,p=p+q
point_3d pointadd(point_3d p, point_3d q)
// 向量和標量相乘p=c*p
point_3d pointtimes(double c, point_3d p)
// 建立乙個3d向量
point_3d makepoint(double a, double b, double c)
這基本上是用c寫的3維的基本函式,她用變數u和4個頂點的陣列計算曲線上點。每次給u加上一定值,從0到1,我們可得乙個很好的近似曲線。
求值器基於bernstein多項式定義曲線,定義p(u ')為:
p(u')=∑bni(u')ri
這裡ri為控制點
bni(u')=[ni]u'i(1-u')n-i
且00=1,[n0]=1
u'=(u-u1)/(u2-u1)
當為貝塞爾曲線時,控制點為4,相應的4個bernstein多項式為:
1、b30 =(1-u)3
2、b31 =3u(1-u)2
3、b32 =3u2(1-u)
4、b33 =u3
// 計算貝塞爾方程的值
// 變數u的範圍在0-1之間
point_3d bernstein(float u, point_3d *p)
這個函式完成共享工作,生成所有三角帶,儲存在display list。我們這樣就不需要每貞都重新計算曲面,除了當其改變時。另外,你可能想用乙個很酷的效果,用morphing教程改變控制點位置。這可以做乙個 很光滑,有機的,morphing效果,只要一點點開銷(你只要改變16個點,但要從新計算)。「最後」的陣列元素用來儲存前一行點,(因為三角帶需要兩 行)。而且,紋理座標由表示百分比的u,v來計算(平面對映)。
還有乙個我們沒做的是計算法向量做光照。到了這一步,你基本上有2種選擇。第一是找每個三角形的中心計算x,y軸的切線,再做叉積得到垂直與兩向量的向 量,再歸一化,得到法向量。或者(恩,這是更好的方法)你可以直接用三角形的法矢(用你最喜歡的方法計算)得到乙個近似值。我喜歡後者;我認為不值得為了 一點點真實感影響速度。
// 生成貝塞爾曲面的顯示列表
gluint genbezier(bezier_patch patch, int divs)
glnewlist(drawlist, gl_compile); // 建立乙個新的顯示列表
glbindtexture(gl_texture_2d, patch.texture); // 邦定紋理
for (u=1;u<=divs;u++)
glend(); // 結束三角形帶的繪製
}glendlist(); // 顯示列表繪製結束
free(last); // 釋放分配的記憶體
return drawlist; // 返回建立的顯示列表
}這裡我們呼叫乙個我認為有一些很酷的值的矩陣。
void initbezier(void)
這是乙個優化的調位圖的函式。可以很簡單的把他們放進乙個簡單迴圈裡調一組。
// 載入乙個*.bmp檔案,並轉化為紋理
bool loadgltexture(gluint *texpntr, char* name)
if (textureimage != null)
if (textureimage->data)
free(textureimage->data);
return success;
}僅僅加了曲面初始化在這。你每次建乙個曲面時都會用這個。再一次,這裡是乙個用c++的好地方(貝塞爾曲面類?)。
int initgl(glvoid) // 初始化opengl
首先調貝塞爾display list。再(如果邊線要畫)畫連線控制點的線。你可以用space鍵開關這個。
int drawglscene(glvoid)
for(i=0;i<4;i++)
glcolor3f(1.0f,1.0f,1.0f);
glenable(gl_texture_2d);
}return true; // 成功返回
}killglwindow()函式沒有改動
createglwindow()函式沒有改動
我在這裡加了旋轉曲面的**,增加/降低解析度,顯示與否控制點連線。
if (keys[vk_left]) rotz -= 0.8f; // 按左鍵,向左旋轉
if (keys[vk_right]) rotz += 0.8f; // 按右鍵,向右旋轉
if (keys[vk_up])
if (keys[vk_down] && divs > 1)
if (keys[vk_space])
Bezier曲線繪製方法
de casteljau演算法能簡單快速地求出某個t值的曲線值,複雜度是o n 2 的乘法與加法。這裡平方級的計算感覺比較慢,如果應用秦九韶演算法來求該多項式值的話,複雜度降低到了o n 難度是因為數值穩定性的原因?而如果是繪製一條曲線的話,如果要用m個等間距的點來逼近這條曲線的話,複雜度是o m ...
Bezier曲線簡單實現
關鍵的公式,此公式不能畫出勻速曲線 completedpercent為當前所想得到位置的百分比0.0f 1.0f 根據貝塞爾曲線函式,求得取得此時的x,y座標 pt.x 1 completedpercent 1 completedpercent x1 2 1 completedpercent com...
Bezier曲線的de Casteljau演算法證明
ezier曲線 又稱貝茲曲線或貝塞爾曲線 的定義和性質請看維基百科貝茲曲線。它的定義是 其中,e casteljau演算法揭示了bezier數學上很美的乙個性質,我八成相信是先有了這個性質,才有了上面的定義式,當然它們是等價的。首先來看維基百科中的三張圖 二次bezier曲線 三次bezier曲線 ...