1 Star 0 Fork 543

cchen55 / SIGer

forked from FlameAI / SIGer 
 / 详情

分形几何

待办的
拥有者
创建于  
2022-01-17 22:32

本篇issue的灵感来源于一次「计算机图形学」的作业。

开篇先来几张视觉冲击力强的实现效果图:

输入图片说明

输入图片说明

输入图片说明

重现步骤

·开发环境:win10 VS2019
·所需调用的OpenGL库:glut

温馨提示:新建项目时务必记得提前下载glut包、配置好环境!

主要代码来源于CSDN上的一位名叫iamttp的博主
原帖链接

#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <ctime>

static double myratio;  // angle绕y轴的旋转角,ratio窗口高宽比
static double x = 0.0f, y = 0.0f, z = 1.3f;  //相机位置
static double lx = 0.0f, ly = 0.0f, lz = -1.0f;  //视线方向,初始设为沿着Z轴负方向

const int WIDTH = 1000;
const int HEIGHT = 1000;

bool mouseDown = false;
double xrot = 0.0f, yrot = 0.0f;
double xdiff = 0.0f, ydiff = 0.0f;

/**
 * 定义观察方式
 */
void changeSize(int w, int h) {
    //除以0的情况
    if (h == 0) h = 1;
    myratio = 1.0f * w / h;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //设置视口为整个窗口大小
    glViewport(0, 0, w, h);
    //设置可视空间
    gluPerspective(45, myratio, 1, 1000);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

/**
 * 视野漫游函数
 */
void orientMe(double directionx, double directiony) {
    x += directionx * 0.1;
    y += directiony * 0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

/**
 * 视野漫游函数
 */
void moveMeFlat(int direction) {
    z += direction * (lz) * 0.1;
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}

// 鼠标右键stop
bool stop = false;

/**
 * 鼠标事件
*/
void mouse(int button, int state, int x, int y) {
    if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
        stop = !stop;
    }
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        mouseDown = true;
        xdiff = x - yrot;
        ydiff = -y + xrot;
    }
    else
        mouseDown = false;
}

/**
 * 鼠标移动事件
 */
void mouseMotion(int x, int y) {
    if (mouseDown) {
        yrot = x - xdiff;
        xrot = y + ydiff;
        glutPostRedisplay();
    }
}

/**
 * 加入按键控制
 */
double rateZoom = 1.5f;
const int N = 110, M = 1000;
struct col {
    GLushort r, g, b;
} colKTable[N];
struct pos {
    double x, y, z;
} posArr[HEIGHT * WIDTH];
col colArr[HEIGHT * WIDTH];
int index_list[HEIGHT * WIDTH];

struct complex {
    double i, j;

    inline complex mul(complex& b) {
        double tempI = i * b.i - j * b.j;
        double tempJ = i * b.j + j * b.i;
        i = tempI, j = tempJ;
        return *this;
    }

    inline complex add(complex& b) {
        i += b.i, j += b.j;
        return *this;
    }
};

complex alpha{ 0, 0 };

void myDisplay() {
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);

    // 实现鼠标旋转的核心
    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glVertexPointer(3, GL_DOUBLE, sizeof(pos), posArr);
    glColorPointer(3, GL_UNSIGNED_SHORT, sizeof(col), colArr);
    glDrawElements(GL_POINTS, HEIGHT * WIDTH, GL_UNSIGNED_INT, index_list);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glFlush();
    glutSwapBuffers();
}

double CalFrequency() {
    static int count;
    static double save;
    static clock_t last, current;
    double timegap;

    ++count;
    if (count <= 50)
        return save;
    count = 0;
    last = current;
    current = clock();
    timegap = (current - last) / (double)CLK_TCK;
    save = 50.0 / timegap;
    return save;
}

bool isGo = true;

void myIdle() {
    if (stop) return;
    double FPS = CalFrequency();
    printf("FPS = %f\n", FPS);
    double rate = 0.03;
    static double ii = -1, jj = -1;
    if (isGo) {
        static bool flag = true;
        if (flag) ii += rate;
        else ii -= rate;
        if (ii > 1) {
            jj += rate;
            flag = false;
        }
        if (ii < -1) {
            jj += rate;
            flag = true;
        }
        if (jj == 0) {
            ii += rate;
        }
    }
    complex C{ ii, jj };
    double d = rateZoom * 2 / HEIGHT;
#pragma omp parallel for
    for (int i = 0; i < HEIGHT; i++)
        for (int j = 0; j < WIDTH; j++) {
            auto& item = colArr[i * WIDTH + j];
            item.r = item.g = item.b = 0;
            complex X{ -rateZoom + i * d, -rateZoom + j * d }; // (-1.5,1.5)
            X = X.add(alpha);
            for (auto& k : colKTable) {
                X = X.mul(X).add(C);
                if (X.i * X.i + X.j * X.j > M) {
                    item.r = k.r;
                    item.g = k.g;
                    item.b = k.b;
                    break;
                }
            }
        }
    myDisplay();
}

void init() {
    for (int k = 0; k < N; k++) {
        int tempK = k * k;
        tempK = tempK * tempK;
        colKTable[k].r = tempK + tempK;
        colKTable[k].g = exp(k);
        colKTable[k].b = tempK * k;
    }

    for (int i = 0; i < HEIGHT; i++)
        for (int j = 0; j < WIDTH; j++) {
            posArr[i * WIDTH + j].x = -0.5 + (double)i / (HEIGHT); // (-1,1)
            posArr[i * WIDTH + j].y = -0.5 + (double)j / (WIDTH); // (1,-1)
            posArr[i * WIDTH + j].z = 0.1;
            index_list[i * WIDTH + j] = i * WIDTH + j;
        }
    glClearColor(0.93f, 0.93f, 0.93f, 0.0f);
}

void processSpecialKeys(int key, int x, int y) {
    bool temp;
    temp = stop;
    stop = false;
    isGo = false;
    // 越近加的越慢
    double addRate = 0.1 * std::abs(std::exp(rateZoom) - 1);
    switch (key) {
    case GLUT_KEY_UP:
        //            orientMe(0, 1);
        alpha.j += addRate;
        break;
    case GLUT_KEY_DOWN:
        //            orientMe(0, -1);
        alpha.j -= addRate;
        break;
    case GLUT_KEY_LEFT:
        //            orientMe(-1, 0);
        alpha.i -= addRate;
        break;
    case GLUT_KEY_RIGHT:
        //            orientMe(1, 0);
        alpha.i += addRate;
        break;
    case GLUT_KEY_PAGE_DOWN:
        //            moveMeFlat(-1);
        rateZoom = (rateZoom + addRate) > 2 ? 2 : rateZoom + addRate;
        break;
    case GLUT_KEY_PAGE_UP:
        //            moveMeFlat(1);
        rateZoom -= addRate;
        break;
    case GLUT_KEY_END:
        moveMeFlat(-1);
        break;
    case GLUT_KEY_HOME:
        moveMeFlat(1);
        break;
    default:
        break;
    }
    myIdle();
    stop = temp;
    isGo = true;
}

int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE);
    glutInitWindowPosition(300, 0);
    glutInitWindowSize(WIDTH, HEIGHT);
    glutCreateWindow("Demo");  // 改了窗口标题

    glutDisplayFunc(myDisplay);
    glutIdleFunc(myIdle);  // 表示在CPU空闲的时间调用某一函数
    glutSpecialFunc(processSpecialKeys);  // 按键
    glutReshapeFunc(changeSize);
    glutMouseFunc(mouse);
    glutMotionFunc(mouseMotion);

    init();
    glutMainLoop();
    return 0;
}

· 代码中包含的主要交互功能
鼠标右键 —— 暂停/继续
方向键 —— 移动
PGUP —— 放大
PGDN —— 缩小
HOIME —— 三维空间放大
END —— 三维空间缩小
鼠标左键 —— 移动三维空间
代码在这一块儿也有注释,「尝试改变迭代关系」可以呈现不同的动态效果,大家可以自己试试。

· 分形几何可以实现动态效果的原理
不同的像素坐标映射比例会决定分形图的大小。
不同的像素坐标映射加上偏移量会决定分形图的偏移。
根据这些原理,大家也可以「调整参数」,实现不一样的视觉效果。

· 其他
开源的代码基本可以实现预期的功能,但有一些小bug需要根据自己的开发环境作调整。
大家也可以根据自己的审美调整「色彩的搭配」。

评论 (11)

cchen55 创建了任务

这个内容,可以跟在, #I4R77Y:分形几何 帖子下面,汇集一定的资料后,我建议:
1, 将源码单独建立一个仓,比如叫分形几何 示范程序
2, 围绕这个示范程序库,制作一个详尽的 README,
3, 并依据这个 README, 制作围绕这个示范库的推介,包括作者介绍,同类项目和软件分析比对等。将会是非常棒的独立期刊。有库有宣传。

重现步骤

  1. 开发环境:win10 VS2019
  1. 所需调用的OpenGL库:glut
  1. 代码中包含的主要交互功能
    鼠标右键 —— 暂停/继续
    方向键 —— 移动
    PGUP —— 放大
    PGDN —— 缩小
    HOIME —— 三维空间放大
    END —— 三维空间缩小
    鼠标左键 —— 移动三维空间
    代码在这一块儿也有注释,「尝试改变迭代关系」可以呈现不同的动态效果,大家可以自己试试。
  1. 分形几何可以实现动态效果的原理
    不同的像素坐标映射比例会决定分形图的大小。
    不同的像素坐标映射加上偏移量会决定分形图的偏移。
    根据这些原理,大家也可以「调整参数」,实现不一样的视觉效果。
  1. 其他
    开源的代码基本可以实现预期的功能,但有一些小bug需要根据自己的开发环境作调整。
    大家也可以根据自己的审美调整「色彩的搭配」。
    温馨提示:新建项目时务必记得提前下载glut包、配置好环境!

https://gitee.com/cchen55/siger/tree/33b8afaa349c8b7885380993d90082f9c640a767/%E5%88%86%E5%BD%A2%E5%87%A0%E4%BD%95

单独建立目录,需要慎重,目前,以 sig 目录下为编委自荐目录。可以先在ISSUES 中充分讨论。再行动,避免过多 PR 被拒绝。

好的老师 :dizzy_face: ,我才看到哈哈

https://www.bilibili.com/video/BV12L4y1g7Uz
录屏

@cchen55 袁老师,前段时间因为我有点事情一直没有跟进分形几何的进度,现在忙完了,今天做了一个动画的视频,您看看效果怎么样

@cchen55 这是程序生成的,然后我录了一下屏

@袁德俊 相当漂亮啊,然后这个音乐配的也很激动人心哈,然后你把那个整个程序仓库然后建好,然后专题就出来,啊然后选一张最漂亮的这个瞬间啊,然后我坐这儿给你看好,就这样等你的这个仓库建立,你单独建一个仓库,然后要求是什么呢?你要写写好说明文档,就说你这个动画怎么产生啊,然后怎么调整能够变化呀。颜色、形状等等吧,能多写一些就能够让同学们DIY。回头我让其他的同学这个尝试一下,看看怎么能弄出不同的版本和花样儿啊,这样子你的任务就完成了,这是一个大任务哈,你申请那点儿小事儿都通过啊。

· 代码中包含的主要交互功能 :
鼠标右键 —— 暂停/继续
方向键 —— 移动
PGUP —— 放大
PGDN —— 缩小
HOIME —— 三维空间放大
END —— 三维空间缩小
鼠标左键 —— 移动三维空间
代码在这一块儿也有注释,「尝试改变迭代关系」可以呈现不同的动态效果,大家可以自己试试。

输入图片说明

背景图还有瑕疵,需要继续渲染!2480x3508 300DPI

@袁德俊

要用程序渲染
你需要查看下 OPENGL 画布尺寸如何修改
可能运算会变慢不少
但要求不能变。

@cchen55

原来如此 我没想到可以直接去改那个画布大小 一直想的是从本来的正方形里裁剪

@cchen55 已经完成。可以修改任务状态了。

登录 后才可以发表评论

状态
负责人
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
参与者(2)
5631341 yuandj 1624973227
JavaScript
1
https://gitee.com/cchen55/siger.git
git@gitee.com:cchen55/siger.git
cchen55
siger
SIGer

搜索帮助