WebGL自由表面流——续

2012.12.11

前一篇文章中提过,主要参考论文Animating sand as a fluid的作者提供了源码。不过我当时并没有编译运行这个代码。近来把它仔细看了看,发现它的计算和可视化是分为两个程序。论文是05年的,可能当时的计算机的能力还不足以实时计算,所以如此。我把两部分合到一起,凑成一个程序,其他方面则尽量保持不变,方便自己学习。这是个命令行程序,可接受两个整数作为参数:第一个指定每个方向的网格数,默认是50,第二个指定一秒钟分为多少帧,默认是30。程序中可用四个方向键控制:右前进一帧,左回到开始,上下选择不同的初始情况(共八种)。虽然代码的名字叫simple_flip2d,使用的粒子更新方式是PIC的,粘性较大,液体的运动显得较稳,视觉效果挺好。程序运行很快,不过在网格较细时也不能实时得到结果。而令我困扰的液体体积减少问题,这个程序也存在。不过这个程序中粘性较大,液体很快就会停下来,不像我的晃啊晃,体积一直减少。

如果只是这样,本文也有点无聊。Emscripten是一个从LLVM代码生成JavaScript的编译器,可以帮助把C/C++之类语言写的程序移植到浏览器上(除了用在浏览器中,JavaScript近来又多了一种使用方式:Node.js,不过Emscripten目前好像主要是针对浏览器环境)。Emscripten的基本使用是很简单的。有一个名为emcc的Python脚本,它有着和gcc类似的用法。不过实际用起来还是很容易遇到问题,主要是因为Emscripten还是个开发中的项目,而介绍文档则写得不很详细。还好,不需改写什么,我的修改版simple_flip2d就可以变成可在浏览器中运行的页面。目前Emscripten对OpenGL和GLUT的支持还不完全,而由于运行在浏览器中毕竟不同于直接运行于机器上,所以可能也无法做到完全支持。因此我在Emscripten版中去掉了GLUT窗口中显示的文字和按钮。此外Emscripten版和本地二进制版的运行结果也有小的差别,其原因我还未仔细分析。生成页面在浏览器中运行的速度约为二进制程序的速度的1/4,这和Emscripten所声称的速度是相符的。

我还想把事情弄得更复杂些。前一篇文章中说整个过程中计算量最大的部分是压力场泊松方程的求解,对于我写的WebGL仿真是这样,但在原代码中并非如此。耗时较多的两项是粒子的移动和速度场的外插,而压力计算与这两者相比耗时少多了。因为这个代码中的时间步长是根据速度自动调整的,每一步中液体的移动距离是有限的,速度场的外插实际只需在此距离内进行就可以了。如此一来,便可省去不少时间。而泊松方程的求解,在计算机图形学的邻域内似乎都用迭代法,可工程软件(CAE)在内存允许的情况下却主要用直接法。所谓直接法,对正定对称阵就是用柯列斯基分解,本例正是此一情形。代码中原也用到了不完全柯列斯基分解。不完全,即是未用柯列斯基分解所得矩阵的全部非零元素。因柯列斯基分解所得矩阵非零元素一般大大多于原矩阵,如全部计算并使用,则计算量和内存占用均大增。所以原代码只用不完全柯列斯基分解来做迭代法的预条件。但如果适当对原矩阵进行置换,也就是调整未知数的顺序,可减少所得矩阵非零元素数量,这被称为减少注入元(fill-reducing)。具体方法有多种,我采用的是AMD。再配合一个简单的柯列斯基分解代码LDL。从运行效果看,速度有明显提升,而内存占用我还不知怎么计算。因为这一步骤本身在计算时间中占的比例较小,所以对整体提升不明显。经此两处改进,编译的二进制文件的运行时间约为原来的70%,而编译的HTML文件的运行时间约为原来的一半。也即Emscripten生成的文件有原代码直接编译所得程序一半的速度。此外应该还有优化的空间,当然原来的代码只是做演示之用,并未在意速度。

是可执行程序,是源代码。将grid.cpp中第7,8行的宏定义取消,便可得无改进的版本。而前面给出的Emscripten生成的页面是改进后的版本。Emscripten生成的页面也支持和可执行程序类似的参数,在URL后加?50&30便是默认的参数。可以把50和30改动看看不同的效果。但是如果前一参数太大会使粒子数过多,页面会报错。虽在Emscripten编译时增大TOTAL_MEMORY值可以容许更多的粒子,可这一值调到太大也会出错。

更新两点:1. 我此文和前文中所说的体积减少的问题很有可能是由于算法不完整。在每一步中都应检查标为液体的网格中粒子的数量,过多要删去一些,过少则要生成补上。此处作者提供的代码文件名为simpleflip2d,既然是simple,已有简化。而前文中的WebGL版本要实现这一步骤应是不可能的。如果对流体有兴趣,可以看看更完整的代码,如mantaflow

2. 此处我尝试使用直接法解泊松方程,主要是拓展思路。在二维的情形,还是不错的选择。若是在三维,我并未实验,但理论上复杂度阶次有点高。对n个未知数,复杂度在二维是O(n3/2),在三维是O(n2)。

附注:LDL是个简单的柯列斯基分解代码,但却有个问题却困扰了我很久,记在这里,也许对别人也有帮助:LDL针对的是对称阵,在不用置换时,只需提供矩阵的上三角部分就可以了,但如果用置换,则一定要提供整个矩阵,否则虽能计算,但结果是错误的。

补:Firefox17真的有问题,显示的粒子是彩色的,可能有人会觉得很好看,但它们应该全是白色的。