1. 程式人生 > >OpenGL進階(七)-康托爾集 謝爾賓斯基地毯 Koch雪花

OpenGL進階(七)-康托爾集 謝爾賓斯基地毯 Koch雪花

這一篇關於分形影象,當然只是入門。

分形通常被定義為“一個粗糙或零碎的幾何形狀,可以分成數個部分,且每一部分都(至少近似地)是整體縮小後的形狀”,即具有自相似的性質。分形有幾種型別,可以分別依據表現出的精確自相似性、半自相似性和統計自相似性來定義。雖然分形是一個數學構造,它們同樣可以在自然界中被找到,這使得它們被劃入藝術作品的範疇。

之前有做過一個鏤墊的程式,今天要做的是康託集,謝爾賓斯基地毯和Koch雪花。

原始碼下載

一維康託集

       康托爾集是由不斷去掉線段的中間三分之一而得出。首先從區間[0, 1]中去掉中間的三分之一(1/3, 2/3),留下兩條線段:[0, 1/3] ∪ [2/3, 1]。然後,把這兩條線段的中間三分之一都去掉,留下四條線段:[0, 1/9] ∪ [2/9, 1/3] ∪ [2/3, 7/9] ∪ [8/9, 1]。把這個過程一直進行下去。

      看程式實現:

[cpp] view plaincopyprint?
  1. void cantuo1(GLfloat *a, GLfloat *b)  
  2. {  
  3.      glBegin(GL_LINES);  
  4.     glVertex3fv(a);  
  5.     glVertex3fv(b);  
  6.     glEnd();  
  7. }  
  8. void divide_cantuo1(GLfloat *a, GLfloat *b, int m)  
  9. {  
  10.     cantuo1(a,b);  
  11.     GLfloat mid[2][3];  
  12.     if(m>0)  
  13.        {  
  14.         /* compute 1/3points */
  15.         mid[0][0]=a[0]+(b[0]-a[0])/3.0;  
  16.         mid[0][1]=(a[1]+b[1])/2.0+0.5;  
  17.         mid[0][2]=(a[2]+b[2])/2.0;  
  18.         mid[1][0]=b[0]-(b[0]-a[0])/3.0;  
  19.         mid[1][1]=(a[1]+b[1])/2.0+0.5;  
  20.         mid[1][2]=(a[2]+b[2])/2.0;  
  21.         a[1]+=0.5;  
  22.         b[1]+=0.5;  
  23.         /* create 2 part  by subdivision */
  24.         divide_cantuo1(mid[1],b, m-1);  
  25.         divide_cantuo1(a,mid[0], m-1);  
  26.     }  
  27. }  
void cantuo1(GLfloat *a, GLfloat *b)
{
     glBegin(GL_LINES);
    glVertex3fv(a);
    glVertex3fv(b);
    glEnd();
}

void divide_cantuo1(GLfloat *a, GLfloat *b, int m)
{
    cantuo1(a,b);
    GLfloat mid[2][3];
    if(m>0)
       {

        /* compute 1/3points */
        mid[0][0]=a[0]+(b[0]-a[0])/3.0;
        mid[0][1]=(a[1]+b[1])/2.0+0.5;
        mid[0][2]=(a[2]+b[2])/2.0;
        mid[1][0]=b[0]-(b[0]-a[0])/3.0;
        mid[1][1]=(a[1]+b[1])/2.0+0.5;
        mid[1][2]=(a[2]+b[2])/2.0;
        a[1]+=0.5;
        b[1]+=0.5;
        /* create 2 part  by subdivision */
        divide_cantuo1(mid[1],b, m-1);
        divide_cantuo1(a,mid[0], m-1);
    }
}

divide_cantuo1中,首先將ab直線繪製出來,然後計算出1/3和2/3處的點座標(y方向要移動0.5個單位),然後在分別繪製起點到1/3處和2/3處到終點的曲線。

cantuo1就是畫線函式。

渲染出來看一下:

[cpp] view plaincopyprint?
  1. void renderGL()  
  2. {  
  3.     GLfloat a[2][3]={{-3.0, 0.0, 0.0},{3.0, 0, 0}};  
  4.     // Clear the color and depth buffers.
  5.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
  6.     // We don't want to modify the projection matrix. */
  7.     glMatrixMode( GL_MODELVIEW );  
  8.     glLoadIdentity( );  
  9.     // Move down the z-axis.
  10.     glTranslatef( 0.0, -2.0, -5.0 );  
  11.     glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
  12.     glLineWidth(4.0);  
  13.     divide_cantuo1(a[0],a[1],5);  
  14.     SDL_GL_SwapBuffers( );  
  15. }  
void renderGL()
{
    GLfloat a[2][3]={{-3.0, 0.0, 0.0},{3.0, 0, 0}};
    // 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, -2.0, -5.0 );
    glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
    glLineWidth(4.0);
    divide_cantuo1(a[0],a[1],5);
    SDL_GL_SwapBuffers( );
}


最後的效果是這樣:


謝爾賓斯基地毯

       謝爾賓斯基地毯的構造與謝爾賓斯基三角形相似,區別僅在於謝爾賓斯基地毯是以正方形而非等邊三角形為基礎的。將一個實心正方形劃分為的9個小正方形,去掉中間的小正方形,再對餘下的小正方形重複這一操作便能得到謝爾賓斯基地毯。

     看程式碼:

[cpp] view plaincopyprint?
  1. void cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d )  
  2. {  
  3.     staticint i=0;  
  4.     glBegin(GL_QUADS);  
  5.     glColor3fv(colors[i++%4]);  
  6.     glVertex3fv(a);  
  7.     glVertex3fv(b);  
  8.     glVertex3fv(c);  
  9.     glVertex3fv(d);  
  10.     glEnd();  
  11. }  
  12. void divide_cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d,int m)  
  13. {  
  14.     GLfloat mid[12][3];  
  15.     if(m>0)  
  16.        {  
  17.         // compute 12 points
  18.         mid[0][0]=a[0]+(b[0]-a[0])/3.0;  
  19.         mid[0][1]=a[1]+(b[1]-a[1])/3.0;  
  20.         mid[0][2]=0.0;  
  21.         mid[1][0]=a[0]+2.0*(b[0]-a[0])/3.0;  
  22.         mid[1][1]=a[1]+2.0*(b[1]-a[1])/3.0;  
  23.         mid[1][2]=0.0;  
  24.         mid[2][0]=d[0]+1.0*(a[0]-d[0])/3.0;  
  25.         mid[2][1]=d[1]+2.0*(a[1]-d[1])/3.0;  
  26.         mid[2][2]=0.0;  
  27.         mid[3][0]=a[0]+(b[0]-a[0])/3.0;  
  28.         mid[3][1]=d[1]+2.0*(a[1]-d[1])/3.0;  
  29.         mid[3][2]=0.0;  
  30.         mid[4][0]=a[0]+2.0*(b[0]-a[0])/3.0;  
  31.         mid[4][1]=d[1]+2.0*(a[1]-d[1])/3.0;  
  32.         mid[4][2]=0.0;  
  33.         mid[5][0]=c[0]+2.0*(b[0]-c[0])/3.0;  
  34.         mid[5][1]=c[1]+2.0*(a[1]-d[1])/3.0;  
  35.         mid[5][2]=0.0;  
  36.         mid[6][0]=d[0]+(a[0]-d[0])/3.0;  
  37.         mid[6][1]=d[1]+(a[1]-d[1])/3.0;  
  38.         mid[6][2]=0.0;  
  39.         mid[7][0]=a[0]+(b[0]-a[0])/3.0;  
  40.         mid[7][1]=d[1]+(a[1]-d[1])/3.0;  
  41.         mid[7][2]=0.0;  
  42.         mid[8][0]=a[0]+2.0*(b[0]-a[0])/3.0;  
  43.         mid[8][1]=d[1]+(a[1]-d[1])/3.0;  
  44.         mid[8][2]=0.0;  
  45.         mid[9][0]=c[0]+2.0*(b[0]-c[0])/3.0;  
  46.         mid[9][1]=d[1]+(a[1]-d[1])/3.0;  
  47.         mid[9][2]=0.0;  
  48.         mid[10][0]=a[0]+(b[0]-a[0])/3.0;  
  49.         mid[10][1]=d[1]+(c[1]-d[1])/3.0;  
  50.         mid[10][2]=0.0;  
  51.         mid[11][0]=a[0]+2.0*(b[0]-a[0])/3.0;  
  52.         mid[11][1]=d[1]+2.0*(c[1]-d[1])/3.0;  
  53.         mid[11][2]=0.0;  
  54.         /* create 9 part  by subdivision */
  55.         divide_cantuo2(a,mid[0],mid[3],mid[2], m-1);  
  56.         divide_cantuo2(mid[0],mid[1],mid[4],mid[3],m-1);  
  57.         divide_cantuo2(mid[1],b,mid[5],mid[4],m-1);  
  58.         divide_cantuo2(mid[2],mid[3],mid[7],mid[6],m-1);  
  59.         //divide_cantuo2(mid[3],mid[4],mid[8],mid[7],m-1);
  60.         divide_cantuo2(mid[4],mid[5],mid[9],mid[8],m-1);  
  61.         divide_cantuo2(mid[6],mid[7],mid[10],d,m-1);  
  62.         divide_cantuo2(mid[7],mid[8],mid[11],mid[10],m-1);  
  63.         divide_cantuo2(mid[8],mid[9],c,mid[11],m-1);  
  64.         printf("m:%d\n",m);  
  65.     }  
  66.     else
  67.     cantuo2(a,b,c,d);  
  68. }  
void cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d )
{
    static int i=0;
    glBegin(GL_QUADS);
    glColor3fv(colors[i++%4]);
    glVertex3fv(a);
    glVertex3fv(b);
    glVertex3fv(c);
    glVertex3fv(d);
    glEnd();
}

void divide_cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d,int m)
{
    GLfloat mid[12][3];
    if(m>0)
       {
        // compute 12 points
        mid[0][0]=a[0]+(b[0]-a[0])/3.0;
        mid[0][1]=a[1]+(b[1]-a[1])/3.0;
        mid[0][2]=0.0;

        mid[1][0]=a[0]+2.0*(b[0]-a[0])/3.0;
        mid[1][1]=a[1]+2.0*(b[1]-a[1])/3.0;
        mid[1][2]=0.0;


        mid[2][0]=d[0]+1.0*(a[0]-d[0])/3.0;
        mid[2][1]=d[1]+2.0*(a[1]-d[1])/3.0;
        mid[2][2]=0.0;

        mid[3][0]=a[0]+(b[0]-a[0])/3.0;
        mid[3][1]=d[1]+2.0*(a[1]-d[1])/3.0;
        mid[3][2]=0.0;

        mid[4][0]=a[0]+2.0*(b[0]-a[0])/3.0;
        mid[4][1]=d[1]+2.0*(a[1]-d[1])/3.0;
        mid[4][2]=0.0;

        mid[5][0]=c[0]+2.0*(b[0]-c[0])/3.0;
        mid[5][1]=c[1]+2.0*(a[1]-d[1])/3.0;
        mid[5][2]=0.0;

        mid[6][0]=d[0]+(a[0]-d[0])/3.0;
        mid[6][1]=d[1]+(a[1]-d[1])/3.0;
        mid[6][2]=0.0;

        mid[7][0]=a[0]+(b[0]-a[0])/3.0;
        mid[7][1]=d[1]+(a[1]-d[1])/3.0;
        mid[7][2]=0.0;

        mid[8][0]=a[0]+2.0*(b[0]-a[0])/3.0;
        mid[8][1]=d[1]+(a[1]-d[1])/3.0;
        mid[8][2]=0.0;

        mid[9][0]=c[0]+2.0*(b[0]-c[0])/3.0;
        mid[9][1]=d[1]+(a[1]-d[1])/3.0;
        mid[9][2]=0.0;

        mid[10][0]=a[0]+(b[0]-a[0])/3.0;
        mid[10][1]=d[1]+(c[1]-d[1])/3.0;
        mid[10][2]=0.0;

        mid[11][0]=a[0]+2.0*(b[0]-a[0])/3.0;
        mid[11][1]=d[1]+2.0*(c[1]-d[1])/3.0;
        mid[11][2]=0.0;
        /* create 9 part  by subdivision */
        divide_cantuo2(a,mid[0],mid[3],mid[2], m-1);
        divide_cantuo2(mid[0],mid[1],mid[4],mid[3],m-1);
        divide_cantuo2(mid[1],b,mid[5],mid[4],m-1);
        divide_cantuo2(mid[2],mid[3],mid[7],mid[6],m-1);
        //divide_cantuo2(mid[3],mid[4],mid[8],mid[7],m-1);
        divide_cantuo2(mid[4],mid[5],mid[9],mid[8],m-1);
        divide_cantuo2(mid[6],mid[7],mid[10],d,m-1);
        divide_cantuo2(mid[7],mid[8],mid[11],mid[10],m-1);
        divide_cantuo2(mid[8],mid[9],c,mid[11],m-1);
        printf("m:%d\n",m);
    }
    else
    cantuo2(a,b,c,d);
}

這次divide_cantuo2函式需要找的是12個點,建議在紙上面畫一下。

cantuo2就是畫矩形。

渲染一下:

[cpp] view plaincopyprint?
  1. void renderGL()  
  2. {  
  3.     GLfloat a[4][3]={{-3.0, 3.0, 0.0},{3.0, 3.0, 0},{3.0, -3.0, 0.0},{-3.0, -3.0, 0}};  
  4.     // Clear the color and depth buffers.
  5.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
  6.     // We don't want to modify the projection matrix. */
  7.     glMatrixMode( GL_MODELVIEW );  
  8.     glLoadIdentity( );  
  9.     // Move down the z-axis.
  10.     glTranslatef( 0.0, 0.0, -6.0 );  
  11.     glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
  12.     divide_cantuo2(a[0],a[1],a[2],a[3],2);  
  13.     SDL_GL_SwapBuffers( );  
  14. }  
void renderGL()
{
    GLfloat a[4][3]={{-3.0, 3.0, 0.0},{3.0, 3.0, 0},{3.0, -3.0, 0.0},{-3.0, -3.0, 0}};
    // 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, -6.0 );
    glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
    divide_cantuo2(a[0],a[1],a[2],a[3],2);
    SDL_GL_SwapBuffers( );
}

當然,可以改變迭代次數和繪製的顏色。



Koch雪花

給定線段AB,科赫曲線可以由以下步驟生成:
將線段分成三等份(AC,CD,DB)
以CD為底,向外(內外隨意)畫一個等邊三角形DMC
將線段CD移去
分別對AC,CM,MD,DB重複1~3。
科赫雪花是以等邊三角形三邊生成的科赫曲線組成的。。每條科赫曲線的長度是無限大,它是連續而無處可微的曲線。

程式碼實現:

[cpp] view plaincopyprint?
  1. void Koch(GLfloat *a, GLfloat *b)  
  2. {  
  3.     staticint i=0;  
  4.     glBegin(GL_LINES);  
  5.     glColor3fv(colors[i++%4]);  
  6.     glVertex3fv(a);  
  7.     glVertex3fv(b);  
  8.     glEnd();  
  9. }  
  10. void divideKoch(GLfloat *a, GLfloat *b,int m)  
  11. {  
  12.     float length= sqrt(pow(a[0] - b[0],2) + pow(a[1] - b[1],2))/3;  
  13.     printf("length:%f\n",length);  
  14.     GLfloat mid[3][3];  
  15.     if(m>0)  
  16.        {  
  17.         // compute 3 points
  18.         mid[0][0]=a[0]+(b[0]-a[0])/3.0;  
  19.         mid[0][1]=a[1]+(b[1]-a[1])/3.0;  
  20.         mid[0][2]=0.0;  
  21.         mid[1][0]=b[0]-(b[0]-a[0])/3.0;  
  22.         mid[1][1]=b[1]-(b[1]-a[1])/3.0;  
  23.         mid[1][2]=0.0;  
  24.         double alpha = 0.0;  
  25.         if (b[1]>= a[1])  
  26.         {  
  27.             alpha = atan((double) (b[1]-a[1]) / (b[0]-a[0]));  
  28.             if (b[0]>a[0])  
  29.             {  
  30.                 mid[2][0] = mid[0][0] + length * cos(alpha + PI / 3);  
  31.                 mid[2][1] = mid[0][1] + length  * sin(alpha + PI / 3);  
  32.             }  
  33.             else
  34.             {  
  35.                 mid[2][0] = mid[0][0] - length * cos(alpha + PI / 3);  
  36.                 mid[2][1] = mid[0][1] - length * sin(alpha + PI / 3);  
  37.             }  
  38.         }  
  39.         else
  40.         {  
  41.             alpha = atan((double) (a[1] - b[1]) / (b[0] - a[0]) );  
  42.             if (b[0]>a[0])  
  43.             {  
  44.                 mid[2][0] = mid[1][0] - length * cos(alpha + PI / 3);  
  45.                 mid[2][1] = mid[1][1] + length * sin(alpha + PI / 3);  
  46.             }  
  47.             else
  48.             {  
  49.                 mid[2][0] = mid[1][0] + length * cos(alpha + PI / 3);  
  50.                 mid[2][1] = mid[1][1] - length * sin(alpha + PI / 3);  
  51.             }  
  52.         }  
  53.         /* create 9 part  by subdivision */
  54.         divideKoch(a,mid[0], m-1);  
  55.         divideKoch(mid[0],mid[2],m-1);  
  56.         divideKoch(mid[2],mid[1],m-1);  
  57.         divideKoch(mid[1],b,m-1);  
  58.         printf("m:%d\n",m);  
  59.     }  
  60.     else
  61.     Koch(a,b);  
  62. }  
void Koch(GLfloat *a, GLfloat *b)
{
    static int i=0;
    glBegin(GL_LINES);
    glColor3fv(colors[i++%4]);
    glVertex3fv(a);
    glVertex3fv(b);
    glEnd();
}

void divideKoch(GLfloat *a, GLfloat *b,int m)
{
    float length= sqrt(pow(a[0] - b[0],2) + pow(a[1] - b[1],2))/3;
    printf("length:%f\n",length);
    GLfloat mid[3][3];
    if(m>0)
       {
        // compute 3 points
        mid[0][0]=a[0]+(b[0]-a[0])/3.0;
        mid[0][1]=a[1]+(b[1]-a[1])/3.0;
        mid[0][2]=0.0;

        mid[1][0]=b[0]-(b[0]-a[0])/3.0;
        mid[1][1]=b[1]-(b[1]-a[1])/3.0;
        mid[1][2]=0.0;
        double alpha = 0.0;
        if (b[1]>= a[1])
		{
			alpha = atan((double) (b[1]-a[1]) / (b[0]-a[0]));
			if (b[0]>a[0])
			{
				mid[2][0] = mid[0][0] + length * cos(alpha + PI / 3);
				mid[2][1] = mid[0][1] + length  * sin(alpha + PI / 3);
			}
			else
			{
				mid[2][0] = mid[0][0] - length * cos(alpha + PI / 3);
				mid[2][1] = mid[0][1] - length * sin(alpha + PI / 3);
			}
		}
		else
		{
			alpha = atan((double) (a[1] - b[1]) / (b[0] - a[0]) );
			if (b[0]>a[0])
			{
				mid[2][0] = mid[1][0] - length * cos(alpha + PI / 3);
				mid[2][1] = mid[1][1] + length * sin(alpha + PI / 3);
			}
			else
			{
				mid[2][0] = mid[1][0] + length * cos(alpha + PI / 3);
				mid[2][1] = mid[1][1] - length * sin(alpha + PI / 3);
			}
		}

        /* create 9 part  by subdivision */
        divideKoch(a,mid[0], m-1);
        divideKoch(mid[0],mid[2],m-1);
        divideKoch(mid[2],mid[1],m-1);
        divideKoch(mid[1],b,m-1);
        printf("m:%d\n",m);
    }
    else
    Koch(a,b);
}

divideKoch只是對一條直線進行分形。思路就是找到線的1/3,2/3處點的座標,還有中間生成的等邊三角形的一個頂點,然後對生成的每一條邊都進行迭代。

渲染一個雪花:

[cpp] view plaincopyprint?
  1. void renderGL()  
  2. {  
  3.     //Define a triangle in space
  4.     GLfloat a[3][3]={{-3.0, 0.0, 0.0},{3.0, 0.0, 0},{0.0, 5.196, 0.0}};  
  5.     // Clear the color and depth buffers.
  6.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
  7.     // We don't want to modify the projection matrix. */
  8.     glMatrixMode( GL_MODELVIEW );  
  9.     glLoadIdentity( );  
  10.     // Move down the z-axis.
  11.     glTranslatef( 0.0, -2.0, -9.0 );  
  12.     glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
  13.     //Koch(a[0],a[1]);
  14.     divideKoch(a[1],a[0],6);  
  15.     divideKoch(a[2],a[1],6);  
  16.     divideKoch(a[0],a[2],6);  
  17.     SDL_GL_SwapBuffers( );  
  18. }  
void renderGL()
{
    //Define a triangle in space
    GLfloat a[3][3]={{-3.0, 0.0, 0.0},{3.0, 0.0, 0},{0.0, 5.196, 0.0}};
    // 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, -2.0, -9.0 );
    glClear(GL_COLOR_BUFFER_BIT);  /* clear the window */
    //Koch(a[0],a[1]);
    divideKoch(a[1],a[0],6);
    divideKoch(a[2],a[1],6);
    divideKoch(a[0],a[2],6);
    SDL_GL_SwapBuffers( );
}

結果: