前言(可以直接略过):
最近paper写完了,空闲时间比较多,于是开始画分形几何来玩。在我的上一篇文章中,Julia集和Mandelbrot集已经画过了,这回我想画一点更有意思的东西,想来想去,最终我决定画一个三维的分形图案,于是到M67的网站去抠了一张图,打算自己把它画出来。最初困扰我的是用什么工具来画它,分形图案都有大量的递归,计算量极大,像matlab和mathematica这样的工具封装太厉害,肯定是不行,速度比较快的也就只有openGL了。为了画这个图形,我花了两天时间来学openGL,总算能把它的粗略样子画出来了,小有成就~~
正文:
左图是实物的照片。它的生成法则是这样的:在一个正方体的6个面上分别生成一个长宽是原来正方体一半的小正方体。迭代了11次就是左图的样子了,其中红色正方体是最原始的正方体。这个图形无限迭代下去会形成一个四棱锥,它的每一面恰好是一个Sierpinski三角形。
右图是我用openGL画的,迭代了7次,在我的电脑上5秒左右可以画出来。
--------------------华丽的分割线----------------------------------------------------------------------------------------
#include <GL/glut.h> #define MAXCOLOR 7 float colortab[MAXCOLOR][4]= { 0.2, 0, 0.4, 0.0, //紫 0.2, 0, 0.5, 0.0, //紫 0.2, 0, 0.6, 0.0, //紫 0.3, 0, 1.0, 0.0, //紫 0.196, 0.3039, 0.296, 0.0, //深绿 0.2784, 1.0, 0.1843, 0.0, //浅绿 1.0, 0.3068, 0.1, 0.0 //橙 }; //画正方体 void Square(float center[3], float size, float color[4]) { glPushMatrix(); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); // 将背景颜色和散射颜色设置成同一颜色 glTranslatef(center[0], center[1], center[2]); //平衡坐标系 glutSolidCube(size);//利用库函数绘制一个半径为1的球体。 glPopMatrix(); } void init(void) { glClearColor(0.5, 0.5, 0.5, 0.0); //清理颜色,为黑色,(也可认为是背景颜色) float light_diffuse[]= { 1.0, 1.0, 1.0, 1.0}; //有灯光才能体现材质的效果,他的灯光的强度是与的关系。貌似是两个相乘什么的。(0-1)范围。 float light_position[] = { 0, 3, 2.0, 0.0 }; //设置点光源的矩阵,这个向量也忒奇怪了,1不跟着变,0跟着变,设置为透视之后又是1跟着变,0不跟着变。 float light_specular[] = { 1.0, 1.0, 0.0, 1.0 }; //反射光 float light_ambient[] = {0.5, 0.5, 0.5, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); //,漫射光会产生漫射的效果和高光的效果 glLightfv(GL_LIGHT0, GL_POSITION, light_position); //点光源没有漫射光的效果,会一直是黑色的。但会有高光反射的效果。 glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); //反射光基本没有效果。可能是我不知道吧 GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0}; GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0}; GLfloat hig_shininess[] = {100.0}; glMaterialfv(GL_FRONT,GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT,GL_SHININESS, hig_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); //如果什么都不设置,GL_LIGHT有默认的值。 glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); } //在给定中心点周围画6个正方体 void SurroundSquare(float center[3], float size, float color[4]) { //周围6个正方体的中心 float halfcenter[6][3] = { center[0] + size * 3/4.0, center[1], center[2], center[0] - size * 3/4.0, center[1], center[2], center[0] , center[1] + size * 3/4.0, center[2], center[0] , center[1] - size * 3/4.0, center[2], center[0] , center[1], center[2] + size * 3/4.0, center[0] , center[1], center[2] - size * 3/4.0, }; for (int i=0; i<6; i++) { Square(halfcenter[i], size/2, color); } } void Iteration(float center[3], float size, int nIter) { if(nIter<0)return; SurroundSquare(center, size, colortab[nIter]); float halfcenter[6][3] = { center[0] + size * 3/4.0, center[1], center[2], center[0] - size * 3/4.0, center[1], center[2], center[0] , center[1] + size * 3/4.0, center[2], center[0] , center[1] - size * 3/4.0, center[2], center[0] , center[1], center[2] + size * 3/4.0, center[0] , center[1], center[2] - size * 3/4.0, }; for (int i=0; i<6; i++) { Iteration(halfcenter[i], size/2, nIter-1); } } void display(void) { //清除颜色缓存和深度缓存 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1.0,1.0,1.0); glLoadIdentity(); gluLookAt(1, -1, 3, 0, 0, 0, 0.4, -3, 0.5); //视点转换 float center[] = {0.0f, 0.0f, 0.0f}; float radius = 2; float color[] = {1.0, 0.1, 0.0, 0}; Square(center, radius, color); Iteration(center, radius, 6); glutSwapBuffers(); //交换双缓存 } void reshape(int width,int height) { glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-3.0, 3.0, -3.0 * height / width, 3.0* height / width, -5, 5); //为了不变形,则要长和宽成比例 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc,char** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //使用双缓存模式和深度缓存 glutInitWindowSize(600,600); glutInitWindowPosition(200,200); glutCreateWindow("三维分形"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(display); //设置空闲时用的函数 glutMainLoop(); return 0; }