Bezier曲線理論與OpenGL實作
在專欄的第二節中,我簡單梳理了計算機圖形學當中簡單的數理知識和常見的圖形學概念。考慮到後期的具體項目——曲面造型,我準備抽出幾節特意學習和整理了一下經常用到的樣條曲線、曲面的理論知識和實作,今天寫個部落格來整理一下Bezier曲線的理論以及代碼實作。
01樣條曲線、曲面概念
在繪圖術語中,**樣條是通過一組指定點集而生成平滑曲線的柔性帶。**當繪制曲線時,幾個較小 的權重沿着樣條的長度配置設定并固定在繪圖表上的相應位置。術語——樣條曲線(spline curve )原指 使用這種方式繪制的曲線。
數學上使用分段三次多項式函數來描述這種曲線,其中各曲線段的連接配接 處有連續的一次和二次導數。
在計算機圖形學中,
樣條曲線指由多項式曲線段連接配接而成的曲線,在每段的邊界處滿足特定的連續性條件。
樣條曲面(spline surface)可以使用兩組正交樣條曲線進行 描述。
如果我們用丄表示控制點的位置,則樣條曲面上的任意一點可用樣條曲線很合函數的積來計算:
02 Bezier曲線概念
Bezier樣條逼近方法是法國工程師Hem Bezier為雷諾(Renault)公司設計汽車車身而開發的。
Bezier曲線通過控制曲線上的四個點(起始點、終止點以及兩個互相分離的中間點)來創造、編輯圖形。其中起重要作用的是位于曲線中央的控制線。這條線是虛拟的,中間與貝塞爾曲線交叉,兩端是控制端點。移動兩端的端點時貝塞爾曲線改變曲線的曲率(彎曲的程度);移動中間點(也就是移動虛拟的控制線)時,貝塞爾曲線在起始點和終止點鎖定的情況下做均勻移動。
P0、P1、P2、P3四個點在平面或在三維空間中定義了三次Bezier曲線。曲線起始于P0走向P1,并從P2的方向來到P3。一般不會經過P1或P2;這兩個點隻是在那裡提供方向資訊。P0和P1之間的間距,決定了曲線在轉而趨進P3之前,走向P2方向的“長度有多長”。
盡管有的圖形軟體将Bezier樣條曲線控制點數限定為4,但一般來說,Bezier樣條曲線段可以拟合任何數目的控制點。Bezier多項式的次數取決于曲線将逼近的控制點數量及相關位置。
多數情況下Bezier曲線是一個階數比控制點數少1的多項式:三點生成一個抛物線,四點生成一個三次曲線,依次類推。
而在一定安排的控制點控制下,可獲得退化的Bezier多項式。例如,使用三個共線控制點生成的Bezier曲線是一直線段。
而所有控制點都在同一坐标位置時生成的說Bezier曲線”是一個點。
02 Bezier曲線描述
對于一般不加限制的Bezier曲線,混合函數描述是最友善的表示辦法。
假設給出n+1個控制點位置:
這裡k可以取0到n,這些坐标點将混合産生下列位置向向量P(u),用來描述P0和pn間逼近Bezier多項式函數的路徑:
Bezier混合函數是Bernstein多項式:
這裡,參數C(n,k)是二項式系數:
由此推導獲得單個曲線坐标三個參數方程的集合:
對于多項式形式,Bezier曲線的一般表達式為:
其中,三次Bezier樣條曲線表達式
03 Bezier曲線性質
1、端點性質:
a)P(0)=P0, P(1)=Pn, 即:曲線過二端點。
b)P’(0)=n(P1-P0), P’(1)=n(Pn-Pn-1)
即:在二端點與控制多邊形相切。
2、凸包性:Bezier曲線完成落在控制多邊形的凸包内。
3、對稱性:由Pi與Pn-i組成的曲線,位置一緻,方向相反。
4、包絡性:Pn (t)=(1-t)Pn-1 (t)+tPn-1 (t)
04 Bezier曲線的OpenGL實作
參考CSDN部落客麥田IT
#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;
}
運作結果
代碼我還沒撸明白,我想在三次B樣條曲線再摳實作細節。晚安。