A Trip to Houdini Geometry Internals 您所在的位置:网站首页 houdini怎么念 A Trip to Houdini Geometry Internals

A Trip to Houdini Geometry Internals

2023-04-08 18:44| 来源: 网络整理| 查看: 265

玩 Houdini 好歹有一年多了,心态已从一开始 “哇,好厉害”,“哇,这就行了?” 转换到了 “emmm,这是如何做到的”,而且这份好奇最近越来越强烈 —— Houdini 的 Geometry 数据到底是如何组织的?细分的时候我随便定义的的属性被谁在什么时候如何做到的自动插值?为什么 for loop 要 compile 之后才能并行?…… 小问号是越来越多

Houdini 虽说没开源,但 SDK 里毕竟包含了大部分的头文件,而且也提供了 pdb,所以想一探其实现细节,还是很容易下手的

…… 那么谁又能抵挡得住这样的诱惑呢?

这不,劳动节正好有了 5 天长假,我就去观赏了一下,暂留此文作为记录,试图回答前面的第一个问题,纯属随便看、随手记,难免会有疏漏,发现了还请斧正

另外 Houdini Geometry 高层次的概念和用法,其实 HDK 文档 已经能很好地解释了,本文主要诞生于我自己对于 “How” 的好奇心,至于亲,建议先看了文档再决定要不要继续读下去

BTW 本文基于 Houdini 18.0.416 indie

代码风格

Houdini 的命名风格不得不说还是挺有特色的

成员变量 myVar ..

静态变量 theVar ...

(虽然有点不习惯,但又好像很有道理)

tab stop : 8

前缀 GA_ : 抽象几何属性(本文重点);GEO_ : 3D 几何;GU_ : 几何操作;UT_ : 杂七杂八(总会碰上);其他:略

GA 是啥

引用 HDK 文档

Houdini 12.0 has a new underlying geometry library. One of the design constraints for the design of the GA library was to minimize effort required to port code from the previous GB incarnation. Thus, though the underlying data structures are very different, the GA library can provide interfaces which make it look similar (not exact), to the older GB library. The GB library was an "object centric" view of geometry. There were point, vertex, and primitive objects (or "elements"). Each object owned its own attribute data All objects of the same type had the same kind of attribute data Points could be shared between primitives Primitives used vertex objects to reference points While the GA library keeps the concepts of points, vertices and primitives, there are no longer any individual objects (with the current exception of primitives). Instead of individual objects, the geometry stores arrays of attribute data for each class of object. GA_Detail 是啥

Detail 在 Houdini SOP 的世界里,指的大概就是一个几何部分

记得我第一次见到这个名字,实在是想不通为什么,以至于忍不住 Google 了一下才确认 ... 原来从释义来看挺合适的,是我孤陋寡闻了

由它吧

那么 GA_Detail 里面有啥呢

最主要的包括

point 属性映射 -> IndexMapvertex 属性映射 -> IndexMapprimitive 属性映射 -> IndexMapglobal 属性映射 -> IndexMapprimitive 列表 -> PrimitiveList属性列表 -> AttributeSet拓扑 -> Topology(point|vertex|prim|edge)Groups -> (Point|Vertex|Primitive|Edge)GroupTable... (无关紧要的其他东西)

至此很容易理解,GA_Detail 最主要的作用是作为容器,记录下 point/vertex/prim/detail 属性和 point/vertex/prim/edge 分组,玩过 SOP 的小伙伴们对于这些概念应该早已烂熟于心了; 而 Houdini SOP 最优雅的地方,在我看来,也就是这一块儿的抽象:属性和分组很容易操作,还可以用 Geometry Spreadsheet 随便看,而对于 “几何” 更本质的拓扑信息,其实是藏起来的

这背后的逻辑(我猜)是,拓扑操作相对复杂,但容易穷举;属性和分组操作相对简单,但无法穷举;而按这样的分法,笨蛋也可以通过用属性+分组+预制的Cut/Extrude/Bevel/UV Unwrap/...操作来实现绝大多数几何处理功能,非常好用;同时对于高玩,拓扑信息在 Vex 里面依旧可以随便访问/修改,灵活度也并没有打折扣。

而 point/vertex/primitive/detail 的分级也是十分有讲究的,尤其是对于 “point” 和 “vertex” 的区分,实实在在使得算法好写了太多

下一个问题,这些属性是如何存的呢?AttributeSet 是啥?IndexMap 又是啥?

AttributeSet 是啥

AttributeSet 可以理解为包装了一层的 AttributeDict, 而 AttributeDict,顾名思义,主要是一个 String -> AttributeProxy 的 Map;

AttributeProxy 呢,则是 Attribute 的一层带引用计数的封装,用以放入侵入式智能指针中管理。

所以抽丝剥茧,AttributeSet 功能上就是 String 到 Attribute 的映射

Attribute 是啥

Attribute 的成员变量平平无奇,因为精华在其中的一堆获取 AIF (Attribute InterFace) 的函数上面 / 注:这 interface 感觉更像 adapter /,以 AIFTuple 为例,这个接口决定了如何获取数值属性的类型/size(向量维度)/如何访问或者设置属性值

而另有 AIFMerge 接口定义属性如何合并 / 有 AIFInterp 接口定义属性如何插值 / ...

这样我们才有办法把各种属性揉在一个 Detail 里面,随便细分/合并都有“符合预期”的插值/补全/替换规则,妙啊

那真正的数值属性在什么地方呢,答案是 ATI (Attribute Type Implementation), 数值属性嘛就是 ATINumeric 实现的,其关键的内容就是 PageArray 类型的 data 成员了,其他的属性类型大同小异

PageArray 是啥

要看 GA_PageArray 首先得弄明白 UT_PageArray

PageArray 这名字倒是意外的 self-explaining,就是 Array of Pages - 分页的数组

UT_PageArray 的注释很精彩,请自行观赏 ……

/// W E L C O M E /// to scenic /// Template Hell /// /// Population: You /// /// If you're just using this class, hopefully you'll have a short and /// not-so-painful visit! /// /// If, however, you're attempting to modify this class, BEWARE! /// Any missteps can and likely will result in application of the /// Doughnut Rules, so be sure to review them before visiting. /// It may take several days to escape, so it may be best to just /// revert and make absolutely sure that it works on all platforms /// before committing again. /// /// This class is the result of a wide variety of needs for a wide variety /// of use cases, most of which need both good performance and flexibility. /// You may want to skip reading this gigantic comment, but whether it's /// immediately or in a few months, you'll realize that skipping reading /// this was a huge mistake. /// ...

这玩意呢,是个可以共享的 Copy-On-Harden 容器,值是存在分页上的,分页可以共享、整个数组也可以共享,对于整个分页取相同值的情况或者整个分页取 0 的“常见”特殊情况,有特殊的标记来指明,在 Houdini 的用法下特别优化

想弄明白这玩意的内存结构,最简单的办法是直接下个断点看 ……

挖出 Entry 再看看 ——

再看看 myData ——

这便是实际的值列表(此例中是 'P' 属性)

Page Array 如同其注释所说,细节不胜繁杂,但细品还是挺妙的 —— 一来通过 intrusive ptr 共享 + bit 0 标记减少了不必要的内存分配,二来可以将合并/拆分等常见操作的复杂度降几个数量级(至少 page entry 作为大头基本都可以共用),同时对 cache 还挺友好;只是相应的,这玩意在多线程情况下如何保证高效且安全就需要特别留心了

IndexMap 是啥

Index Map 主要用于处理 offset 和 index 的互相转换

至于 offset 和 index 分别是啥,Houdini 注释里的信息量实在是少了点

GA_Offset defines an offset into the attribute table of a GA_IndexMap GA_Index defines an ordered index of a GA_IndexMap

实际上关于这两者的区别,简单可以理解 index == @vtxnum 或 @ptnum 或 @primnum

而 offset 则是其对应的 vertex/point/primitive attribute 数组中的索引

i.e. 理论上有点 0,1,2 对应属性值 a,b,c;此时 index->offset 的对应关系为 0->0, 1->1, 2->2。删掉点 1 之后,属性列表的内容可以还是 a,b,c; index->offset 的对应关系则改成了 0->0, 1->2 // 可以通过 defragment 清理

比较有趣的是,offset 和 index 完全匹配的情况下 IndexMap.myIndexFromOffset.myData == 1,文档没说,且头文件是对不上的

// GA_ListType::setTrivialRange, 看这起手式:

Primitive 是啥

Primitive 对应 Houdini 所知的图元类型,比如 Face, Volume 之类的:

—— 一个典型的 primitive list 长这样

// TODO: 其中 vertex list 里面的 data 字段明显还有 hack, 暂且不深究了

Topology 是啥

Topology 的主要功能从它的接口可以窥见一斑:

addVertexaddPointdelVertexdelPointwireVertexPointwireVertexPrimitiveisPointUsedisPointSharedhedgeToPrevHedgehedgeToNextHedge...

当 Detail 以各种形式被改变的时候都会触发 Topology 更新 ——

……

BTW. GA_Topology 似乎只用来维护点/顶点以及点/图元之间的关系(?)关键的 half-edge 结构看上去基本上永远不维护(??)至少 vex 里面用到的 hedge_XXX 函数以及 GU 里的各种算法是通过 GEO_DetachedHedgeInterface / GEO_HedgeInterface 来实现的

Group ...

group 比较简单 就不挖了

值得参考AIF + ATI 的设计模式挺有意思SOA 结构 + Page Array + Index Map 真的挺配

感觉以上结构的属性容器其实不光在几何操作上好用,用来做个通用数据处理工具之类的似乎也特别合适

哦对了,打开 Houdini 直奔 Geometry Spreadsheet、把 Houdini SOP 当作数据处理工具来用好像本来也挺好用的 - -

疑问集Global 属性为啥也是个 IndexMap?Topology 信息为啥要以(隐藏的) Attribute 类型来保存?纯粹因为它和属性一样也是一个元素对应一个值的关系?还是有不为人知的神奇用法?看起来 VEX 执行的时候需要把用到的属性值都先拷贝到连续的数组里面来传递,完了再重新拷回原来的属性容器,那对于大量使用 VEX 的应用场景 Page Array 还是个好的设计嘛?

…… 这次先到这儿,剩下的以后再接着看接着写吧 orz



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有