zoukankan      html  css  js  c++  java
  • 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;  
    
    }  
    

      

  • 相关阅读:
    一个网站架构的变迁
    网络编程
    http协议篇
    第1篇 编程能力是什么
    django中的cookies和session机制
    django的认证与授权系统
    python的异常处理
    第0篇
    mysql优化和全局管理杂记
    k8s中pod的资源配置详解
  • 原文地址:https://www.cnblogs.com/caster99/p/4746652.html
Copyright © 2011-2022 走看看