控制放大 Mandelbrot 集的位置

Controlling where to zoom in on the Mandelbrot set

我编写了一个简单的片段着色器来渲染 mandelbrot 集。我在 c 和 opengl 中使用 glsl 执行此操作。

#version 330 core
in vec2 fCoord; //position.x position.y which is -1 to 1 on both axis
uniform int maxIterations;
uniform sampler1D mandiTexture;

out vec4 color;

void main()
{
   vec2 c, z;

    c.x = fCoord.x;
    c.y = fCoord.y;

        int i;
        z = vec2(0.0f, 0.0f);
        for(i=0; i<maxIterations; i++) {
            float x = (z.x * z.x - z.y * z.y) + c.x;
            float y = (z.y * z.x + z.x * z.y) + c.y;

            if((x * x + y * y) > 4.0) break;
            z.x = x;
            z.y = y;
        }

    vec4 tcolor;

    if (i == maxIterations)
    {
       tcolor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
    }
       else
    {
       tcolor = texture(mandiTexture, float(i) / float(maxIterations));
    }
    color =  tcolor;
}

我注意到使用初始 z 值时我得到了一些不同的结果,但大多数结果都超出了我的四边形。当 z 为 0, 0 时,我得到了这个结果。

如您所见,左侧未在四边形上渲染。

c 值来自顶点着色器,所以我假设它在 x 轴和 y 轴上都为 -1 到 1,并在两者之间进行插值。

我的问题是:

我想如果我能理解如何放大场景,我就能弄清楚如何放大特定部分,但我不确定。

编辑,确保我的代码是

main.c

int maxIterations = 70;
int iterAmount = 1;

char* vshad, *fshad;

GLuint verticesBuffer, colorBuffer, vao, texCoordBuffer, indicesBuffer;
GLuint mandiTextureID, sp;

mat4_s vm, pm, opm, tm;
GLint viewMat = -1;
GLint projMat = -1;
GLint modelMat = -1;

GLint mandiTexture = -1;
GLint maxIterLoc = -1;



void initShaders(void)
{

    char* vertexShaderSource = getResource("vert.shad");
    char* fragmentShaderSource = getResource("frag.shad");

    vshad = readFile(vertexShaderSource);
    fshad = readFile(fragmentShaderSource);

    free(vertexShaderSource);
    free(fragmentShaderSource);
}

int run_game()
{
    current_utc_time(&start_time);
    while(game_running)
    {
        current_utc_time(&current_time);
        double frameTime = (diff(start_time,current_time).tv_sec + diff(start_time,current_time).tv_nsec) * .00000001;
        //printf("float time: %0.8f\n",frameTime);
        if ( frameTime > 0.25 )
        {
            frameTime = 0.25;
        }
        current_utc_time(&start_time);
        current_time = start_time;

        accumulator += frameTime;

        while ( accumulator >= dt )
        {
            accumulator -= dt;
            t += dt;
            //printf("fixed update dt: %0.8f\n",dt);
        }
        //render_state = currentState * alpha +  previousState * ( 1.0 - alpha );
        const double alpha = accumulator / dt;
        render();

        if(game_running < 1) { break; }
        while (SDL_PollEvent(&event))
        {
            switch (event.type) {
                case SDL_QUIT:
                    game_running = -1;
                    break;
                case SDL_KEYDOWN:
                    switch (event.key.keysym.sym)
                    {
                        case SDLK_ESCAPE:
                            game_running = -1;
                            break;
                    }
                    break;
            }
        }
    }
    return -1;
}

int main(int argc, char const *argv[]) {
    initShaders();

    mat4_identity(&vm);
    vec3_s eye    = {0, 0, 0};
    vec3_s center = {0, 0, -1};
    vec3_s up     = {0, 1, 0};

    mat4_lookAt(&vm, &eye, &center, &up);

    mat4_identity(&opm);
    mat4_ortho(&opm, 0, 200, 0, 200, 1, 100);

    mat4_identity(&tm);
    mat4_scalex(&tm, &tm, 100, 100, 0);
    mat4_translatex(&tm, &tm, 100.0f, 100.0f, -20);

    SDL_Surface* mandiSurface = loadPNG(getResource("mandi.png"));

    if(!mandiSurface) {
        printf("IMG_Load: %s\n", IMG_GetError());
        // handle error
    }

    GLenum Mode1 = GL_RGB;

    if(4 == mandiSurface->format->BytesPerPixel)
    {
        Mode1 = GL_RGBA;
        printf("mode change");
    }

    sp = getShaderProgram(vshad, fshad);
    r = newRenderable2d();

    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &verticesBuffer);
    glGenBuffers(1, &colorBuffer);
    glGenBuffers(1, &indicesBuffer);
    glGenBuffers(1, &texCoordBuffer);

    glBindVertexArray(vao); //bind vertex array buffer

    glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->vertices), r->vertices, GL_STATIC_DRAW);

    //bind n setup indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(r->indices), r->indices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind indices

    //bind n setup colors
    glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->colors), r->colors, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind colors

    //bind n setup texture coords
    glBindBuffer(GL_ARRAY_BUFFER, texCoordBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(r->texCoords), r->texCoords, GL_STATIC_DRAW);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind texture coords

    glBindVertexArray(0); //unbind vertex array buffer

    //mandi 1d texture
    glGenTextures(1, &mandiTextureID);
    glBindTexture(GL_TEXTURE_1D, mandiTextureID);

    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexImage1D(GL_TEXTURE_1D, 0, Mode1, mandiSurface->w, 0, Mode1, GL_UNSIGNED_BYTE, mandiSurface->pixels);
    glBindTexture(GL_TEXTURE_1D, 0);
    free(mandiSurface);


    while(run_game() >= 0);
    free(r);
    IMG_Quit();
    SDL_GL_DeleteContext(maincontext);
    SDL_DestroyWindow(window);
    return 0;
}

void render()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glUseProgram(sp);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_1D, mandiTextureID);//mandiTexture
    mandiTexture = getUniformLocation(sp, "mandiTexture");
    glUniform1i(mandiTexture, 0);


    glBindVertexArray(verticesBuffer);
    viewMat = getUniformLocation(sp, "viewMat");
    modelMat = getUniformLocation(sp, "modelMat");
    projMat = getUniformLocation(sp, "projMat");

    maxIterLoc = getUniformLocation(sp, "maxIterations");

    glUniformMatrix4fv(viewMat, 1, GL_FALSE, vm.m);
    glUniformMatrix4fv(projMat, 1, GL_FALSE, opm.m);
    glUniformMatrix4fv(modelMat, 1, GL_FALSE, tm.m);

    glUniform1i(maxIterLoc, maxIterations);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    SDL_GL_SwapWindow(window);
}

int init_sdl(int width, int height, char* title, double fps)
{

if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
  {
    SDL_Log("sdl failed to init");
    SDL_Quit();
    return -1;
  }

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);


  window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
  if(window == NULL)
  {
    SDL_Log("sdl failed to create window");
    SDL_Quit();
    return -1;
  }

  maincontext = SDL_GL_CreateContext(window);
  if(maincontext == NULL)
  {
    SDL_Log("sdl failed to create opengl context");
    SDL_Quit();
    return -1;
  }
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    SDL_GL_SetSwapInterval(1);


  return 1;
}

顶点着色器

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 icolor;
layout (location = 2) in vec2 vTexCoord;

uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;

out vec4 fcolor;
out vec2 fTexCoord;
out vec2 fCoord;

void main()
{
    gl_Position =  projMat * viewMat * modelMat * vec4(position, 1.0);

    fCoord = vec2(position);
    fTexCoord = vTexCoord;
    fcolor = vec4(icolor, 1.0f);
}

感谢@samgak,我能够解决我遇到的问题,现在我添加了一些 Mandelbrot 集的镜头。

The c value is coming from the vertex shader so i assume it goes for -1 to 1 on both x and y axis and being interpolated in between.

没错。

1) How can I center the image on the quad? I am not really sure of that.

您只需要缩小 1.5 倍并将其居中于 -0.5。 Mandelbrot 集的有趣部分在实轴上大致从 -2 延伸到 1,在虚轴上大致从 -i 延伸到 i:

2) How can I say zoom in on some in on the mandelbrot set and a follow up, lets say I want to zoom in on a specific part of the set? 2B) Let's say I click the screen and get the position in NDC?

放回之前版本中的 2 件制服:

uniform vec2 center;
uniform float scale;

声明变量以在您的 C 代码中保存这些值,并使用 glUniform2fglUniform1f 设置它们。要使集合居中,初始值应为 -0.5,中心为 0.0,比例为 1.5(较大的值缩小)。然后在你的片段着色器中,像这样应用它们:

c = (fCoord * scale) + center;

要单击屏幕并放大特定位置,请根据其在屏幕上的位置将鼠标位置转换为 -1,-1 和 1,1 之间的值,然后将上面的等式应用于它以找到您点击的位置。将其设置为新中心并将比例乘以小于 1 的值以放大给定数量。

3) If I set my max iterations higher the set seems to get really jaggy, is that normal behavior?

您发布的屏幕截图看起来不错。如果您在片段着色器中实现某种多重采样可能看起来会更好(例如,在循环中计算多个值并将它们加在一起,以便每个像素实际上是 2x2 或 4x4 像素块的平均值等)。

如果放大得足够远,最终您将 运行 达到 GPU 使用的浮点数精度的极限。