在 GLSL 中模拟扩散方程

Simulating diffusion equation in GLSL

我正在尝试模拟 glsl 中的扩散(不是 Gray Scott 反应-扩散方程),但我似乎无法让它正常工作。到目前为止,在我所有的测试中,扩散在某一点停止并在我预期之前很久就达到平衡。

我的 glsl 代码:

#version 460
#define KERNEL_SIZE 9

float kernel[KERNEL_SIZE];
vec2 offset[KERNEL_SIZE];

uniform float width;
uniform float height;
uniform sampler2D current_concentration_tex;
uniform float diffusion_constant; // rate of diffusion of U

in vec2 vTexCoord0;

out vec4 fragColor;

void main(void)
{
    float w         = 1.0/width;
    float h         = 1.0/height;

    kernel[0] = 0.707106781;
    kernel[1] = 1.0;
    kernel[2] = 0.707106781;
    kernel[3] = 1.0;
    kernel[4] = -6.82842712;
    kernel[5] = 1.0;
    kernel[6] = 0.707106781;
    kernel[7] = 1.0;
    kernel[8] = 0.707106781;

    offset[0] = vec2( -w, -h);
    offset[1] = vec2(0.0, -h);
    offset[2] = vec2(  w, -h);

    offset[3] = vec2( -w, 0.0);
    offset[4] = vec2(0.0, 0.0);
    offset[5] = vec2(  w, 0.0);

    offset[6] = vec2( -w, h);
    offset[7] = vec2(0.0, h);
    offset[8] = vec2(  w, h);

    float chemical_density = texture( current_concentration_tex, vTexCoord0 ).r; // reference texture from last frame

    float laplacian;

    for( int i=0; i<KERNEL_SIZE; i++ ){
        float tmp = texture( current_concentration_tex, vTexCoord0 + offset[i] ).r;
        laplacian += tmp * kernel[i];
    }

    float du = diffusion_constant * laplacian; // diffusion equation
    chemical_density += du; // diffuse; dt * du
    //chemical_density *= 0.9999; // decay

    fragColor = vec4( clamp(chemical_density, 0.0, 1.0 ), 0.0, 0.0, 1.0 );
}

如果扩散模拟正常工作,我希望在每一帧中绘制一个“源”,并让化学物质从源慢慢扩散到帧的其余部分。然而,扩散似乎提前“停止”并且不会进一步消退。例如,当我在中心画一个常量环作为我的来源时:

我也尝试过在中心使用化学物质的一次性初始条件,没有添加额外的化学物质——同样,我希望它会褪色到零(均匀分布在整个画面中),但相反它比我预期的更早停止:

我在glsl中的模拟代码有问题吗?或者这更像是一种数值方法问题?从 3x3 内核扩展到更大的内核会改善这种情况吗?

(从评论中移出)

这很可能是一个精度问题 -- 您是否将其渲染到 8 位目标中?您可能至少需要一个 32 位浮点数。

由于您使用的是现代 OpenGL,因此最好使用 glTextureStorage2D 创建纹理并为 internalformat 指定 GL_RGBA32F。没有“我的 GL 纹理的默认格式是 8 位 RGBA”这样的东西,除非您使用遗留 API、非标准扩展或直接在屏幕上渲染。