[OpenGL] 随机分形地形生成

来源:互联网 发布:汽车销售网络平台 编辑:程序博客网 时间:2024/05/21 11:47

reference : 《随机分形地形生成及其浏览》

* 本项目属于游戏程序设计5000行计划


        算法实现起来很简单,分形也很有意思,我们假设我们越靠近一个物体,它的细节就会显示出来,那么对于具有分形特性的物体而言,我们看到的细节和整体并没有什么区别。正如我们不断zoom in观察这个 Koch 雪花,每个突出的角又能长出三个角,如此递归,永无止境……

       

        地形也有这样的特性,对于两张不同尺度上的地形俯拍图,我们看到的细节也是类似的,也就是说,对于下图中的蓝色区域的地面,我们截取两个不同大小的橙色框所得到的图片,在人眼看来是差不多的。

        

        利用这样的特性,我们就可以用分形的思想来生成随机地形。

        具体的算法在开头给出的论文已经说的非常明白了,虽然这个论文并不是该算法(diamand - square algorithm)的最早版本,但中文看起来比英文要好理解一些。在这里只给出一些概述。


        一维分形地形


       类似于二分法,取线段中点,使y值为线段两段的平均值加上一个随机数,得到两条线段,再分别取两个线段的中点,重复前面的过程。


       二维分形地形

       

        由一维扩展而成,利用已知的a,b,c,d高度值,计算e,h,f,g, 设r为随机数(r随着迭代的次数,取值范围不断缩小)其中

        h(e) = (h(a) + h(b)) / 2 + r(e)

        h(f) = (h(b) + h(c)) / 2 + r(f)

        h(g) = (h(c) + h(d)) / 2 + r(g)

        h(h) = (h(a) + h(d)) / 2 + r(h)

        然后利用e,f,g,h,计算m

        h(m) = (h(e) + h(f) + h(g) + h(h))/4 + r(m)

        如此得到一次递归流程,然后再对四个正方形进行同样的操作……


        Diamand - Square 算法


       同样适用于二维,但是对上述算法的改进。

       

        上图反映的是两次递归过程,以下只描述一次递归中做的事情。

         先计算h(E) = (h(A) + h(B) + h(C) + h(D)) / 4 + r(E)

         再计算:

         h(F) = (h(A) + h(B) + h(E) + h(E)) / 4 + r(F)

         h(I) = (h(A) + h(C) + h(E) + h(E)) / 4 + r(I)

         h(S) = (h(B) + h(D) + h(E) + h(E)) / 4 + r(S)

         h(H) = (h(C) + h(D) + h(E) + h(E)) / 4 + r(H)

         然后再对新的四个正方形做同样的操作。


fractal.h

#pragma onceclass fractal{private:enum { MAX = 10000 };float roughness;int times;int height;float minHeight;float maxHeight;int n;int D;int indiceNum;float** data;float* vertex;int* indice;float step;void genIndice();void chooseColor(float height);public:void draw();void calculate();fractal(int _height, int _times, float _step, int D,int seed);};

fractal.cpp

#include <time.h>  #include <math.h>  #include <stdlib.h>  #include <iostream>#include "fractal.h"  #include "gl/glut.h"using namespace std;// height : 值越大,地形起伏越大 // times  : 迭代次数,次数越多,tire越多// step   : 绘制时一个网格的程度// D      : 随机数衰减因子,越大随机数随迭代的取值越小// seed   : 随机数种子fractal::fractal(int _height, int _times, float _step, int _D,int seed){srand(seed);step = _step;times = _times;height = _height;D = _D;n = pow(2, times) + 1;indiceNum = 6 * (n - 1)*(n - 1);vertex = new float[3 * n*n];indice = new int[indiceNum];data = new float*[n];for (int i = 0; i < n; i++) {data[i] = new float[n];for (int j = 0; j < n; j++) {data[i][j] = 0;}}}// 生成顶点索引数据void fractal::genIndice(){for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - 1; j++) {indice[3 * ((n - 1) * i + j)] = (n * i + j);indice[3 * ((n - 1) * i + j) + 1] = (n * i + j + 1);indice[3 * ((n - 1) * i + j) + 2] = (n * (i + 1) + j + 1);}}cout << endl;int off = 3 * (n - 1)*(n - 1);for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - 1; j++) {indice[off + 3 * ((n - 1) * i + j)] = (n * i + j);indice[off + 3 * ((n - 1) * i + j) + 1] = (n * (i + 1) + j);indice[off + 3 * ((n - 1)* i + j) + 2] = (n * (i + 1) + j + 1);}}}// 生成[-num,num]之间的随机数static float randnum(float num){float max = num;float min = -num;int r;floatx;r = rand();x = (float)(r & 0x7fff) /(float)0x7fff;return (x * (max - min) + min);}// 计算顶点高度void fractal::calculate(){int size = n - 1;int number = 1;int ratio = pow(2, D);roughness = height;//times为迭代次数for (int t = 0; t < times; t++) {// diamand阶段for (int i = 0; i < number; i++) {for (int j = 0; j < number; j++) {float r = randnum(.5) * roughness;int center_x = (size >> 1) + size * i;int center_y = (size >> 1) + size * j;data[center_x][center_y] =(data[size * i][size * j]+ data[size*i + size][size * j]+ data[size * i][size * j + size]+ data[size * i + size][size * j + size]) / 4 + r;}}// square阶段int pointNum = ((t + 1) << 1) + 1;int pointStep = (n - 1) / (pointNum - 1);for (int i = 0; i < pointNum; i++) {for (int j = 0; j < pointNum; j++) {if ((i + j) % 2 == 1){float r = randnum(.5) * roughness;if (i == 0){data[i*pointStep][j*pointStep] =(data[n - pointStep][j*pointStep] +data[(i + 1)*pointStep][j*pointStep] +data[i*pointStep][(j + 1)*pointStep] +data[i*pointStep][(j-1)*pointStep]) / 4 + r;}else if (j == 0){data[i*pointStep][j*pointStep] =(data[(i-1)*pointStep][j*pointStep] +data[(i + 1)*pointStep][j*pointStep] +data[i*pointStep][(j + 1)*pointStep] +data[i*pointStep][n - pointStep]) / 4 + r;}else if (i == pointNum - 1){data[i*pointStep][j*pointStep] =(data[pointStep][j*pointStep] +data[(i - 1)*pointStep][j*pointStep] +data[i*pointStep][(j + 1)*pointStep] +data[i*pointStep][(j - 1)*pointStep]) / 4 + r;}else if (j == pointNum - 1){data[i*pointStep][j*pointStep] =(data[(i-1)*pointStep][j*pointStep] +data[(i + 1)*pointStep][j*pointStep] +data[i*pointStep][pointStep] +data[i*pointStep][(j - 1)*pointStep]) / 4 + r;}else {data[i*pointStep][j*pointStep] =(data[(i - 1)*pointStep][j*pointStep] +data[(i + 1)*pointStep][j*pointStep] +data[i*pointStep][(j + 1)*pointStep] +data[i*pointStep][(j - 1)*pointStep]) / 4 + r;}}}}roughness = roughness / ratio;size >>= 1;number <<= 1;}// 把data映射到vertex数据上,并删除data,同时计算出最大高度和最小高度minHeight = 10000;maxHeight = -10000;for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {vertex[3 * (i*n + j)] = i* step - n*step / 2;vertex[3 * (i*n + j) + 1] = data[i][j];vertex[3 * (i*n + j) + 2] = j*step - n*step / 2;if (maxHeight < data[i][j]) {maxHeight = data[i][j];}if (minHeight > data[i][j]) {minHeight = data[i][j];}}delete[] data[i];}delete[] data;// 生成索引genIndice();}// 根据高度选择颜色void fractal::chooseColor(float height){const GLfloat blue[] = { 1.0 * 65 / 255, 1.0 * 127 / 255, 1.0 * 219 / 255 };const GLfloat green[] = { 1.0 * 73 / 255, 1.0 * 161 / 255, 1.0 * 101 / 255 };const GLfloat yellow[] = { 1.0 * 172 / 255, 1.0 * 189 / 255, 1.0 * 117 / 255 };const GLfloat brown[] = { 1.0 * 153 / 255, 1.0 * 123 / 255, 1.0 * 46 / 255 };float interval = maxHeight - minHeight;if (height < minHeight + interval / 4){glColor3fv(blue);}else if (height < minHeight + interval / 2){glColor3fv(green);}else if (height < minHeight + 3 * interval / 4){glColor3fv(yellow);}else if (height < maxHeight) {glColor3fv(brown);}}void fractal::draw(){glPushMatrix();glBegin(GL_TRIANGLES);for (int i = 0; i < indiceNum / 3; i++) {chooseColor(vertex[3 * indice[3 * i] + 1]);glVertex3f(vertex[3 * indice[3 * i]], vertex[3 * indice[3 * i] + 1], vertex[3 * indice[3 * i] + 2]);chooseColor(vertex[3 * indice[3 * i + 1] + 1]);glVertex3f(vertex[3 * indice[3 * i + 1]], vertex[3 * indice[3 * i + 1] + 1], vertex[3 * indice[3 * i + 1] + 2]);chooseColor(vertex[3 * indice[3 * i + 2] + 1]);glVertex3f(vertex[3 * indice[3 * i + 2]], vertex[3 * indice[3 * i + 2] + 1], vertex[3 * indice[3 * i + 2] + 2]);}glColor3f(1,1,1);glEnd();glPopMatrix();}

main.cpp

#define _CRT_SECURE_NO_WARNINGS      #include <stdlib.h>       #include<time.h>    #include"fractal.h"      #include"texture.h"  fractal* f;float center[] = { 0, 0, 0 };float eye[] = { 0, 0, 20 };float tx, ty = 10, ax, ay=10, mx, my, zoom = 0;bool isLine = false;bool isDown = false;void reshape(int width, int height){if (height == 0) {    height = 1;          }glViewport(0, 0, width, height);glMatrixMode(GL_PROJECTION);glLoadIdentity();float whRatio = (GLfloat)width / (GLfloat)height;gluPerspective(45, whRatio, 1, 500);glMatrixMode(GL_MODELVIEW);}void idle(){glutPostRedisplay();}void init(void){glClearColor(1.0, 0.0, 0.0, 0.0);glShadeModel(GL_SMOOTH);glEnable(GL_DEPTH_TEST);glColor4f(1.0, 1.0, 1.0, 1.0f);glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);// height : 值越大,地形起伏越大 // times  : 迭代次数,次数越多,tire越多// step   : 绘制时一个网格的程度// D      : 随机数衰减因子,越大随机数随迭代的取值越小(大于1)// seed   : 随机数种子f = new fractal(5, 3, 2, 2, 23);f->calculate();}void redraw(){glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     glClearColor(0, 0, 0, 1);glMatrixMode(GL_MODELVIEW);glLoadIdentity();        gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0);if(isLine)glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);glTranslatef(tx,ty,zoom);glRotatef(ax, 1.0f, 0.0f, 0.0f);glRotatef(ay, 0.0f, 1.0f, 0.0f);glPushMatrix();glTranslatef(0, -roomSizeY / 2,0);f->draw();glPopMatrix();glutSwapBuffers();}void myMouse(int button, int state, int x, int y){if (button == GLUT_LEFT_BUTTON) {if (state == GLUT_DOWN) {isDown = true;mx = x;my = y;}else if (state == GLUT_UP) {isDown = false;}}glutPostRedisplay();}void mouseMotion(int x, int y){if (isDown) {ax += 1.0f*(y - my) / 10.0f;ay += 1.0f*(x - mx) / 10.0f;mx = x;my = y;}glutPostRedisplay();}void myKeyboard(unsigned char key, int x, int y){switch (key){case 'a': { //左移    tx -= 1;  break;}case 'd': { //右移    tx += 1;  break;}case 'w': { //上移    ty += 1;  break;}case 's': { //下移    ty -= 1;  break;}case 'z': { //后移    zoom += 1;  break;}case 'c': { //前移    zoom -= 1;  break;}case 'p': {// 切换绘制模式if (isLine) {isLine = false;}else isLine = true;}}glutPostRedisplay();}int main(int argc, char *argv[]){glutInit(&argc, argv);       glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);            glutInitWindowSize(800, 600);int windowHandle = glutCreateWindow("Simple GLUT App");               glutDisplayFunc(redraw);             glutReshapeFunc(reshape);      glutMouseFunc(myMouse);glutMotionFunc(mouseMotion);glutKeyboardFunc(myKeyboard);glutIdleFunc(idle);          init();glutMainLoop();  system("pause");return 0;}



0 0