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]。把这个过程一直进行下去。
看程序实现:
- 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);
- }
- }
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就是画线函数。
渲染出来看一下:
- 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( );
- }
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个小正方形,去掉中间的小正方形,再对余下的小正方形重复这一操作便能得到谢尔宾斯基地毯。
看代码:
- 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);
- }
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就是画矩形。
渲染一下:
- 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( );
- }
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( );
}
![](https://img-my.csdn.net/uploads/201212/29/1356754016_2657.png)
当然,可以改变迭代次数和绘制的颜色。
Koch雪花
给定线段AB,科赫曲线可以由以下步骤生成:
将线段分成三等份(AC,CD,DB)
以CD为底,向外(内外随意)画一个等边三角形DMC
将线段CD移去
分别对AC,CM,MD,DB重复1~3。
科赫雪花是以等边三角形三边生成的科赫曲线组成的。。每条科赫曲线的长度是无限大,它是连续而无处可微的曲线。
代码实现:
- 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);
- }
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处点的坐标,还有中间生成的等边三角形的一个顶点,然后对生成的每一条边都进行迭代。
渲染一个雪花:
- 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( );
- }
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( );
}
结果:
关于分形,这里只是一个很小很小的开始,还有很多的分形图像比如门格海绵,分形地形,分形云彩等等。可以参考:http://zh.wikipedia.org/wiki/%E5%88%86%E5%BD%A2