mandelbrot集可视化器中的色带

问题描述 投票:0回答:1

我有两个版本的mandelbrot展示台。一个在CPU上计算,另一个在GPU上计算。对于两个版本,我基本上使用了相同的代码,但是它们的行为有所不同。

这里是CPU版本(以CPU计算,使用OpenGL绘制):CPU version

这里是GPU版本(使用Vulkan在片段着色器中计算):enter image description here

这是CPU版本的代码的重要位:

首先,根据复数坐标计算一些值:

double Renderer::mandelbrot(std::complex<double> c, int power) {
    std::complex<double> z = {0.0, 0.0};

    //limit is the number of iterations
    for (int i = 0; i < limit; ++i) {
        z = std::pow(z, power) + c;
        if (std::norm(z) > 4) {
            for (int j = 0; j < 3; ++j) {
                z = std::pow(z, power) + c;
            }

            //LOG2INV = 1/log(2) -> #define LOG2INV 1.44269504089
            double mu = i + 1 - std::log(std::log(std::abs(z))) * LOG2INV;

            // If this is enabled, it won't repeat colours, this was disabled in the image.
            if (m_state.m_smoother) {
                mu = mu * m_state.m_viewports.back().limitInv * m_colours.size();
            }
            return mu;
        }
    }

    return -1;
}

第二,基于该值,为其着色:

glm::vec3 Renderer::colourScheme(double divergence) {
    int clr1 = divergence;
    float t2 = divergence - clr1;
    float t1 = 1 - t2;
    clr1 = clr1 % m_colours.size();
    int clr2 = (clr1 + 1) % m_colours.size();

    return m_colours[clr1] * t1 + m_colours[clr2] * t2;
}

[m_colours是具有12种不同颜色的glm::vec3数组。

至于GPU版本,这是完整的片段着色器:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(push_constant) uniform PushConstants {
    dvec2 offset;
    dvec2 scale;
    uint limit;
    double limitInv;
    bool smoothGradient;
} fc;

//This will give coordinates of the pixel on the screen scaled to 1. 
layout (location = 0) in vec2 outUV;

layout(location = 0) out vec4 outColor;

//Same values are used for the CPU version
const vec3 colours[12] = vec3[] (
    vec3(1.0, 0.0, 0.0),
    vec3(0.5, 0.0, 0.0),
    vec3(1.0, 1.0, 0.0),
    vec3(0.5, 0.5, 0.0),

    vec3(0.0, 1.0, 0.0),
    vec3(0.0, 0.5, 0.0),
    vec3(0.0, 1.0, 1.0),
    vec3(0.0, 0.5, 0.5),

    vec3(0.0, 0.0, 1.0),
    vec3(0.0, 0.0, 0.5),
    vec3(1.0, 0.0, 1.0),
    vec3(0.5, 0.0, 0.5)
);

dvec2 square(dvec2 z) {
    return dvec2(z.x * z.x - z.y * z.y, 2 * z.x * z.y);
}

double norm(dvec2 z) {
    return z.x * z.x + z.y * z.y;
}

double module(dvec2 z) {
    return sqrt(z.x * z.x + z.y * z.y);
}

double mandelbrot(dvec2 c) {
    dvec2 z = dvec2(0.0);
    for (int i = 0; i < fc.limit; ++i) {
        z = square(z) + c;
        if (norm(z) > 4) {
            for (int j = 0; j < 3; ++j) {
                z = square(z) + c;
            }

            double mu = i + 1.0 - log(log(module(z))) * 1.44269504089;
            if (fc.smoothGradient) {
                mu = mu * fc.limitInv * colours.length();
            }

            return mu;
        }
    }

    return -1;
}

void main() {
    // Determines zoom level, also deals with the aspect ratio.
    dmat2 scale = dmat2(fc.scale.x, 0.0,
                        0.0, fc.scale.y);

    // Center the image, scales it and then offsets it.
    dvec2 c = (outUV.xy - dvec2(0.5)) * scale + fc.offset;

    double mu = mandelbrot(c);

    if (mu == -1) {
        outColor = vec4(vec3(0.0), 1.0);
    } else {
        int clr1 = int(mu);
        float t2 = float(mu - clr1);
        float t1 = 1 - t2;
        clr1 = clr1 % colours.length();
        int clr2 = (clr1 + 1) % colours.length();

        outColor = vec4(vec3(colours[clr1] * t1 + colours[clr2] * t2), 1.0);
    }
}

您可以看到坐标变换得很好(或者在两种情况下至少是相同的方式),但是颜色的作用不同。我并不在乎它们的顺序,我只是希望GPU颜色具有平滑的渐变,与CPU版本相同。我猜测问题是某些值被限制在GPU上的某个位置,但我无法确定位置。

graphics fragment-shader vulkan fractals mandelbrot
1个回答
0
投票

我尚未完全确认这一点,但我想我知道问题出在哪里。

CPU版本会遍历每个屏幕坐标,然后将其转换为复数坐标(一对双精度),以进行进一步的计算。这样就可以了。

另一方面,GPU绘制与屏幕重叠的单个三角形,并通过outUV变量将标准化后的空间坐标发送到片段着色器。然后将其插值并转换为复数坐标。但是,插值是在float变量上完成的,因为那里不支持double。我认为这种精度损失会在迭代过程中传播并最终导致问题。

我不确定是否要回到程序中进行检查,如果可以,我将编辑答案,但是如果有人遇到类似的问题,可以从此开始。

© www.soinside.com 2019 - 2024. All rights reserved.