基于网格的流体模拟错误
Grid based fluid simulation errors
所以我正在编写基于 http://jamie-wong.com/2016/08/05/webgl-fluid-simulation/ 的流体模拟。
这是在 C++ 中。
class Grid
只是一个 class,具有以下内容:
Grid.data[][] : float[][] - floating point data
Grid.copy(): Grid - creates a new grid with identical elements
Grid.set(int x,int y, float f): void - sets the data at (x,y)
Grid.operator() (int x,int y): float - gets the data at (x,y)
这是一个单独的 class 的原因是网格 class 可以处理环绕坐标(我对边界条件太懒了)。
平流:
void advect(Grid &from, Grid &to, Grid &vx, Grid &vy);
我确信平流函数工作得很好。
这些是压力函数和背离函数:
void calcDivergence(Grid &density, Grid &divergence, Grid &vx, Grid &vy){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
divergence.set(x,y, (-2/dt)*(vx(x+1,y)-vx(x-1,y)+vy(x,y+1)-vy(x,y-1)) );
}
}
}
void solvePressure(Grid &divergence, Grid &pressure){
pressure.init();//set to 0
Grid pressure0=pressure.copy();
for(int i=0;i<PRESSURE_SOLVE_ITERS;i++){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
pressure.set(x,y, (divergence(x,y)+pressure0(x-1,y)+pressure0(x+1,y)+pressure0(x,y-1)+pressure0(x,y+1))/4.0f );
}
}
pressure0=pressure.copy();
}
}
void fixDivergence(Grid &pressure, Grid&vx, Grid &vy){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
float gradX=dt*0.5*(pressure(x+1,y)-pressure(x-1,y));
float gradY=dt*0.5*(pressure(x,y+1)-pressure(x,y-1));
// printf("%f,%f\n",gradX,gradY);//why is this zero? solvePressure must be broken, check swap code
vx.set(x,y, vx(x,y)-gradX);
vy.set(x,y, vy(x,y)-gradY);
}
}
}
并确定压力求解器的精度:
float pressureAccuracy(Grid &density, Grid &pressure){
float f=0;
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
float realValue=4*pressure(x,y)-pressure(x-1,y)-pressure(x+1,y)-pressure(x,y-1)-pressure(x,y+1);
f+=abs(realValue-density(x,y));
}
}
return f/GRIDSIZE/GRIDSIZE;
}
最后,更新功能:
void update(Grid &vx, Grid &vy, Grid &density, Grid &pressure, Grid &divergence){
Grid vx0=vx.copy();
Grid vy0=vy.copy();
advect(vx0,vx,vx0,vy0);
advect(vy0,vy,vx0,vy0);
calcDivergence(density, divergence, vx, vy);
solvePressure(divergence, pressure);
fixDivergence(pressure, vx, vy);
Grid density0=density.copy();
advect(density0,density,vx,vy);
}
网格初始化如下:
vx[x][y]=cos(4pi*y/GRIDSIZE)
vy[x][y]=sin(4pi*x/GRIDSIZE)
pressure[x][y]=0
density[x][y]=1 if (x/100)%2+(y/100)%2==1, otherwise 0
divergence[x][y]=0
GRIDSIZE 为 200。
出了什么问题?
编辑 1:
澄清:当你看到形状奇特的密度等值线时,它们会随机振荡但永远保持在同一位置。
编辑 2:
过了一会儿,网格平均密度均匀并且没有振荡。
不应在 2 个单位外对压力进行采样,而应在 solvePressure
内仅在 1 个样本外进行采样。这可以防止相邻迭代发生振荡。
pressureAccuracy
应该通过散度,而不是密度。
以上所有内容对我来说都完美解决了。
所以我正在编写基于 http://jamie-wong.com/2016/08/05/webgl-fluid-simulation/ 的流体模拟。
这是在 C++ 中。
class Grid
只是一个 class,具有以下内容:
Grid.data[][] : float[][] - floating point data
Grid.copy(): Grid - creates a new grid with identical elements
Grid.set(int x,int y, float f): void - sets the data at (x,y)
Grid.operator() (int x,int y): float - gets the data at (x,y)
这是一个单独的 class 的原因是网格 class 可以处理环绕坐标(我对边界条件太懒了)。
平流:
void advect(Grid &from, Grid &to, Grid &vx, Grid &vy);
我确信平流函数工作得很好。
这些是压力函数和背离函数:
void calcDivergence(Grid &density, Grid &divergence, Grid &vx, Grid &vy){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
divergence.set(x,y, (-2/dt)*(vx(x+1,y)-vx(x-1,y)+vy(x,y+1)-vy(x,y-1)) );
}
}
}
void solvePressure(Grid &divergence, Grid &pressure){
pressure.init();//set to 0
Grid pressure0=pressure.copy();
for(int i=0;i<PRESSURE_SOLVE_ITERS;i++){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
pressure.set(x,y, (divergence(x,y)+pressure0(x-1,y)+pressure0(x+1,y)+pressure0(x,y-1)+pressure0(x,y+1))/4.0f );
}
}
pressure0=pressure.copy();
}
}
void fixDivergence(Grid &pressure, Grid&vx, Grid &vy){
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
float gradX=dt*0.5*(pressure(x+1,y)-pressure(x-1,y));
float gradY=dt*0.5*(pressure(x,y+1)-pressure(x,y-1));
// printf("%f,%f\n",gradX,gradY);//why is this zero? solvePressure must be broken, check swap code
vx.set(x,y, vx(x,y)-gradX);
vy.set(x,y, vy(x,y)-gradY);
}
}
}
并确定压力求解器的精度:
float pressureAccuracy(Grid &density, Grid &pressure){
float f=0;
for(int x=0;x<GRIDSIZE;x++){
for(int y=0;y<GRIDSIZE;y++){
float realValue=4*pressure(x,y)-pressure(x-1,y)-pressure(x+1,y)-pressure(x,y-1)-pressure(x,y+1);
f+=abs(realValue-density(x,y));
}
}
return f/GRIDSIZE/GRIDSIZE;
}
最后,更新功能:
void update(Grid &vx, Grid &vy, Grid &density, Grid &pressure, Grid &divergence){
Grid vx0=vx.copy();
Grid vy0=vy.copy();
advect(vx0,vx,vx0,vy0);
advect(vy0,vy,vx0,vy0);
calcDivergence(density, divergence, vx, vy);
solvePressure(divergence, pressure);
fixDivergence(pressure, vx, vy);
Grid density0=density.copy();
advect(density0,density,vx,vy);
}
网格初始化如下:
vx[x][y]=cos(4pi*y/GRIDSIZE)
vy[x][y]=sin(4pi*x/GRIDSIZE)
pressure[x][y]=0
density[x][y]=1 if (x/100)%2+(y/100)%2==1, otherwise 0
divergence[x][y]=0
GRIDSIZE 为 200。
出了什么问题?
编辑 1: 澄清:当你看到形状奇特的密度等值线时,它们会随机振荡但永远保持在同一位置。
编辑 2: 过了一会儿,网格平均密度均匀并且没有振荡。
不应在 2 个单位外对压力进行采样,而应在 solvePressure
内仅在 1 个样本外进行采样。这可以防止相邻迭代发生振荡。
pressureAccuracy
应该通过散度,而不是密度。
以上所有内容对我来说都完美解决了。