如何在基于位置的动力学中并行化碰撞约束

How to parallelize collision constraints in position based dynamics

我正在尝试在 OpenCL 中实现 PBD 算法。在 original paper 中,算法表示为

(1) forall vertices i
(2)     initialize x[i]=x0[i], v[i]=v0[i], w[i]=1/m[i]
(3)endfor
(4)loop
(5)    forall vertices i do v[i]←v[i]+∆t*w*i*f_ext(x[i])
(6)    dampVelocities(v1,...,vN)
(7)    forall vertices i do p[i]←x[i]+∆t*v[i]
(8)    forall vertices i do generateCollisionConstraints(xi→pi)
(9)    loop solverIterations times
(10)        projectConstraints(C1,...,CM+Mcoll,p1,...,pN)
(11)   endloop
(12)   forall vertices i
(13)       v[i]←(p[i]−x[i])/∆t(14)x[i]←p[i]
(15)   endfor
(16)   velocityUpdate(v1,...,vN)
(17)endloop

第 8 步是最难有效并行化的。我目前正在做的是使用常规 3D 网格(表示为单个缓冲区),我首先在其中插入所有粒子。接下来我迭代相邻的单元格并检查碰撞。但是当实际检测到碰撞时我该怎么办?我不能保存可变大小的向量,也不能将约束“附加”到缓冲区,因为这需要原子操作,而原子操作不是并行的。在 GPU 上实现 PBD 的一些有效方法是什么?

到目前为止我发现这可能是实现它的最佳方法 是

  • 使用atomicAdd模拟堆栈,类似于人脸剔除的方式https://vkguide.dev/docs/gpudriven/compute_culling/
  • 为每个线程预分配一个小堆栈,并让每个线程将约束附加到该私有堆栈。然后,您可以在对数时间内将所有这些堆栈有效地连接到一个大数组中。为了更直观地显示它,考虑 3 个堆栈,每个最大长度为 2
Stack 1: |_|_|  (empty stack)
Stack 2: |A|_|  (stack with one element)
Stack 3: |B|C|  (stack fully filled up to its maximum length)

然后像这样连接这些堆栈

|A|_|_|_|B|C|

现在迭代计算每个元素应该移动多少,这等于它左边的空元素数(含)。创建第二个缓冲区来保存这些空元素的数量。这个缓冲区的初始状态是

|0|1|1|1|0|0|  (each cell is 0 if it is empty or 1 otherwise)

在第二次迭代中,您对位置 ii+1 处的单元格值求和并将结果存储在 i+1.

|0|1|2|2|1|0|  (each cell holds number of filled cells up to 2 places to the left, including itself)

在第三次迭代中,您将单元格 ii+2

的值相加
|0|1|2|3|3|2|  (each cell holds number of filled cells up to 4 places to the left, including itself)

在第四次迭代中,您将单元格 ii+4

的值相加
|0|1|2|3|3|3|  (each cell holds number of filled cells up to 8 places to the left, including itself)

现在您知道 A 应该向左移动 0B 向左移动 3C 向左移动 3 , 得出最终结果

|A|B|C|_|_|_|