OpenGL繪制簡單的參數曲線(完)——三次B樣條曲線


  我們今天來介紹一下B樣條曲線。相比較Beizer曲線來說,B樣條有着兩個優點:(1)k次B樣條曲線具有良好的局部性,它只與k+1個控制點有關;(2)B樣條曲線拼接較為簡單。不過B樣條曲線的公式比較難懂,網上介紹原理的也着實不多,這里詳細分享一下。

圖1

  我們先來看看什么是B樣條曲線,如圖1,我們以三次B樣條曲線為例。由於k次B樣條曲線的控制點有k+1個,所以P0P1P2P3控制u1u2段曲線,P1P2P3P4控制u2u3段曲線,P2P3P4P5控制u3u4段曲線。所以這樣就不會像beizer曲線那樣,改變任一控制點,都會對整個曲線產生影響。接下來我們看一下B樣條曲線的公式:

圖2

  S(t)表示的是uaua+1段曲線,k表示的k次B樣條曲線,所以S(t)就是控制點與基函數的乘積之和,其中控制點是從Pj點到Pi點,一共有i-j+1個。結合圖1中的u1u2段曲線舉個例子,Su1u2(t) = P0N0,3(t) + P1N1,3(t) + P2N2,3(t) + P3N3,3(t)。這個公式比較抽象,我們換種寫法,同時我們再給出N(t)的公式:

圖3 

  圖3、圖2的公式參數意義略有不同。圖3中,j表示的是起始的控制點,k表示的是k次B樣條曲線,i表示的是迭代參數,相當於控制點是從PjPj+k。基函數N(t)中參數ik跟其上面的公式保持同步。這個公式看起來很復雜,光用文字說明並不能解釋太清楚,我們舉一個二次B樣條曲線和一個三次B樣條曲線的例子來說明一下。

  (1)二次B樣條曲線:k=2,下面是二次B樣條曲線的三個基函數(建議大家拿筆算一算,這樣對公示理解的更深):

圖4

  這里我們跟據上面的基函數給出P0,2(t)的公式及相關性質:

圖5

  這條曲線的端點位置和端點切失如下:

圖6 

  根據上面的公式和性質可以得到如下曲線:

圖7

  (2)三次B樣條曲線:k=3,這個也是我們今天代碼展示的曲線。下面是三次B樣條曲線的四個基函數:

圖8

  這里我們跟據上面的基函數給出P0,3(t)的公式及相關性質:

圖9

  這條曲線的端點位置和端點切失如下:

圖10

  根據上面的公式和性質可以得到如下曲線:

圖11


   我們這里就講完三種最基本也是最常用的曲線,我們在貼B樣條曲線的代碼之前,先討論一下三種曲線的優缺點(這只是個人觀點)。首先,Hermite曲線和Beizer曲線基本是一致的,雖然原理上有着一定的差異,但從性質以及參數的影響程度來說,大致上是相似的。而且這兩種曲線在表達復雜的曲線時,都是利用低次曲線拼接的方式來表達。相比這兩種曲線,B樣條曲線就顯得比較具有優勢,具體優勢開頭也有提到,這里就不重復了,缺點也非常明顯,控制點不在曲線上導致不好容易控制。

  說到這,OpenGL繪制曲線就正式完結了。最后貼出三次B樣條曲線的代碼(效果就是圖1,不過可以拖動頂點調整曲線),大家可以試試。

 

#include <math.h>
#include <gl/glut.h>
#include <iostream>
using namespace std;

#define NUM_POINTS 6
#define NUM_SEGMENTS (NUM_POINTS-3)  

struct Point2
{
	double x;
	double y;
	
	Point2() { ; }
	Point2(int px, int py) { x = px; y = py; }
	void SetPoint2(int px, int py) { x = px; y = py; }
};

/*全局變量*/
Point2 vec[NUM_POINTS];  
bool mouseLeftDown = false;

/*繪制B樣條曲線*/
void Bspline(int n)
{
	float f1, f2, f3, f4;
	float deltaT = 1.0 / n;
	float T;

	glBegin(GL_LINE_STRIP);
	for (int num = 0; num < NUM_SEGMENTS; num++)
	{
		for (int i = 0; i <= n; i++) {

			T = i * deltaT;

			f1 = (-T*T*T + 3*T*T - 3*T + 1) / 6.0;
			f2 =(3*T*T*T - 6*T*T + 4) / 6.0;
			f3 = (-3*T*T*T +3*T*T + 3*T + 1) / 6.0;
			f4 = (T*T*T) / 6.0;

			glVertex2f( f1*vec[num].x + f2*vec[num+1].x + f3*vec[num+2].x + f4*vec[num+3].x,
				f1*vec[num].y + f2*vec[num+1].y + f3*vec[num+2].y + f4*vec[num+3].y);
		}
	}
	
	glEnd();
}


void display()   
{  
	glClear(GL_COLOR_BUFFER_BIT);  
	glLoadIdentity();  

	glLineWidth(1.5f);
	glColor3f(1.0,0.0,0.0);  
	glBegin(GL_LINE_STRIP);  
	for(int i = 0;i < NUM_POINTS; i++)   
	{  
		glVertex2f(vec[i].x, vec[i].y);
	}  
	glEnd();  

	glPointSize(10.0f);  
	glColor3f(0.0, 0.0, 1.0);
	glBegin(GL_POINTS);  
	for(int i = 0;i < NUM_POINTS; i++)   
	{  
		glVertex2f(vec[i].x, vec[i].y);
	}  
	glEnd();  

	Bspline(20);

	glFlush();
	glutSwapBuffers();  
}  

void init()   
{  
	glClearColor(1.0, 1.0, 1.0, 0.0);  
	glShadeModel(GL_FLAT);

	vec[0].SetPoint2(200, 400);
	vec[1].SetPoint2(100, 300);
	vec[2].SetPoint2(200, 200);
	vec[3].SetPoint2(250, 300);
	vec[4].SetPoint2(400, 200);
	vec[5].SetPoint2(400, 400);
}  

void reshape(int w, int h)  
{  
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, (GLsizei)w, (GLsizei)h, 0.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}  

void mouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		mouseLeftDown = true;
	}

	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
	{
		mouseLeftDown = false;
	}
}

double distance(int x1, int y1, int x2, int y2)
{
	return sqrt((x1-x2) * (x1 -x2) + (y1-y2) * (y1-y2));
}

void motion(int x, int y)
{
	if (mouseLeftDown)
	{
		for (int i = 0; i < NUM_POINTS; i++)
		{
			if (distance(vec[i].x, vec[i].y, x, y) < 20)
			{
				vec[i].SetPoint2(x, y);
			}
		}
	}

	glutPostRedisplay();
}

int main(int argc,char** argv)  
{  
	glutInit(&argc,argv);  
	glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);  
	glutInitWindowSize(500, 500);  
	glutInitWindowPosition (200, 200);
	glutCreateWindow("B-Spline Curve");  
	init();  

	glutDisplayFunc(display);  
	glutReshapeFunc(reshape);  
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutMainLoop();  

	return 0;  

}  

  


免責聲明!

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



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