[{"data":1,"prerenderedAt":566},["ShallowReactive",2],{"\u002F2020-10-25-kine-driver-note":3},{"id":4,"title":5,"body":6,"date":543,"description":12,"extension":544,"meta":545,"navigation":548,"path":560,"seo":561,"stem":562,"tags":563,"__hash__":565},"blogs\u002F_legacy\u002F2020\u002F2020-10-25-kine-driver-note.md","辅助骨骼系统KineDriver笔记",{"type":7,"value":8,"toc":513},"minimark",[9,13,23,27,31,34,37,40,43,46,49,52,55,59,62,65,69,72,82,85,94,101,104,107,110,113,119,122,125,128,146,149,152,155,158,169,172,175,178,181,185,188,191,194,197,200,203,207,210,213,219,222,230,233,236,239,244,248,251,254,257,260,263,266,269,272,277,280,283,292,295,299,314,317,320,323,332,335,338,343,346,367,370,373,376,381,384,393,396,401,404,410,413,416,428,433,436,439,442,445,450,455,458,462,465,470,473,478,481,486,489,495,498,504,507,510],[10,11,12],"p",{},"这个是Square Enix在2019年分享的角色动画相关的笔记。",[10,14,15,16],{},"原文：",[17,18,22],"a",{"href":19,"rel":20},"http:\u002F\u002Fwww.jp.square-enix.com\u002Ftech\u002Flibrary\u002Fpdf\u002FCEDEC2019_KineDriver.pdf",[21],"nofollow","www.jp.square-enix.com\u002Ftech\u002Flibrary\u002Fpdf\u002FCEDEC2019_KineDriver.pdf",[24,25,26],"h2",{"id":26},"系统概要",[28,29,30],"h3",{"id":30},"辅助骨骼系统简介",[10,32,33],{},"辅助骨骼系统主要用于提升蒙皮动画的效果，其目的是为了提高骨骼动画的表现，所以并不与实际存在的骨骼完全对应。",[10,35,36],{},"在设计上，由骨骼进行驱动。通过骨骼的运动驱动数值，其结果不仅可以用于蒙皮动画的表现，也可以用于Shader和材质等特效。",[10,38,39],{},"因此，辅助骨骼系统并不仅是一个工作于资源制作环境下的工具，而是一个在资源制作和实际运行环境中同时存在，并且能够保证在两个环境下的运行效果一致性的系统。",[10,41,42],{},"这么做的好处主要有两个，首先，可以降低动画资源的大小，同时，还可以方便在运行时进行动态的调整。",[10,44,45],{},"版本历史上看，一开始是使用softimage的，后面换到了maya上，到3.0版本已经支持使用图形化界面来拖节点的编辑形式了。",[10,47,48],{},"由于提供了中间文件，可以方便的在各种引擎环境下运行，也能导入到MotionBuilder中，但是辅助骨骼的制作还是使用的Maya。",[28,50,51],{"id":51},"功能和概念",[10,53,54],{},"系统内共有三种控制器(operator)，source、target和constraint。",[56,57,58],"h4",{"id":58},"源和目标控制器",[10,60,61],{},"源控制器可以从多个骨骼获取需要的状态并对其进行插值混合。",[10,63,64],{},"坐标空间上，默认使用父节点作为local space的坐标参考，同时也支持指定其他骨骼作为坐标参照。",[66,67,68],"h5",{"id":68},"旋转表现",[10,70,71],{},"旋转表现有两种方式",[73,74,75,79],"ol",{},[76,77,78],"li",{},"Bend&Roll，将旋转分解到水平方向和竖直方向，方便美术理解",[76,80,81],{},"Expmap，使用四元数的对数，主要用于程序自动生成逻辑",[10,83,84],{},"对于Bend&Roll，在欧拉角的处理上有一些问题。首先，欧拉角在计算上具有一些复杂度，而且同一个状态所对应的欧拉角并不唯一，例如(0,90,60)和(-30,90,30)是同一个姿势。同时，在运行时通常引擎内部都是采用四元数的，将其转换回欧拉角时便无法保证能够回到预期的那个欧拉角。",[10,86,87,88,93],{},"因此最终采用了",[17,89,92],{"href":90,"rel":91},"https:\u002F\u002Fzh.wikipedia.org\u002Fzh\u002F%E7%90%83%E6%A5%B5%E5%B9%B3%E9%9D%A2%E6%8A%95%E5%BD%B1",[21],"球极平面投影","的方法，来对旋转角度中的水平角度进行处理。处理方式上，将起始位置的朝向分解为vx，vy和vz。将到达目标旋转vx'所需要的角度分解为θh和θv。详细的公式可以看图：",[10,95,96],{},[97,98],"img",{"alt":99,"src":100},"旋转转换","\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002F%E5%9C%96%E7%89%87.png",[10,102,103],{},"Bend&Roll在实际使用时，水平旋转和竖直旋转的应用顺序可以根据不同的骨骼进行配置，方便在使用时进行直观的理解。",[10,105,106],{},"Expmap则没有顺序上的问题，程序内部直接计算结果并进行控制。",[66,108,109],{"id":109},"缩放表现",[10,111,112],{},"缩放同样采用对数空间进行内部控制，采用对数是为了获得更大的精度空间，如下图所示：",[10,114,115],{},[97,116],{"alt":117,"src":118},"对数空间的优势","\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002F%E5%9C%96%E7%89%87-1.png",[10,120,121],{},"不过ppt里面说没有实装。",[66,123,124],{"id":124},"节点图支持",[10,126,127],{},"可以使用图形化的方式对控制器进行操作，主要提供了以下的节点：",[129,130,131,134,137,140,143],"ul",{},[76,132,133],{},"EZParamLinkLinear(线性插值)",[76,135,136],{},"EZParamLink(简单贝塞尔插值)",[76,138,139],{},"LinkWith(DrivenKey位置链接)",[76,141,142],{},"RBFInterp(支持多个输入输出的链接)",[76,144,145],{},"Expr(自定义表达式)",[10,147,148],{},"节点名和功能微妙的没有对上，但是原文就是这样的。",[10,150,151],{},"DrivenKey，查了下是maya的功能，就不详细的去看了。RBF后面有说明。",[56,153,154],{"id":154},"约束控制器",[10,156,157],{},"与节点图不同的，基于约束的控制器。",[129,159,160,163,166],{},[76,161,162],{},"Position：位置约束，可以基于多个源进行计算",[76,164,165],{},"Orientation：朝向约束，可以基于多个源进行计算",[76,167,168],{},"Dirction：让旋转指向目标，3点模式可以额外控制水平旋转",[66,170,171],{"id":171},"紧贴表面",[10,173,174],{},"约束控制器也提供紧贴表面的功能，根据Position和Orientation约束以及目标表面的骨骼权重进行计算。",[10,176,177],{},"在制作Bonamik的引导骨骼的时候经常用到。",[24,179,180],{"id":180},"工作流",[28,182,184],{"id":183},"maya中的资源制作","Maya中的资源制作",[10,186,187],{},"似乎是使用了maya的插件功能，节点图部分使用了C++的实现，导出和界面之类的功能使用了Python。",[10,189,190],{},"节点在显示时使用的是惯用的单位，在连接节点时会自动插入UnitConversion统一进行单位换算。",[28,192,193],{"id":193},"中间文件",[10,195,196],{},"中间文件采用xml的形式进行输出。",[10,198,199],{},"文件中保存了各种控制器的的节点的输出，以及节点之间的连接。",[10,201,202],{},"在进行文件输出时，可以指定缩放倍数，方便在使用不同单位的引擎中进行使用。",[28,204,206],{"id":205},"ue4插件","UE4插件",[10,208,209],{},"为了方便在项目中复用，进行了插件化。",[10,211,212],{},"模块构成，直接上图：",[10,214,215],{},[97,216],{"alt":217,"src":218},"image","\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb.png",[10,220,221],{},"使用上提供了两种方式",[73,223,224,227],{},[76,225,226],{},"动画节点：使用标准的动画控制流程，无法预览KineDriver的功能",[76,228,229],{},"Component版：作为SkeletonMesh的子组件，在tick中对骨骼进行更新，可以预览效果",[10,231,232],{},"提供了动画通知用于在动画的分段中关闭KineDriver。",[10,234,235],{},"对于与Bonamik的兼容关系，分开使用的时候会使用AddTickPrequist来决定调用顺序。另外似乎还有将KineDriver和Bonamik统合在一起的插件。",[10,237,238],{},"数据流程直接上图：",[10,240,241],{},[97,242],{"alt":217,"src":243},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-1.png",[56,245,247],{"id":246},"assetuserdata","AssetUserData",[10,249,250],{},"使用了AssetUserData进行数据的存储，方便在骨骼数据上直接添加和修改数据，通过引用指向具体的导入资源。",[10,252,253],{},"将数据放在骨骼上的好处是可以不必调整componnet的属性就直接对辅助骨骼的资源进行修改。",[56,255,256],{"id":256},"缩放传递",[10,258,259],{},"在Maya中，对骨骼的缩放不会传递到子骨骼。",[10,261,262],{},"而在UE4中则会进行缩放的传递，为了保持效果的一致性，在UE4中作了处理，对于TargetScaleOp的骨骼的子骨骼进行缩放的重新设置。",[24,264,265],{"id":265},"功能实装",[28,267,268],{"id":268},"表达式",[10,270,271],{},"提供基本的表达式支持，语法尽量的精简化了，不会使用for这些循环的语句。",[10,273,274],{},[97,275],{"alt":217,"src":276},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-2.png",[10,278,279],{},"主要使用一些内置的函数，同时添加对三维向量和四元数的支持。运算上支持了基本的四则运算和括号。",[10,281,282],{},"对于输入的参数，则转换到x1,x2,...,xn的形式进行记录。",[10,284,285,286,291],{},"在maya内部使用时，会将转换后的表达式使用[",[17,287,290],{"href":288,"rel":289},"https:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002F%E8%B0%83%E5%BA%A6%E5%9C%BA%E7%AE%97%E6%B3%95",[21],"调度场算法","]进行转换和计算。如果要存储到中间文件的话，会将二进制代码转换为汇编语句的格式。各个游戏引擎加载时则重新将汇编语句转换为二进制代码。",[10,293,294],{},"之所以会用到表达式是因为有很多的分支需求，同时有的用几行表达式就能做好的事情用节点就要拖半天，所以需求也比较大。即便初期有预先设置很多功能节点，但是最后还是使用表达式具有更高的灵活性。",[28,296,298],{"id":297},"rbf插值","RBF插值",[10,300,301,302,307,308,313],{},"RBF总之就是一种拟合的方式，RBF本身可以参考[",[17,303,306],{"href":304,"rel":305},"https:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002F%E5%BE%84%E5%90%91%E5%9F%BA%E5%87%BD%E6%95%B0",[21],"径向基函数","]，RBF插值可以参考[",[17,309,312],{"href":310,"rel":311},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRadial_basis_function_interpolation",[21],"Radial basis function interpolation","]。",[10,315,316],{},"这边有很大的篇幅中介绍这个算法的实现和原理，其实主要的目的是让插值变得更加平滑。",[28,318,319],{"id":319},"辅助骨骼自动生成",[10,321,322],{},"通过在特定姿态时美术已经调好的mesh状态，与完全没有调节的默认骨骼在这个状态的表现之间对比，自动的生成辅助骨骼。",[10,324,325,326,331],{},"使用了[",[17,327,330],{"href":328,"rel":329},"http:\u002F\u002Fgraphics.cs.uh.edu\u002Fwp-content\u002Fpapers\u002F2012\u002F2012_SA_SSDR_preprint.pdf",[21],"Smooth Skinning Decompostion with Rigid Bones","] ，简称SSDR。",[10,333,334],{},"主要的功能就是，对具有J个顶点的模型，使用N个形变示例动画，尝试生成D个骨骼来做一个蒙皮的操作。",[10,336,337],{},"由于很难同时对骨骼的位置和蒙皮的权重同时进行最优化处理，所以会分别进行十次来试图逼近最优解。",[10,339,340],{},[97,341],{"alt":217,"src":342},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-3.png",[10,344,345],{},"进行简单的列出",[347,348,349,352,355,358,361,364],"blockquote",{},[10,350,351],{},"a. 主骨骼没有调整的情况下，使用SSDR对权重进行最优化处理",[10,353,354],{},"b. 添加指定数量的辅助骨骼",[10,356,357],{},"c1. 使用SSDR进行权重最优化处理",[10,359,360],{},"c2. 暂时指定辅助骨骼的父骨骼",[10,362,363],{},"c3. 使用SSDR对辅助骨骼的位置进行最优化处理",[10,365,366],{},"d. 决定辅助骨骼的父骨骼，以及自动控制用的RBF插值设定。",[10,368,369],{},"其中c的步骤就是要执行10次的步骤。",[56,371,372],{"id":372},"添加指定数量的辅助骨骼",[10,374,375],{},"这个过程只会执行一次，但是其操作内部需要进行多次迭代才能完成。",[10,377,378],{},[97,379],{"alt":217,"src":380},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-4.png",[56,382,383],{"id":383},"蒙皮权重最优化",[10,385,386,387,392],{},"对一开始的公式中的[",[17,388,391],{"href":389,"rel":390},"https:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002F%E4%BA%8C%E6%AC%A1%E8%A7%84%E5%88%92",[21],"二次规划","]中的w进行单独求解。",[10,394,395],{},"在整个过程中会多次使用到，虽然角标写的是SSDR。但是额外参考了Computer Graphics Gems JP 2015的向井老师的分享。在辅助骨骼的生成中，为了使得最优化的求解过程更加安定，进行了以下的优化：",[73,397,398],{},[76,399,400],{},"移除参考动画中的重复顶点",[10,402,403],{},"在对角色的姿态进行学习时，从单独的顶点来看会有完全没有移动的帧存在",[73,405,407],{"start":406},2,[76,408,409],{},"对骨骼进行合并",[10,411,412],{},"会出现对于某一个顶点而样，有多个骨骼的给出的变换结果相同的情况。此时对这些骨骼进行合并作为一个骨骼来计算，获得的最优化权重结果则按照原本的权重比例分散回去。",[56,414,415],{"id":415},"辅助骨骼的位置最优化",[10,417,418,419,423,424,427],{},"步骤c3，参考的还是上面的CGG JP2015的分享，同时参考了",[420,421,422],"span",{},"Horn 1987","以及",[420,425,426],{},"Mukai 2018","的缩放传递控制。",[10,429,430],{},[97,431],{"alt":217,"src":432},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-5.png",[10,434,435],{},"以点群为单位进行适配，来获得最佳的位置、旋转和缩放。",[10,437,438],{},"这里面原文是Transform，所以包含了Translation，Rotation和Scale。只是一时找不到合适的中文替代所以翻成了位置。",[56,440,441],{"id":441},"暂时指定辅助骨骼的父骨骼",[10,443,444],{},"步骤c2，这个步骤只有在使用了scale的计算时才会需要，在进行辅助骨骼计算时，为了计算的稳定性，是在本地旋转为0的情况下进行的。此时应用缩放会产生较大的误差，因此要暂时寻找到父骨骼并转换到骨骼空间之后再进行缩放处理，这个父骨骼可以随便找一个，主要的目的是为了将计算转换到骨骼空间去。",[10,446,447],{},[97,448],{"alt":217,"src":449},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-6.png",[10,451,452],{},[97,453],{"alt":217,"src":454},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-7.png",[10,456,457],{},"说实话还没有仔细看过计算过程，所以只是把自己的猜测描述了下。",[56,459,461],{"id":460},"决定父骨骼以及rbf配置","决定父骨骼以及RBF配置",[10,463,464],{},"最终的步骤d，计算出辅助骨骼以及其被主骨骼驱动的形式。",[10,466,467],{},[97,468],{"alt":217,"src":469},"\u002Fwp-content\u002Fuploads\u002F2020\u002F10\u002Fimage_thumb-8.png",[10,471,472],{},"生成辅助骨骼的自动控制总共有四个步骤：",[73,474,475],{},[76,476,477],{},"从蒙皮权重来寻找候补驱动者",[10,479,480],{},"从本辅助骨骼所驱动的顶点来寻找原本驱动这些顶点的主骨骼",[73,482,483],{"start":406},[76,484,485],{},"从候补驱动中寻找父骨骼",[10,487,488],{},"在所有的候补中寻找误差最小的",[73,490,492],{"start":491},3,[76,493,494],{},"从剩余的候补中选择RBF用骨骼",[10,496,497],{},"选择能够尽量覆盖RBF插值的1~2个骨骼",[73,499,501],{"start":500},4,[76,502,503],{},"RBF关键点修正",[10,505,506],{},"从没有关键点的情况下误差最大的地方开始插入关键点",[24,508,509],{"id":509},"总结",[10,511,512],{},"本来是从FF7的动画分享过来的，整个系统的原理其实还是挺清晰的。只是后面自动生成的部分使用了很复杂的计算逻辑，由于没有太过深入的打算，所以详细的公式以及额外引用的论文就没有去参考了，所以可能会有意思不明确的地方。",{"title":514,"searchDepth":406,"depth":491,"links":515},"",[516,523,531,542],{"id":26,"depth":406,"text":26,"children":517},[518,519],{"id":30,"depth":491,"text":30},{"id":51,"depth":491,"text":51,"children":520},[521,522],{"id":58,"depth":500,"text":58},{"id":154,"depth":500,"text":154},{"id":180,"depth":406,"text":180,"children":524},[525,526,527],{"id":183,"depth":491,"text":184},{"id":193,"depth":491,"text":193},{"id":205,"depth":491,"text":206,"children":528},[529,530],{"id":246,"depth":500,"text":247},{"id":256,"depth":500,"text":256},{"id":265,"depth":406,"text":265,"children":532},[533,534,535],{"id":268,"depth":491,"text":268},{"id":297,"depth":491,"text":298},{"id":319,"depth":491,"text":319,"children":536},[537,538,539,540,541],{"id":372,"depth":500,"text":372},{"id":383,"depth":500,"text":383},{"id":415,"depth":500,"text":415},{"id":441,"depth":500,"text":441},{"id":460,"depth":500,"text":461},{"id":509,"depth":406,"text":509},"2020-10-25","md",{"layout":546,"status":547,"published":548,"author":549,"author_login":551,"author_email":552,"wordpress_id":553,"wordpress_url":554,"date_gmt":555,"excerpt":556},"post","publish",true,{"display_name":550,"login":551,"email":552,"url":514},"风铃","flinkor","flinkor@foxmail.com",3087,"\u002F?p=3087","2020-10-25 13:24:15 +0000",{"type":7,"value":557},[558],[10,559,12],{},"\u002F2020-10-25-kine-driver-note",{"title":5,"description":12},"_legacy\u002F2020\u002F2020-10-25-kine-driver-note",[564],"Animation","TfXlUuRjzV9y-L1RkRFoOxZUtX1_A8Nk1XSHmiIagGE",1788763181219]