在 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、非标准扩展或直接在屏幕上渲染。
我正在尝试模拟 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、非标准扩展或直接在屏幕上渲染。