一、Taichi设计目标

性能

生产力

空间稀疏计算

可微编程 --> 深度学习

元编程

二、设计决策

计算与数据结构解耦合

自动特定的编译器优化

Megakernels

实现两个尺度自动微分

嵌入进python

三、99行代码分析

https://zhuanlan.zhihu.com/p/97700605

https://www.bilibili.com/read/cv4358206?spm_id_from=333.788.b_636f6d6d656e74.55

1、基本数据类型

taichi库最常用到的数据类型是矩阵,ti.var、ti.Vector、ti.Matrix这三个函数生成的其实都是矩阵,别被名字误导

1.1、ti.Vector

功能:创建一个矩阵,矩阵的每个元素是向量

ti.Vector(每个元素是几维向量,dt=数据类型,shape=矩阵形状)
dt:data_type的意思,dt=ti.f32指32位float
shape:确定矩阵的行数和列数,如果shape是一个数字,就是单行矩阵,如果shape是一个元祖(3,4),就是3行4列矩阵exp: ti.Vector(2,dt=ti.f32,shape=1000) 他是1000个2维向量组成的1行矩阵,每个数字是float32,访问最后一个元素写v[999][1]即可

1.2、ti.Matrix

功能:创建一个矩阵,矩阵的每个元素也是矩阵

ti.Matrix(每个元素行数,每个元素列数,dt=数据类型,shape=矩阵形状)

1.3、ti.var

功能:创建一个矩阵,每个元素是一个数值

ti.var(dt=数据类型,shape=矩阵形状)

1.4、数据结构分析

x=ti.Vector(2,dt=ti.f32,shape=n_particles) #position位置,每一个粒子有一个位置
v=ti.Vector(2,dt=ti.f32,shape=n_particles) #velocity速度,每一个粒子有一个速度
C=ti.Matrix(2,2,dt=ti.f32,shape=n_particles) #affine速度场,每一个粒子对应一个2X2矩阵
F=ti.Matrix(2,2,dt=ti.f32,shape=n_particles) #deformation gradient变形梯度矩阵
material=ti.var(dt=ti.i32,shape=n_particles) # material id 这个粒子里有三种材料,分别是0,1,2
Jp=ti.var(dt=ti.f32,shape=n_particles) # plastic deformation 塑形变形,不可恢复变形
grid_v=ti.Vector(2,dt=ti.f32,shape=(n_grid,n_grid)) #grid node
momemtum/velocity: 128X128矩阵,每一个元素是一个Vector2,每个格子一个总体速度
grid_m=ti.var(dt=ti.f32,shape=(n_grid,n_grid)) #grid node mass格子质量,128X128矩阵,每个元素是一个数字,每个格子一个质量

2、粒子particle,格子grid

# 99行程序开头定义
#粒子数量,网格数量=128*1
n_particles,n_grid=9000*quality**2,128*quality
#每个网格宽度dX=1/128,以及它的倒数inv_dx
dx,inv_dx=1/n_grid,float(n_grid)

所有的坐标、距离都是用0~1的小数表示

可以看出,粒子总数很多,但是格子只有128*128个,如下所示:

示意图中,格子画的很稀疏,实际上每个格子很小。红色圈是有粒子的格子,蓝色圈是没有粒子的格子,蓝色圈可以跳过计算,极大提高运算效率。

原程序的思路是:某些算法与粒子紧密相关,每帧每个粒子都要计算;而另外一些属性是总体属性,只对每个包含粒子的格子计算一次。比如重力的影响就是整体属性,要通过格子算。而且,每个格子只对周围相邻的格子有影响,影响不会超过一个格子的距离。如果没有这个假设,粒子的实时模拟就不可能做到了

之后会看到,每一帧的计算分三段:

  • ​ 粒子相关计算,粒子被归入某一个格子(particle to grid);
  • ​ 格子之内的计算,以及周边一共9个格子互相的影响
  • ​ 格子的速度、速度场等属性,要再影响到每一个粒子(grid to particle)

3、taichi这个库到底做了什么

可以看出,所有粒子计算的方法,全都写在了python代码中,由此可见taichi这个库并非一个即插即用的“物理模拟引擎”,而是一种用于科学计算的基础设施。

熟悉深度学习的朋友应该对这种模式更熟悉一些,最常用的TensorFlow的底层也是在做同样的事,实际上这些库主要是解决了底层计算问题,要拿来直接用还得再做一层封装。

和深度学习的python代码相同,程序并非是在执行python脚本时进行的计算,实际的底层运算是被延后的。python脚本所做的事情都可以看成“准备工作”。例如代码中间这一行:

# 二次核函数
w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)]

如果你想通过print 打印 w 的值只会无功而返,因为运行完这句话,w的值还没有开始计算呢~~这也为分析代码带来很多麻烦,因为不能通过打印log的方式观察每一步计算的结果。

4、完整代码+注释

import taichi as ti# 计算品质,越大算得越准确,但也越慢。quality = 1 # Use a larger value for higher-res simulations# 粒子数量=9000,网格数量=128n_particles, n_grid = 9000 * quality ** 2, 128 * quality# 每个网格宽度ΔX=1/128,以及它的倒数inv_dxdx, inv_dx = 1 / n_grid, float(n_grid)# deltaTime,演算的时间间隔dt = 1e-4 / quality# 体积vol,密度 (rho就是ρ)p_vol, p_rho = (dx * 0.5)**2, 1# 质量 = 体积 * 密度p_mass = p_vol * p_rho#以下是材料系数# E=0.1e4=1000, 泊松分布系数nu=0.2E, nu = 0.1e4, 0.2 # Young's modulus and Poisson's ratio# Lame系数,定义材料性质的,分别是μ和λmu_0, lambda_0 = E / (2 * (1 + nu)), E * nu / ((1+nu) * (1 - 2 * nu)) # Lame parameters# 小技巧:taichi的对象类型不易查看,可以调用矩阵对象的.to_numpy()方法,把它变成numpy对象再看x = ti.Vector(2, dt=ti.f32, shape=n_particles) # position位置, 每个粒子有一个位置v = ti.Vector(2, dt=ti.f32, shape=n_particles) # velocity速度,每个粒子有一个速度C = ti.Matrix(2, 2, dt=ti.f32, shape=n_particles) # affine速度场,每个粒子对应一个2x2矩阵F = ti.Matrix(2, 2, dt=ti.f32, shape=n_particles) # deformation gradient变形梯度矩阵material = ti.var(dt=ti.i32, shape=n_particles) # material id,这个例子里有3种材料,分别是0、1、2Jp = ti.var(dt=ti.f32, shape=n_particles) # plastic deformation 塑性变形,不可恢复变形grid_v = ti.Vector(2, dt=ti.f32, shape=(n_grid, n_grid)) # grid node momemtum/velocity 一个128x128矩阵,每个元素是一个Vector2。每个格子一个总体速度grid_m = ti.var(dt=ti.f32, shape=(n_grid, n_grid)) # grid node mass格子质量。128x128矩阵,每个元素是一个数字,每个格子一个质量ti.cfg.arch = ti.cuda # Try to run on GPU,Nvidia显卡的CUDA# 下面的函数是每帧更新用的,先跳过这个函数看主程序初始化~~# 留到最后看这个函数@ti.kerneldef substep():# 每次都要将每个格子的总速度和总质量归0for i, j in ti.ndrange(n_grid, n_grid):grid_v[i, j] = [0, 0]grid_m[i, j] = 0# 更新每一个粒子,并让它们属于某一个格子(Particle to Grid)for p in range(n_particles): # Particle state update and scatter to grid (P2G)# 用粒子坐标x[p],计算出它所属的格子的左下角base = (x[p] * inv_dx - 0.5).cast(int)# fx是该粒子距离格子左下角的局部坐标fx = x[p] * inv_dx - base.cast(float)# 二次核函数# Quadratic kernels  [http://mpm.graphics   Eqn. 123, with x=fx, fx-1,fx-2]w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)]# w是一个延迟计算的表达式,无法直接看到它的值,后面很多参数都是类似的# w是位置的函数。# w是一个权重,会影响当前格子与周围格子的质量、速度# 猜测:一个格子边缘的粒子,显然对旁边格子的影响更大#每帧更新F,F = somefunc(F, C, dt)F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] # deformation gradient update# h "加硬"系数(雪积累在一起变硬),是根据塑性变形参数Jp算出来的h = ti.exp(10 * (1.0 - Jp[p])) # Hardening coefficient: snow gets harder when compressed# jelly 果冻的加硬系数固定0.3if material[p] == 1: # jelly, make it softerh = 0.3# μ和λ的值根据h确定mu, la = mu_0 * h, lambda_0 * h# 液体的μ固定为0if material[p] == 0: # liquidmu = 0.0# ---------------硬核计算开始,按理说不要随意改动-----------------U, sig, V = ti.svd(F[p])J = 1.0for d in ti.static(range(2)):new_sig = sig[d, d]if material[p] == 2:  # Snownew_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3)  # Plasticity 塑性Jp[p] *= sig[d, d] / new_sigsig[d, d] = new_sigJ *= new_sigif material[p] == 0:  # Reset deformation gradient to avoid numerical instabilityF[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J)elif material[p] == 2:F[p] = U @ sig @ V.T() # Reconstruct elastic deformation gradient after plasticitystress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(ti.f32, 2) * la * J * (J - 1)stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stressaffine = stress + p_mass * C[p]# ===============硬核计算结束==================# 遍历相邻8+1个格子,把粒子参数算到到格子上for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid node neighborhoodoffset = ti.Vector([i, j])dpos = (offset.cast(float) - fx) * dxweight = w[i][0] * w[j][1]# 每个粒子的速度、质量叠加,得到当前格子与周围格子的速度、质量grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)grid_m[base + offset] += weight * p_mass#遍历所有的格子for i, j in ti.ndrange(n_grid, n_grid):#如果这块格子有质量才需要计算if grid_m[i, j] > 0: # No need for epsilon here# 动量转为速度。格子质量越大,格子速度变得越小grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] # Momentum to velocity# 重力影响grid_v[i, j][1] -= dt * 50 # gravity# 碰到四周墙壁,格子速度强行置为0。数字3就是第几个格子算墙壁的意思,可以改大试试if i < 3 and grid_v[i, j][0] < 0:          grid_v[i, j][0] = 0 # Boundary conditionsif i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0if j < 3 and grid_v[i, j][1] < 0:          grid_v[i, j][1] = 0if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0# 最后,把格子的计算还原到粒子上去。遍历所有粒子for p in range(n_particles): # grid to particle (G2P)base = (x[p] * inv_dx - 0.5).cast(int)fx = x[p] * inv_dx - base.cast(float)w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5)]new_v = ti.Vector.zero(ti.f32, 2)new_C = ti.Matrix.zero(ti.f32, 2, 2)# 同样,要考虑该粒子周围的几个格子for i, j in ti.static(ti.ndrange(3, 3)): # loop over 3x3 grid node neighborhooddpos = ti.Vector([i, j]).cast(float) - fxg_v = grid_v[base + ti.Vector([i, j])]weight = w[i][0] * w[j][1]# 新速度 += 权重 * 格子速度new_v += weight * g_vnew_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos)# 更新粒子的速度、Cv[p], C[p] = new_v, new_C# 位置 += 速度 * ΔTimex[p] += dt * v[p] # advection# ~~~~初始化开始~~~~~# 这里要结合运行效果分析import randomgroup_size = n_particles // 3       # 分三组,每组一个正方形for i in range(n_particles):# 位置都是归1化的,取值0~1x[i] = [random.random() * 0.2 + 0.3 + 0.10 * (i // group_size), random.random() * 0.2 + 0.05 + 0.32 * (i // group_size)]material[i] = i // group_size # 0: fluid流体 1: jelly果冻 2: snow雪v[i] = [0, 0]     # 初始速度0F[i] = [[1, 0], [0, 1]]       #变形梯度矩阵初始值Jp[i] = 1                     #塑料变形参数初始值import numpy as np# 初始化ti.GUI,显示用gui = ti.GUI("Taichi MLS-MPM-99", res=512, background_color=0x112F41)# 运行20000帧for frame in range(20000):# s从0到18,每帧要算19次for s in range(int(2e-3 // dt)):substep()colors = np.array([0x068587, 0xED553B, 0xEEEEF0], dtype=np.uint32)    # 三种颜色,每组一个颜色gui.circles(x.to_numpy(), radius=1.5, color=colors[material.to_numpy()])    # 根据位置画小圆点,颜色3选1gui.show() # Change to gui.show(f'{frame:06d}.png') to write images to disk   # 每帧画一次
作者:皮皮关做游戏
https://www.bilibili.com/read/cv4358206?spm_id_from=333.788.b_636f6d6d656e74.55
出处: bilibili

taichi 冰雪奇缘学习相关推荐

  1. Taichi的学习笔记

    1 感想 南溪看了一些关于Taichi的资料,我感觉它就是"JIT+CUDA"的超强编译器,既具有"CUDA-C++"的超快速度,而且像numba.jit一样可 ...

  2. 用 Python 轻松玩转并行编程 Taichi 加速

    Taichi 是一门开源的.嵌入在 Python 中的并行编程语言 语法简单,上手容易,运行高效 大大简化高性能图形学.数值计算.人工智能应用开发T 官网地址:Taichi编程语言-开源并行计算框架 ...

  3. AI System 人工智能系统 TVM深度学习编译器 DSL IR优化 计算图 编译 优化 内存内核调度优化 DAG 图优化 DFS TaiChi 函数注册机 Registry

    DSL 领域专用语言 TVM深度学习编译器 AI System 人工智能系统 参考项目 TaiChi 三维动画渲染物理仿真引擎DSL TVM 深度学习DSL 密集计算DSL LLVM 模块化编译器 编 ...

  4. MIT博士99 行代码就能实现《冰雪奇缘》的特效引擎入门-用Taichi画太极

    可能最近不少读者也像我一样被某公号的那篇<清华毕业生开发新特效编程语言,99行代码实现<冰雪奇缘>,网友:大神碉堡!创世的快乐>吓了一大跳, 尤其是开篇就引用了冰雪奇缘的动画C ...

  5. 【GAMES201学习笔记】00 - Taichi三维可视化 - Taichi_THREE

    1. 开始之前 1.1 学习资源链接 Taichi_THREE 原作者的教程视频: https://www.bilibili.com/video/av628129594 github 链接: http ...

  6. 【GAMES201学习笔记】00 - Taichi 编程

    参考文档 <Taichi编程语言 - 中文文档> 1. 初始化 Taichi ti.init(arch = ti.cuda) 推荐使用: ti.init(arch = ti.gpu) 会自 ...

  7. 物理专业要用的计算机语言,16岁被保送清华,本科毕业进麻省理工读博,现开发Taichi爆红网络...

    文/星主 本文为星主原创文章,版权归本作者所有.欢迎个人转发与分享. 这几天,一个叫胡渊鸣的年轻人火了,上了各大社交平台的热搜.原因是他开发了物理模拟编程语言Taichi,用99行代码实现了介质模拟器 ...

  8. 清华姚班毕业生开发新特效编程语言,99 行代码实现《冰雪奇缘》,网友:大神碉堡!创世的快乐...

    公众号关注 "GitHubDaily" 设为 "星标",每天带你逛 GitHub! 转自量子位,作者边策.鱼羊 只用 99 行代码,你也可以像<冰雪奇缘& ...

  9. 清华开源深度学习框架计图,开源超级玩家再进阶

    2020-03-22 09:24 导语:清华开源计图,背后是三代人的共同努力. 雷锋网AI源创评论报道,据官方消息,清华大学计算机系图形实验室宣布开源一个全新的深度学习框架:Jittor,中文名计图. ...

最新文章

  1. 概率论—随机变量的数字特征、大数定律及中心极限定理
  2. vim php psr2 插件,将vim打造成c++超级ide(vim插件安装)
  3. 介绍两个Eclipse插件: Implementors Call Hierarchy
  4. Binary Tree Inorder Traversal
  5. Android学习笔记(八)XML文档的解析
  6. Attribute 和 Parameter 的区别
  7. ubuntu添加默认路由才可以访问网络
  8. xwt100编程器使用方法与xtw100没有找到编程器解决办法
  9. 谷歌公开裸眼3D全息视频聊天技术:8k屏幕、4块GPU
  10. Lcb小粉书隐私政策
  11. C盘里的HTML是什么文件,C盘Windows下的winsxs是什么文件可以删除吗
  12. Elasticsearch入门登录篇
  13. 2021-05-29 DOM元素的属性和操作:节点非内置属性,节点增删改查,cssDOM设置行内样式与非行内样式等
  14. 怎么用python编简单游戏大全_适合新手练手的三个python简单小游戏
  15. 把握SDN研发方向,展望未来发展趋势
  16. Python - 面向对象编程 - 三大特性之继承
  17. element-ui 级联选择器el-cascader踩坑
  18. 省公司交流期间一线工作总结
  19. 华为手机开机卡在开机画面,该怎么解决呢?
  20. 幕客学习CSS3全面基础知识点

热门文章

  1. tinyCC 超轻量级编译器
  2. python爬取斗鱼鱼吧_[Python爬虫]使用Python爬取静态网页-斗鱼直播
  3. “数字化”与“信息化”的区别和联系
  4. p5js怎么导入html,p5.js loadTable()用法及代码示例
  5. 记一次golang/json转义问题
  6. 基于CRM跟进(活动)记录中关键字识别的客户跟进加权值的成单概率算法
  7. 一个有意思的图片鼠标切换
  8. 面经:2019网易游戏客户端实习生
  9. 吃西瓜--爬虫系列之Request使用方法
  10. C语言实现扫雷【经典】