简体   繁体   English

在OpenGL中使用等离子分形生成地形

[英]Terrain generating using plasma fractal in OpenGL

I want to generate random terrain using plasma fractal. 我想使用等离子分形生成随机地形。 I was browsing Internet to find a solution which can help me in my particular problem but have not found anything. 我正在浏览Internet,以找到可以对我的特定问题有所帮助的解决方案,但没有发现任何问题。 In fact I generated simple plasma fractal which looks like this: 实际上,我生成了简单的等离子体分形,如下所示:

Here is the picture of the fractal: 这是分形的图片:

fractal http://i65.tinypic.com/2qa8lyo.jpg 分形http://i65.tinypic.com/2qa8lyo.jpg

The fractal is not perfect because there are seen squares but accepted by my teacher. 分形并不完美,因为存在正方形,但被我的老师接受。 The algorithm for generating such fractal was that given an input square I was dividing it to smaller ones by inserting four points in the middle of each square side and then one point in the center which lead to creating 4 new squares. 生成这种分形的算法是,给定一个输入正方形,我通过在每个正方形边的中间插入四个点,然后在中心插入一个点,将其划分为较小的正方形,从而创建4个新正方形。 Each newly generated vertex had its color value which is a float of range [0, 255] which was counted using special formula. 每个新生成的顶点都有其颜色值,该颜色值是范围为[0,255]的浮点,使用特殊公式对其进行计数。 I have now a task to generate random terrain using this fractal and knowing that height of each point is proportional to counted color value. 我现在有一个任务可以使用此分形生成随机地形,并且知道每个点的高度与所计算的颜色值成正比。 My problem is that I try to draw quads using generated coordinates of the squares corners but my quads are not connected (there are some places where two quads rims are on such different heights that a gap appears between them). 我的问题是,我尝试使用生成的角点坐标绘制四边形,但四边形未连接(在某些地方,两个四边形边缘的高度不同,因此它们之间会出现间隙)。

Here is the picture of my terrain: 这是我的地形图片:

terrain http://i64.tinypic.com/kt7pi.jpg 地形http://i64.tinypic.com/kt7pi.jpg

Here is my code for generating this terrain: 这是我生成此地形的代码:

#include <iostream>
#include "glut\glut.h"
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <fstream>

int window_width = 600, window_height = 600;
typedef float point2d[2]; 
struct vertex;
struct square;
std::vector<square> sq; // generated squares

// represents the vertex in 2 D 
struct vertex
{
    point2d pos;
    float c;
};

// represents square in 2D
struct square
{
    square() {}
    square(vertex x, vertex y, vertex z, vertex w) : a(x), b(y), c(z), d(w)
    {
        this->x = abs(y.pos[0] - x.pos[0]);
    }
    vertex a, b, c, d;
    float x; // length of the side of the square
};
// deklaracje funkcji glut:
void display_scene();
void reshape(GLsizei width, GLsizei height);
void key_pressed(unsigned char key, int x, int y);

// helper function used when counting color of
// newly generated verticle (point)
float W(float x)
{
    return (-1.0 / window_width)*x + (float)(1.0 / 2.0);
}

// the same as above but for middle point 
float Wc(float x)
{
    return ((-1.0 / 1200.0f)*x + (float)(1.0 / 2.0)) / 2;
}

// converts value of range [0.0, 255.0] to 
// the numeber of range [0.0, 1.0]
float rgb_to_float(float rgb)
{
    return (1.0f / 255.0f) * rgb;
}

// gets color for a vertex based on the newly created 
// square's side length x
float get_color(float c1, float c2, float x)
{
    int c_prim = (rand() % 256); // draw any number of range [0, 255]
    float w = W(x); // count helper function for given side length
    return (1 - 2 * w) * c_prim + c1*w + c2 * w; // color is the result of such equation
}

// similarly for the center point 
float get_middle_color(float c1, float c2, float c3, float c4, float x)
{
    int c_prim = rand() % 256;
    float w = Wc(x);

    return (1 - 4 * w)*c_prim + w*c1 + w*c2 + w*c3 + w*c4;
}

// each time the function is invoked five new points 
// are counted by which the current square is divided
// so that 4 new squares are created. Four points are
// in the middle length of the side of the square that is
// currently processed and the fifth is in the center of it
// and that brings 4 new squares. The action is repeated for 
// each square in the input vector sq. 
std::vector<square> divide_square(std::vector<square> sq)
{
    vertex c12, c23, c34, c41, cc; // newly generated points
    std::vector<square> new_squares; // newly created squares go there
    float x = sq[0].x / 2; // length of new squares is half of the length of the original one
    // for each square in input vector do the dividing operation
    for (int i = 0; i < sq.size(); i++)
    {
        // initializing new vertices on the sides of old square
        c12.pos[0] = sq[i].a.pos[0] + x; c12.pos[1] = sq[i].a.pos[1];
        c23.pos[0] = sq[i].b.pos[0]; c23.pos[1] = sq[i].b.pos[1] + x;
        c34.pos[0] = sq[i].d.pos[0] + x; c34.pos[1] = sq[i].d.pos[1];
        c41.pos[0] = sq[i].a.pos[0]; c41.pos[1] = sq[i].a.pos[1] + x;
        // ... and the center one:
        cc.pos[0] = c12.pos[0]; cc.pos[1] = c23.pos[1];
        // counting color based on above formulas
        c12.c = get_color(sq[i].a.c, sq[i].b.c, x); c23.c = get_color(sq[i].b.c, sq[i].c.c, x);
        c34.c = get_color(sq[i].c.c, sq[i].d.c, x); c41.c = get_color(sq[i].a.c, sq[i].d.c, x);
        cc.c = get_middle_color(sq[i].a.c, sq[i].b.c, sq[i].c.c, sq[i].d.c, x);
        // generating and adding four newly generated squares to the container of squares for further processing 
        square s1(sq[i].a, c12, cc, c41);
        square s2(c12, sq[i].b, c23, cc);
        square s3(cc, c23, sq[i].c, c34);
        square s4(c41, cc, c34, sq[i].d);
        new_squares.push_back(s1); new_squares.push_back(s2);
        new_squares.push_back(s3); new_squares.push_back(s4);
    }
    return new_squares;
}

// dynamic two-dimensional array representing matrix for storing all
// generated squares (this array should be ordered 
// in such way that each row "i" contains 256 squares
// which A vertex has Y coordinate equal to "i"
// for instance Map[3][0] should represent the first
// square which has A corner vertex coordinates like (0, 3)
square **Map = new square*[256];

// performing the dividing mechanism and filling up the 
// Map matrix
void foo()
{
    vertex a, b, c, d; // vertices of the entering square of size 256x256
    a.pos[0] = 0.0f; a.pos[1] = 0.0f;
    b.pos[0] = 256.0f; b.pos[1] = 0.0f;
    c.pos[0] = 256.0f; c.pos[1] = 256.0f;
    d.pos[0] = 0.0f; d.pos[1] = 256.0f;
    a.c = 0.5f; b.c = 0.5f; c.c = 0.5f; d.c = 0.5f;
    sq.push_back(square(a, b, c, d)); // adding it as the first the square to the container
    // while generated smaller squares have the x length more than 1.0 divide them on smaller ones
    while (sq[0].x > 1.0f)
    {
        sq = divide_square(sq);
    }
    int tempor = 0; // helper for iterating columns of Map matrix
    float curr_y; // represent the y-coordinate of left-upper square corner (A)
    for (int j = 0; j < 256; j++)
    {
        Map[j] = new square[256]; // new row of 256 squares is initialized
        // search all squares for finding those which left-upper corner (A)
        // y-coordinate is equal to the row numer
        for (int i = 0; i < sq.size(); i++)
        {
            curr_y = sq[i].a.pos[1];
            if (curr_y == j)
            {
                Map[j][tempor++] = sq[i];
            }
        }
        tempor = 0; // setting to first column again
    }
}

// helper global variables to set some properties
// for drawing and transforming which can be set
// by pressing some keys (they are set in key_pressed
// function)
double rot = 10.0; // rotation angle;
int rows = 1; // the variable to iterate rows of Map matrix
int columns = 10; // the variable to iterate columns of Map matrix

void key_pressed(unsigned char key, int x, int y)
{
    if (key == '>')
        glRotated(rot, 1.0, 1.0, 1.0);
    if (key == 'z')
        rows++;
    if (key == 'x')
        rows--;
    if (key == 't')
        glTranslated(-1.0, 0.0, 0.0); // translating to the left
    if (key == 's')
        columns += 40;
    display_scene();
}
int main()
{
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); // inicjalizacja bufora ramki: podwójne buforowanie, RGB
    glutInitWindowSize(window_width, window_height);
    glutCreateWindow("Terrain");

    glutDisplayFunc(display_scene); // przekazanie wskaźnika do funkcji wywoływanej przez GLUT przy wyświetlaniu
    glutReshapeFunc(reshape); // jw. ale przy zmianie wielkości okna
    glutKeyboardFunc(key_pressed);
    // invoking function to generate squares.
    foo();
    glutMainLoop();

    return 0;
}


void display_scene(){
    // setting background color
    glClearColor(0.4f, 0.4f, 0.4f, 1.0);
    // clearing buffer to draw new image
    glClear(GL_COLOR_BUFFER_BIT);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            // drawing quads in 3D where X and Z coordinates are just like
            // the square A, B, C or D vertices X and Y coordinates and 
            // the Y coordinate (height) depends on the color of the vertex
            // (the darker color the lower height)
            glBegin(GL_QUADS);
            float col = rgb_to_float(Map[i][j].a.c); 
            // color may be to brigth (for instance 0.0019) so some
            // scalling is done 
            if (col < 0.1)
                col *= 10;
            glColor3f(col, col, col); // seting color for drawing the verticle
            glVertex3f(Map[i][j].a.pos[0], col * 10, Map[i][j].a.pos[1]);
            col = rgb_to_float(Map[i][j].b.c);
            if (col < 0.1)
                col *= 10;
            glColor3f(col, col, col);
            glVertex3f(Map[i][j].b.pos[0], col*10, Map[i][j].b.pos[1]);
            col = rgb_to_float(Map[i][j].c.c);
            if (col < 0.1)
                col *= 10;
            glColor3f(col, col, col);
            glVertex3f(Map[i][j].c.pos[0], col*10, Map[i][j].c.pos[1]);
            col = rgb_to_float(Map[i][j].d.c);
            if (col < 0.1)
                col *= 10;
            glColor3f(col, col, col);
            glVertex3f(Map[i][j].d.pos[0], col*10, Map[i][j].d.pos[1]);
            glEnd();
        }
    }
    glFlush(); // powyższe polecenia zostaną przesłąne do sterownika karty graficznej (lepsza wydajność, bo naraz podaje się wszystkie dane, a nie każdą daną po kolei, co zajmowałoby więcej czasu)
    glutSwapBuffers();
}

void reshape(GLsizei width, GLsizei height){
    if (height == 0) // omitting diving by zero in counting AspectRatio
        height = 1;
    // setting view port the same as window size
    glViewport(0, 0, width, height);
    // switching to projection matrix for setting proper view aspects
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLfloat AspectRatio = (GLfloat)width / (GLfloat)height;
    if (width <= height)
        glOrtho(-7.5, 7.5, -7.5 / AspectRatio, 7.5 / AspectRatio, 10.0, -10.0);
    else
        glOrtho(-7.5*AspectRatio, 7.5*AspectRatio, -7.5, 7.5, 10.0, -10.0);
    // switching to modelview matrix to enable performing transformations on 
    // the image such as translating, rotating etc.
    glMatrixMode(GL_MODELVIEW);                                
    glLoadIdentity();
}

I am sorry for inserting whole my code but the error may be hidden in opengl parameters configuration so I decided to bring whole the code. 很抱歉,我插入了全部代码,但是该错误可能隐藏在opengl参数配置中,因此我决定带上全部代码。 I would like to know how to overcome the problem with disconnected (broken) quads. 我想知道如何解决四边形断开(断开)的问题。 Any help appreciated :) 任何帮助表示赞赏:)

Your "square" class is not an ideal model for calculating the heights in this mesh, because every time you subdivide you end up modifying points that are shared by neighbouring squares, and possibly also by the parent square. 您的“正方形”类不是用于计算此网格中高度的理想模型,因为每次细分时,最终都会修改相邻正方形甚至父正方形可能共享的点。

At the very least, have your squares contain references (or pointers) to the vertices, and share those references between adjacent squares. 至少,让正方形包含顶点的引用 (或指针),并在相邻正方形之间共享这些引用。

Then, when you modify any vertex then all squares that share it will automatically get the updated coordinates. 然后,当您修改任何顶点时,所有共享该正方形的正方形都会自动获取更新的坐标。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM