Houdini 18 的Vellum目前使用的是更高级的PBD: XPBD(eXtended Position Based Dynamics) 。XPBD使用了2nd Order Integration ,传统的PBD使用的1nd  Order Intergration 。想要查看的话,如下图(默认是2nd Order Integration)。

Houdini  Vellum 之 Graph Color 的算法分析

Vellum Solver中因为要对约束重复的运算(Constraint Iterations 设置迭代的次数),需要比较高的效率。 为了能够让并行运算,引入了Graph Corlor技术。Graph Color说白了, 就是定义一个属性,每个约束的属性和与它相邻的约束的属性值不同。

比如用一个Grid,用Vellum Contraints 得到 distant contrainsts ,然后用Graph Color节点进行计算 (结果默认写入在一个整型color属性上),最后用wrangle 显示颜色(相同的color属性颜色相同), 如下图,能够看到,每一条边(primitive)都与相邻的边的颜色不同。 这样让颜色相同的contrainst在同一个Workset。 不同颜色,不同的Workset。这样在对constrait进行迭代计算时(是在opencl里面进行的),Opencl会根据Workset,同一个Workset才会一起平行计算。

Houdini  Vellum 之 Graph Color 的算法分析

当然,Vellum  中Graph Color的计算时在Vellum Solver内部,这里只是为了演示方便。

另个案例了解Vellum Solver 中 的 Graph Color

新建个一个Grid,选中两边的点, 给Pin Constraints,把两边定住。加 distant constraints ( Vellum Constraint 选 Distant along edges),设置如下图。注意Stiffness设置为最大(10*1e+10),这种材质现实中是没有弹性的, 但是我们又把Rest Length Scale设置为0.5,Vellum Solver会迫使它压缩(现实中这种材质的布料是不存在的)。然后把Vellum Solver的 Smooth Iteration 设置为0(这步很重要)。 结算。

Houdini  Vellum 之 Graph Color 的算法分析

 结算后发现,有的边的长度缩短大致0.5倍,有的反而被拉长了。用Graph Color查看,发现拉伸边长的是同一个颜色,即作为同一个WorkSet在opencl中并行计算的。如下图:

Houdini  Vellum 之 Graph Color 的算法分析

 这是因为黄色的边做一个同一个Workset首先在opencl中运算,红色和蓝色在黄色后面分别进行运算,这样导致错误都转嫁到了第一次计算的边上(这里的错误是指,设置了Rest Length Scale为0.5倍,红色和蓝色都正确压缩了,黄色反而拉长了,这是错误的, 不是我们想要的)。

Graph Color 的算法分析

 下面分析下Graph Color节点里面的算法。新建一个Grid,Graph Color, Color(random from attributes)。

Houdini  Vellum 之 Graph Color 的算法分析

进入Graph Color内部, 主要分析最左边这路(Connectivity 为Primtive),Contraints运算的时候也是以Primtive为单位。

(1)create_color_attrib  

         创建 color (Integer)primitive属性

(2)find_npts

          把当前primitive总共有多少邻居记录在属性 @__npts属性上。   

i@__npts = len(primpoints(0, @primnum));

(3)color_high_valence (Run  Over Detail )

         这步主要是把邻居多的primitive筛选出来(Graph Color 的参数Max Valence设置阈值,默认20),单独计算。因为进入opencl进行并行计算时,需要把邻居primitive写入在一个array数组里,如果相邻的邻居太多,会造成存储和计算时间的极大浪费。挑其中比较重要的代码部分进行分析:

        下面的代码把筛选出来的符合条件的primitve(@__npts>maxvalence) 存入在prims数组里面。 

int maxvalence = chi("maxvalence");
string grp = sprintf("\@__npts>=%d", maxvalence);
int prims[] = expandprimgroup(0, grp);

  然后对数组进行排序。npts数组记录prims数组中对应的prim的点的数量乘以-1 。argsort对npts数组排序,它返回的是一个indice的数组。

  indice: 比如一个整型数组  int a[] = {0,4,2} 。a[1] = 4 ,中括号里面的1就是indice, 也就是数组下标。

  对于一个数组int b[] = {-4,-10,-2,-8} 进行 argsort排序会得到一个数组下标的数组 {1,3,0,2},最后用reorder函数得到最终的排序数组 {b[1],b[3],b[0],b[2]}  即{ -10,-8,-4,-2}。 (argsort 和reorder一般搭档起来用,来对数组排序)。

foreach(int i; int prim; prims)
{
    npts[i] = -len(primpoints(0, prim));
}
prims = reorder(prims, argsort(npts));

  最终得到一个数组,根据primitive的点的数量由大到小排列。  

  核心的部分在下面这个foreeach部分

foreach(int i; int prim; prims)
{
    int useNewColor = 1;     // 声明新变量,是否使用新颜色,默认1,即使用新染色
    int pts[] = primpoints(0, prim); 
    for(int j = 0; j < ncolors; j++)    // ncolors记录当前不同color的数量, 遍历所有color
    {
        int useColor = 1;       // 声明新变量,能否用当前颜色,默认1, 可以使用
        foreach(int pt; pts)    // 遍历当前primitive的每个点
        {
            if (mapping[j * npt + pt] != 0)   //mapping是一个数组,npts是所有点的数量(不止当前primitive)
            {                                 //比如npts= 14 ,当前primitive2个点,对于j=0,即color值为0 ,mapping[0*14+0]~mappint[0*14+(14-1)] 
                useColor = 0;        //对应的所有的点使用的color 0的情况,mapping值若为1即不等于0, 说明当前color值被邻居占用,不能使用
                break;               // 这是把useColor赋值0, 即当前color值不可用
            }
        }
        if (useColor)                 //如果当前颜色可用
        {
            colors[i] = j;           // 把当前primitive 的color值记录到colors数组里
            foreach(int pt; pts)   // 对于当前primitive的所有点pt,color为j,对应mapping[j*npt+pt],设置为1,表示被当前primitive占用
{ mapping[j
* npt + pt] = 1; // } useNewColor = 0; // 这种情况就不使用新颜色了 break; } } if (useNewColor) //说明没有可用的color,需要新增加一个color { colors[i] = ncolors; // 新的color值存到colors数组里,colors[i]是prims[i]对应的颜色值 ncolors++; // 总颜色数加1 resize(mapping, ncolors * npt); // mapping数组resize,空间比之前多了npt个 foreach(int pt; pts) { mapping[colors[i] * npt + pt] = 1; //对于新增的color[i],当前primitive的点pt, 把对应的mappint[colors[i]*npt+pt]赋值1, 表示被占用 } } }

 这个节点完整的代码如下:

 1 // Handle high valence prims in a serial pre-pass,
 2 // else the find_neighbors wrangle below ends up creating
 3 // extremely large neighbors arrays, which are expensive
 4 // in memory and time.
 5 int maxvalence = chi("maxvalence");
 6 string grp = sprintf("\@__npts>=%d", maxvalence);
 7 int prims[] = expandprimgroup(0, grp);
 8 int nprim = len(prims);
 9 if (nprim == 0)
10     return;
11 
12 // Create mapping for each point to each color and
13 // prims to each color.
14 int npt = npoints(0);
15 int mapping[];
16 int colors[];
17 resize(colors, nprim);
18 int ncolors = 0;
19 
20 
21 // Reverse sort by num pts in constraint
22 // so constraints with more points are first.
23 int npts[];
24 resize(npts, nprim);
25 foreach(int i; int prim; prims)
26 {
27     npts[i] = -len(primpoints(0, prim));
28 }
29 prims = reorder(prims, argsort(npts));
30 
31 foreach(int i; int prim; prims)
32 {
33     int useNewColor = 1;
34     int pts[] = primpoints(0, prim);
35     for(int j = 0; j < ncolors; j++)
36     {
37         // See if we can use this color.
38         int useColor = 1;
39         foreach(int pt; pts)
40         {
41             if (mapping[j * npt + pt] != 0)
42             {
43                 useColor = 0;
44                 break;
45             }
46         }
47 
48         if (useColor)
49         {
50             colors[i] = j;
51             foreach(int pt; pts)
52             {
53                 mapping[j * npt + pt] = 1;
54             }
55             useNewColor = 0;
56             break;
57         }
58     }
59     if (useNewColor)
60     {
61         colors[i] = ncolors;
62         ncolors++;
63         resize(mapping, ncolors * npt);
64         foreach(int pt; pts)
65         {
66             mapping[colors[i] * npt + pt] = 1;
67         }
68     }
69 }
70 
71 // Set prim color attribute.  Colored prims will be skipped
72 // by the OpenCL pass.
73 foreach(int i; int prim; prims)
74     setprimattrib(geoself(), chs("colorname"), prim, colors[i]);
View Code

相关文章:

  • 2022-01-18
  • 2022-01-16
  • 2022-12-23
  • 2021-06-25
  • 2021-07-20
  • 2022-12-23
猜你喜欢
  • 2021-09-14
  • 2022-12-23
  • 2021-06-07
  • 2022-02-17
  • 2021-10-14
  • 2021-09-28
  • 2021-09-12
相关资源
相似解决方案