返回首页

Mandelbrot 动画:80 位 SSAA 8x8

Mandelbrot 分形动画生成器的实现使用 80 位算术、8×8 超采样和 OpenMP 创建高质量视频。两阶段管道优化计算,FFmpeg 提供专业编码。

80 位 Mandelbrot:带有 SSAA 8×8 和 OpenMP 的视频
Advertisement 728x90

使用 80 位精度和 SSAA 生成曼德勃罗集分形动画

该工具生成 255 帧 BMP 格式的曼德勃罗集图像,分辨率为 1920×1080,并将其编译为 30 FPS 的 MP4 视频。FFmpeg 集成自动化了整个流程:从分形计算到最终渲染。FFmpeg 参数经过优化以保留细节:qp=20 确保高质量,pix_fmt yuv420p 保证兼容性,deblock=-6 最小化压缩伪影。

关键命令选项:

  • -stream_loop 3:四次动画循环(基础 + 3 次重复)
  • -framerate 30:输入帧率
  • -color_range full:全亮度范围 0-255
  • no-psy:禁用心理视觉增强以保持分形结构的纯净度

热键和点坐标

程序支持从 8 个预设区域中选择集合,或从 Mandelbrot.txt 文件加载参数(键 9)。坐标设置为 80 位精度(long double):

Google AdInline article slot
case 1: absc = -0.5503432753421602L; ordi = -0.6259312704294012L; size_val = 0.0000000000004L; break;
case 2: absc = -0.691488093510181825L; ordi = -0.465680729473216972L; size_val = 0.000000000000003L; break;
case 3: absc = -0.550345905862346513L; ordi = 0.625931416301985337L; size_val = 0.000000000000005L; break;
case 4: absc = -1.78577278039667471L; ordi = -0.00000075696313293L; size_val = 0.000000000000004L; break;
case 5: absc = -1.785772754399825165L; ordi = -0.000000756806080773L; size_val = 0.0000000000000014L; break;
case 6: absc = -1.40353608594492038L; ordi = -0.02929181552009826L; size_val = 0.000000000000095L; break;
case 7: absc = -1.7485462508265219L; ordi = 0.000002213770706L; size_val = 0.00000000000029L; break;
case 8: absc = -1.94053809966024986L; ordi = -0.00000120260253359L; size_val = 0.00000000000003L; break;

80 位算术和 x87 FPU

实现使用 long double 来处理 10^18 的缩放范围(而 double 为 10^14)。对 x87 FPU 的硬件支持确保了最大的计算精度,无需软件模拟。这对于深入缩放迷你分形而不产生像素化至关重要。

80 位精度的优势:

  • +4 位十进制精度
  • 比标准实现深 10,000 倍的缩放
  • 直接使用 x87 寄存器,无需中间转换

使用 OpenMP 并行化

OpenMP 用于并行化迭代计算循环。#pragma omp parallel for schedule(dynamic) 指令有效地跨核心分配负载,确保从 4 核系统到 128 线程服务器的可扩展性。

Google AdInline article slot

8×8 超级采样(每像素 64 个样本)

SSAA 通过以超高分辨率 15360×8640(原始 1920×1080 的 8 倍)渲染来实现。每个最终像素聚合 64 个独立的分形样本,并进行单独的 RGB 分量计算。

平滑算法:

  • 以 80 位算术计算每个子像素的迭代次数
  • 使用调色板旋转独立着色子像素
  • 累积 R/G/B 强度(通道平均)
  • 下采样到目标分辨率

结果:无色噪,分形结构边缘具有电影级清晰度。

Google AdInline article slot

优化的帧生成

流程分为两个阶段以最小化计算:

阶段 1:迭代映射

  • 创建大小为 132 MB 的 uint8_t 数组(15360×8640)
  • 为整个动画执行一次 do-while 迭代循环
  • 使用 OpenMP 并行填充

阶段 2:渲染 255 帧

  • 从内存读取迭代映射
  • 为每一帧应用偏移调色板
  • 8×8 块的局部平均(不使用 long double)
  • 并行写入 BMP 文件

正弦调色板:127 + 127 cos(2π a / 255)127 + 127 sin(2π a / 255)

实现示例

#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <cstdint>
#include <string>
#include <atomic>
#include <omp.h>
#include <cstdio>
using namespace std;

const double PI = 3.14159265358979323846;

#pragma pack(push, 1)
struct BMPHeader {
    uint16_t type{0x4D42};
    uint32_t size{0};
    uint16_t reserved1{0};
    uint16_t reserved2{0};
    uint32_t offBits{54};
    uint32_t structSize{40};
    int32_t  width{0};
    int32_t  height{0};
    uint16_t planes{1};
    uint16_t bitCount{24};
    uint32_t compression{0};
    uint32_t sizeImage{0};
    int32_t  xpelsPerMeter{2834};
    int32_t  ypelsPerMeter{2834};
    uint32_t clrUsed{0};
    uint32_t clrImportant{0};
};
#pragma pack(pop)

void save_bmp(const string& filename, const vector<uint8_t>& data, int w, int h) {
    int rowSize = (w * 3 + 3) & ~3;
    BMPHeader header;
    header.width = w;
    header.height = h;
    header.sizeImage = rowSize * h;
    header.size = header.sizeImage + 54;
    ofstream f(filename, ios::binary);
    f.write(reinterpret_cast<char*>(&header), 54);
    f.write(reinterpret_cast<const char*>(data.data()), data.size());
    f.close();
}

关键要点

  • 80 位精度 long double + FPU x87:缩放高达 10^18 而无伪影
  • SSAA 8×8:每像素 64 个样本,完全消除噪点
  • 两阶段渲染:132 MB 迭代映射 + 快速颜色组装
  • OpenMP:具有动态调度的自动多线程
  • FFmpeg 集成:qp=20, no-psy, deblock=-6 以实现专业质量

— Editorial Team

Advertisement 728x90

继续阅读