Homework2: OOP Taichi and Eulerian Fluid Engine Design (半成品)

1. 《 Fluid Engine Development 》

首先给想了解流体动画和引擎实现的大伙列上两本书吧 。

一本是胡老师力荐的《Fluid Simulation for Computer Graphics》, 略看了一下,粒子方法,网格方法等等各种模型的解释和推导都很清楚 。

第二本《Fluid Engine Development》是我偶尔在某个知乎问答里发现的。相比第一本各种前沿效果和模型的介绍,这本更加着重于使用C++进行工程实现,并且讨论了各种边界条件的细节。风格上算是跟第一本完全不同。

两本各有千秋,又相辅相成。有时间的话,能都看一遍那自然是坠吼了。

2. Homework Start

开始尽管看懂了模型的公式推导,但实操时又陷于各种具体的边界细节,迟迟未打出手。

想来还是自己的理解比较粗糙,所以最后决定抱起《 Fluid Engine Development 》啃完。使用Taichi执行计算,用python实现抽象,从头撸一个2D的流体引擎 !本作业就来证明,尽管taichi是Data Orient design,但完全也是可以OOP的!

放一下Github Repo。

代码目录还没来得及整合,各位看官见笑了。

3. 步子迈太大还是容易扯到蛋

啃完了几章,再来敲作业时间确实不太够了 。期间重新过了一遍公式和各种condition,两本书轮着翻了一周,代码修修改改又整了几个晚上。

截至目前该engine只实现了Eulerian Fluid的框架,并在该框架的基础上先行重构了作业1 :frowning:

在这之上补充实现了粘性计算和温度场、浮力控制,作为烟雾的模型应该算是完整了吧。

最后一晚肝了个障碍物的支持,目前还没有实现刚体物理,所以暂时只有静态的RigidBodyCollider (线速度和角速度为0)。为了支持Collider的表面操作,在 geometry.py 中实现 Surface 和 Implicit Surface ,并添加了一些常见的形状(Box 和 Ball )用以计算Collider的SDF。

基本上《 Fluid Engine Development 》的一、三章大部分内容都里面了,剩下的二、四章对应Partical和Hybrid Method目前还是TODO。

由于时间上快赶不上HW2 ddl了,就先放出来个寒碜一点的demo( 看看能不能混个杯子啥的

flow
H W 1 复 刻

上图的 example demo 暂时放在了 repo 的main.py里面作演示,待整理:

import taichi as ti
from smoke_solver import Smoke_Builder , FlowEmitter
from smoke_animation import Smoke_Animation
from colliders import RigidBodyCollier , Collider
from geometry import Box , Ball

res = (512 ,512)

ti.init(arch = ti.gpu , kernel_profiler = True)
gui = ti.GUI("smoke animation" , res = res)

smoke = \
    Smoke_Builder(res)  \
    .add_flow_emitter([512//2 , 0] , 512//3 , 2000.0)    \
    .set_decay(0.995)   \
    .build()

ani = Smoke_Animation(smoke ,res)
ani.reset()

# collider
smoke.add_collider(RigidBodyCollier(Box([156,156] , [226 , 276])))
smoke.add_collider(RigidBodyCollier(Ball([276,226] , 30)))

while gui.running:
    ani.update()
    ani.display(gui)

ti.kernel_profiler_print()

标题党一下 :30行代码实现Eulerian Fluid Solver!!!

4. Problems

在taichi 中实现工程抽象时,有很多点需要注意的:

  1. ti,fieldti.Vector.field 这些定义对taichi而言属于全局量 ,在Object中定义的话,必需要注意定义在taichi执行materialize之前 。这要求这些包含field的对象在程序开始时创建 ,无形中限制了很多对象在运行时的动态增删 。(或者taichi其实还有什么操作可以避免这个的?)

  2. 执行kernel时 ,每个不同的实参都会发生一次kernel的编译(同类型不同实例),这应该算是一个编译优化的feature ? 我一开始没注意使用了临时对象,导致某个kernel每一帧都编译了一次。。。

  3. 静态类型有一个好,大大减少了运行时的类型错误。只要设计的类型足够强,通过编译之后,只要数值不错,逻辑基本能保证正确。尽管 python 类型设计是动态的,但同时也内建了typing。因此我在代码里面加了许多本来不必要的hint ,一方面也可以辅助linting ,在编程时提供多一些提示,减少compile失败重新回头改代码的次数。

  4. Grid open boundary 的计算还有点搞不太清楚,再研究研究。

5. Todo

  • Partical , Eulerian 和 Hybrid Method 分类,本课程里介绍的方法慢慢会集成进去。
  • 线性系统solver的抽象
  • 出于实时性效果的考虑,目前只设计了2D。未来或许会重构成dimension free的设计扩展到3D?先画个饼 :stuck_out_tongue_winking_eye:

6. Over

cpp 写多了,上手python确实还不太习惯,加上对taichi的各点设计了解不够全面,导致编译kernel的过程比较痛苦,大部分的时间也花在了调试踩坑这上面_(:°з」∠)_

发一张调试地狱纪念本次GAMES 201课程 , 完结撒花 ?



胡老师和各位助教大牛们学业顺利 : )

6 个赞

太棒啦!!!! :grinning: