OpenGL进阶(七):康托尔集 谢尔宾斯基地毯 Koch雪花

发表于2017-09-26
评论0 867浏览

这一篇关于分形图像,当然只是入门。

分形通常被定义为“一个粗糙或零碎的几何形状,可以分成数个部分,且每一部分都(至少近似地)是整体缩小后的形状”,即具有自相似的性质。分形有几种类型,可以分别依据表现出的精确自相似性、半自相似性和统计自相似性来定义。虽然分形是一个数学构造,它们同样可以在自然界中被找到,这使得它们被划入艺术作品的范畴。

之前有做过一个镂垫的程序,今天要做的是康托集,谢尔宾斯基地毯和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]。把这个过程一直进行下去。

      看程序实现:

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

divide_cantuo1中,首先将ab直线绘制出来,然后计算出1/3和2/3处的点坐标(y方向要移动0.5个单位),然后在分别绘制起点到1/3处和2/3处到终点的曲线。

cantuo1就是画线函数。

渲染出来看一下:

  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. }  


最后的效果是这样:



谢尔宾斯基地毯

       谢尔宾斯基地毯的构造与谢尔宾斯基三角形相似,区别仅在于谢尔宾斯基地毯是以正方形而非等边三角形为基础的。将一个实心正方形划分为的9个小正方形,去掉中间的小正方形,再对余下的小正方形重复这一操作便能得到谢尔宾斯基地毯。

     看代码:

  1. void cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d )  
  2. {  
  3.     static int 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.   
  13. void divide_cantuo2(GLfloat *a, GLfloat *b,GLfloat *c,GLfloat *d,int m)  
  14. {  
  15.     GLfloat mid[12][3];  
  16.     if(m>0)  
  17.        {  
  18.         // compute 12 points  
  19.         mid[0][0]=a[0] (b[0]-a[0])/3.0;  
  20.         mid[0][1]=a[1] (b[1]-a[1])/3.0;  
  21.         mid[0][2]=0.0;  
  22.   
  23.         mid[1][0]=a[0] 2.0*(b[0]-a[0])/3.0;  
  24.         mid[1][1]=a[1] 2.0*(b[1]-a[1])/3.0;  
  25.         mid[1][2]=0.0;  
  26.   
  27.   
  28.         mid[2][0]=d[0] 1.0*(a[0]-d[0])/3.0;  
  29.         mid[2][1]=d[1] 2.0*(a[1]-d[1])/3.0;  
  30.         mid[2][2]=0.0;  
  31.   
  32.         mid[3][0]=a[0] (b[0]-a[0])/3.0;  
  33.         mid[3][1]=d[1] 2.0*(a[1]-d[1])/3.0;  
  34.         mid[3][2]=0.0;  
  35.   
  36.         mid[4][0]=a[0] 2.0*(b[0]-a[0])/3.0;  
  37.         mid[4][1]=d[1] 2.0*(a[1]-d[1])/3.0;  
  38.         mid[4][2]=0.0;  
  39.   
  40.         mid[5][0]=c[0] 2.0*(b[0]-c[0])/3.0;  
  41.         mid[5][1]=c[1] 2.0*(a[1]-d[1])/3.0;  
  42.         mid[5][2]=0.0;  
  43.   
  44.         mid[6][0]=d[0] (a[0]-d[0])/3.0;  
  45.         mid[6][1]=d[1] (a[1]-d[1])/3.0;  
  46.         mid[6][2]=0.0;  
  47.   
  48.         mid[7][0]=a[0] (b[0]-a[0])/3.0;  
  49.         mid[7][1]=d[1] (a[1]-d[1])/3.0;  
  50.         mid[7][2]=0.0;  
  51.   
  52.         mid[8][0]=a[0] 2.0*(b[0]-a[0])/3.0;  
  53.         mid[8][1]=d[1] (a[1]-d[1])/3.0;  
  54.         mid[8][2]=0.0;  
  55.   
  56.         mid[9][0]=c[0] 2.0*(b[0]-c[0])/3.0;  
  57.         mid[9][1]=d[1] (a[1]-d[1])/3.0;  
  58.         mid[9][2]=0.0;  
  59.   
  60.         mid[10][0]=a[0] (b[0]-a[0])/3.0;  
  61.         mid[10][1]=d[1] (c[1]-d[1])/3.0;  
  62.         mid[10][2]=0.0;  
  63.   
  64.         mid[11][0]=a[0] 2.0*(b[0]-a[0])/3.0;  
  65.         mid[11][1]=d[1] 2.0*(c[1]-d[1])/3.0;  
  66.         mid[11][2]=0.0;  
  67.         /* create 9 part  by subdivision */  
  68.         divide_cantuo2(a,mid[0],mid[3],mid[2], m-1);  
  69.         divide_cantuo2(mid[0],mid[1],mid[4],mid[3],m-1);  
  70.         divide_cantuo2(mid[1],b,mid[5],mid[4],m-1);  
  71.         divide_cantuo2(mid[2],mid[3],mid[7],mid[6],m-1);  
  72.         //divide_cantuo2(mid[3],mid[4],mid[8],mid[7],m-1);  
  73.         divide_cantuo2(mid[4],mid[5],mid[9],mid[8],m-1);  
  74.         divide_cantuo2(mid[6],mid[7],mid[10],d,m-1);  
  75.         divide_cantuo2(mid[7],mid[8],mid[11],mid[10],m-1);  
  76.         divide_cantuo2(mid[8],mid[9],c,mid[11],m-1);  
  77.         printf("m:%d\n",m);  
  78.     }  
  79.     else  
  80.     cantuo2(a,b,c,d);  
  81. }  

这次divide_cantuo2函数需要找的是12个点,建议在纸上面画一下。

cantuo2就是画矩形。

渲染一下:

  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. }  


当然,可以改变迭代次数和绘制的颜色。




Koch雪花

给定线段AB,科赫曲线可以由以下步骤生成:
将线段分成三等份(AC,CD,DB)
以CD为底,向外(内外随意)画一个等边三角形DMC
将线段CD移去
分别对AC,CM,MD,DB重复1~3。
科赫雪花是以等边三角形三边生成的科赫曲线组成的。。每条科赫曲线的长度是无限大,它是连续而无处可微的曲线。

代码实现:

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

divideKoch只是对一条直线进行分形。思路就是找到线的1/3,2/3处点的坐标,还有中间生成的等边三角形的一个顶点,然后对生成的每一条边都进行迭代。

渲染一个雪花:

  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. }  

结果:



关于分形,这里只是一个很小很小的开始,还有很多的分形图像比如门格海绵,分形地形,分形云彩等等。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引