OpenGL进阶(三) - 样条曲线的绘制

Cardinal样条曲线

这个比较简单,一个终点,一个起点,两个控制点。

终点和起点中间的点靠插值实现,插值函数:

P(u)=Pk-1(-s*u*u*u+2s*u*u)+Pk[(2-s)u*u*u+(s-3)u*u+1]+Pk+1[(s-2)*u*u*u+(3-2s)*u*u+s*u]+Pk+2(s*u*u*u-s*u*u)

代码实现:

void getInterpolation(point &p1,point &p2,point &p3,point &p4,float s,float u,point *result)
{
    float u2=u*u;
    float u3=u2*u;
    result->x=p1.x*(2*s*u2 - s*u3 - s*u) + p2.x*((2-s)*u3 + (s-3)*u2 + 1) +p3.x*((s-2)*u3 + (3-2*s)*u2 + s*u) + p4.x*(s*u3 - s*u2);
    result->y=p1.y*(2*s*u2 - s*u3 - s*u) + p2.y*((2-s)*u3 + (s-3)*u2 + 1) +p3.y*((s-2)*u3 + (3-2*s)*u2 + s*u) + p4.y*(s*u3 - s*u2);
}
void drawCdnLine(point *a,float tension)
{
    int i,j,k=100;
    float s,step=1.0/(float)k;
    s=(1.0-tension)/2.0;
    glColor3f(1.0f,0.0f,0.0f);
    glBegin(GL_LINE_STRIP);
    float uValue;
    for(i=0;i<4;i++)
    {
        uValue=0.0f;
        for(j=0;j<k;j++)
        {
        point tmp;
        getInterpolation(a[i],a[(i+1)%4],a[(i+2)%4],a[(i+3)%4],s,uValue,&tmp);
        glVertex2f(tmp.x,tmp.y);
        uValue += step;
        }
    }
    glEnd();
}
void renderGL()
{
    // Clear the color and depth buffers.
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    // We don't want to modify the projection matrix. */
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    // Move down the z-axis.
    glTranslatef( 0.0, -1.0, -8.0 );
    point c[4]={{-2,0,0},{0,2,0},{3,0,0},{0,-1,0}};
    drawCdnLine(c,0.0);
    SDL_GL_SwapBuffers( );
}

OpenGL进阶(三) - 样条曲线的绘制_第1张图片


Bezier曲线

与上面的类似,只是插值的方式不同。

这里就直接调用OpenGL的库函数了。

首先定义一个全局的数组:

GLfloat c[4][3]={{-10,-10,0},{-1,20,0},{1,-20,0},{10,10,0}};


初始化函数中添加两行代码:

glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &c[0][0]);
glEnable(GL_MAP1_VERTEX_3);


然后写渲染函数:

void renderGL()
{
    int i;
    // Clear the color and depth buffers.
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    // We don't want to modify the projection matrix. */
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    // Move down the z-axis.
    glTranslatef( 0.0, -0.0, -50.0 );
    glColor3f(1.0, 1.0, 1.0);
    glBegin(GL_POINTS);
    for (i = 0; i <= 90; i++)
         glEvalCoord1f((GLfloat) i/90.0);
    glEnd();
    glPointSize(5.0);
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_POINTS);
      for (i = 0; i < 4; i++)
         glVertex3fv(&c[i][0]);
    glEnd();
    SDL_GL_SwapBuffers( );
}


OpenGL进阶(三) - 样条曲线的绘制_第2张图片

主要注意两个函数:glMap1和glEvalCoord1。

1.void glMap1{fd}(GLenum target,TYPE u1,TYPE u2,GLint stride, GLint order,const TYPE *points);
功能:定义求值器。 

参数:target:指出了控制顶点的意义以及在points参数中需要提供多少值。

points:可以指向控制点集、RGBA颜色值或是纹理坐标串等。

u1、u2:限定了变量U的取值范围,通常是从0变化到1。

stride:表示跨度(在每块存储区内浮点数或双精度数的个数,即两个控制点间的偏移量)。

order:阶数,等于次数加1,与控制点数相等。

2.void glEvalCoord1{fd}[v](TYPE u)。
功能:该函数将产生曲线坐标值并将其绘制。

参数:u:为定义域内的任意值,每调用一次将只产生一个坐标,此坐标值也是任意的。

但目前较多采用的是定义均匀间隔曲线坐标值,依次调用glMapGrid1*()和glEvalMesh1()可以获得等间隔值。这两个函数分别用来定义一个一维网格和计算相应的坐标值。

另外,曲线定义后必须再glEnable()函数显式启动后才能起作用,其参数与target保持一致。在使用完毕后通过glDisable()函数将其关闭。



你可能感兴趣的:(OpenGL进阶(三) - 样条曲线的绘制)