Houdini中角色通用修穿插方法

公司特效组最近一半的人一直都在做着修穿插这样的一个重复的事情,听说来公司之前大家也都一直这样做着,这种完全不能把艺术家从重复劳动中解放出来的状态确实有点让人神伤。最近本人也在做着这方面工作,想借着这个机会,总结一下自己这方面的经验,也重点探讨一个能够适用常规多数角色穿插问题的解决方法。希望能做到抛砖引玉的效果。

首先聊一聊角色动画中常见的两种穿插情况。

第一种比较常见的是角色与其他物体的穿插,比如手拿杯子脚踩地。这种情况的解决方法比较灵活,因为是两个物体,所以常常可以使用volume的那套方法找到穿插的位置在用gradient或者ray的方法解决穿插的问题。另外这种情况往往我们可以很清楚的定义穿插面和被穿插面,两个虽然理论上是相互影响的,但是处理起来我们完全可以忽略掉被穿插面(被角色碰到的区域)的变形问题,因为大多情况是根本不用变形,所以变形的变数是很好控制的。

还有另一种情况的角色穿插就是由于前端rigging流程没有做到百分中百的角色防穿插或者类似自己的手拂动自身的肉这种情况,首先我们是在一个solid surface上找到不同的面之间的穿插,如果引入volume的那套方法就意味着我们要依靠人为的判断,手动的在穿插之间的地方将角色砍开,在分别做处理。这种方法无疑是离不开重复劳动,而且如果穿插位置有好几处的话,大概拿到镜头的人就只会想怎么cheat了。

估计如果大家真有在houdini里面修穿插的经历后基本上都会了解怎么使用volume和ray来处理了。我这里只想重点聊一聊第二种情况下,如何程序化的搞定穿插问题。

解放艺术家的双手,让他们去做更有创造力的事情。  -- 机器猫

先看一看效果:

变形前

变形后

先讲一讲思路,再一个一个细聊:

1,使用intersect方法找到交叉位置

2,将穿插位置的法相方向统一

3,用新的法线在找出来的区域重新做一次intersect得到较准确的挤压偏移量displacement

4,找出交叉区域露在外面的边缘

5,柔化外边缘后根据法线和边缘权重向外挤出一点budge

6,统一给穿插以及边缘部位做平滑smooth

在整个过程中因为测试的几何体细分程度有比较大的不同,发现直接使用原mesh上的点做上面的采样与计算,结果是非常不稳定的,而且最后的动画穿插部位会有很严重的抖动问题。因为随着动画的运动,穿插部位的点进入穿插和离开穿插区域都会对之后的变形产生比较明显的影响,说白了就是采样点不够密。所以在这个讨论的改进版本中,使用了scatter给物体撒点来采样的方法。散的点密度越高,面片抖动的问题就会越小。这个方法要求的就是scatter撒的点在动画过程中相对位置是固定在mesh上的,这个使用uv的方法是不难实现的。

1,使用intersect方法找到交叉位置

用撒完的点云在三角面片化的mesh上进行一次intersect计算。判断穿插与未穿插其实也不难,如果我们沿着向外的法线去求解的话,如果碰到自己mesh的次数是奇数次的话那就说明这个点是在几何体的内部,如果是偶数次的话则说明是没有穿插的点。道理自己想。

值得一提的是如果使用intersect这个功能的话,碰撞的到的prim的法线方向与入射的法线方向接近90度的话是非常容易对下一次迭代的计算长生干扰而出现错误的。这种情况类似于入射的法线切过曲面表面。在这里我使用的是向量点乘的方法避开这种比较敏感的情况。另外针对于穿插的区域以及边缘区域,我在整个过程中使用的红色来代表的,这样可视化的程度也高一点。

这个可能算是这个方法里面最重要的一步了,就是用它找到了自身穿插的部位。

 1 vector startPos = @P + normalize(@N) * 0.00001;
 2
 3 vector rayDir = normalize(@N) * 100;
 4
 5 vector collidPos;
 6 float myu, myv;
 7 int flag = 1;
 8 int count = 0;
 9
10 while(flag){
11         int primNum = 0;
12
13         primNum = intersect(1, startPos, rayDir, collidPos, myu, myv);
14
15         if(primNum != -1){
16             //[email protected] = primNum;
17             vector primNor = prim(1, "N", primNum);
18             //[email protected] = primNor;
19
20             if(dot(normalize(primNor),normalize(@N)) > 0.05){
21                 startPos = collidPos + 0.00001 * normalize(@N);
22                 count++;
23             }else{
24                 break;
25                 }
26         }else{
27             break;
28         }
29 }
30
31 if(count % 2  != 0){
32
33     @Cd = set(1,0,0);
34 }else{
35     @Cd = set(0,0,0);
36 }

2,将穿插位置的法相方向统一

这个步奏就稍稍需要一点技巧了,统一或者模糊任何一个属性的话是需要与相邻的点进行平均。而穿插的问题就是不同法相的点已经搅合在一起了,所以使用穿插状态的点云来统一法相,效果会适得其反。这里我们就需要引入T pos模型了,做动画的肯定会有T pos这个状态的模型给你调用的。

解决法相统一的问题是为了之后沿着新的法相做偏移做准备的。因为穿插的部位往往是具有弧度的曲面,那么在挤出穿插部分时,如果使用原来的法相,有可能会出现挤出后点变得混乱。如图:

虽然上面示意的可能有点极端,但是这就是为什么我们不推荐使用原来的法相来进行挤出了。

这里说到的统一其实也是没有达到绝对意义上的一致,只是让法线与相邻点的法线更加有相关性。

3,用新的法线在找出来的区域重新做一次intersect得到较准确的挤压偏移量displacement

有了第一步做准备,这一步就没什么门槛了,再重复一次intersect,只不过这次只是在之前被检测的区域内反向进行计算。

4:找出交叉区域露在外面的边缘

这里我们使用的方法是根据原来检测出来的区域范围使用pcfilter的方法把选区扩大,当然filter会改变已有选取的值而且边缘也是渐变模糊的,我们直接使用大于0的判断重新把值变为1就好了。在用这个扩大了的选区减去之前的选区就得到了围绕穿插位置的一个圈圈选区了。这个过程用到了点云计算所以依旧是挪到T pos下面做的处理。

5,柔化外边缘后根据法线和边缘权重向外挤出一点budge

这一步没什么好讲的了,就是把上一步的选区柔化一下,然后当做权重值来挤出穿插的边缘区域,让碰撞的地方更有肉感一点。

6,统一给穿插以及边缘部位做平滑smooth

这里可以直接使用smooth节点,或者再之前使用neighbours找出邻居点并平均邻居点位置来确定自己的位置,这样能够更平滑一点。

终结一下,这个方法能完善的地方其实还有很多,比如如何然穿插偏移量更精确,以及让scatter点分布更均匀。尤其是scatter这个环节,H14上的scatter能够均匀分布了,但之前了解到的这种通过迭代来均匀分布的方法也不能100%的保证点云能够完全跟着动画mesh走。这些方面再以后有机会了慢慢完善吧。

最后是这个方法的一个文件,大家可以参考一下,也很希望得到意见与反馈。

修穿插源文件

时间: 2024-10-11 17:14:52

Houdini中角色通用修穿插方法的相关文章

Houdini中总结Volume Lattice的方法

这两天挪威大神不在,感觉有点寂寞.刚刚学习完他的一个牛逼工具Volume Lattice.鉴于他直接把这个工具已经拿到Orbolt里面卖钱了,我在这就只讲讲自己的学习理解,代码什么的就不在这上了,何况要是理解了方法其实零代码也能够自己实现出来.这里是他的工具连接,给这位牛逼的外国师傅做做广告:Volume Lattice 效果图: 这个工具的思路主要是使用点云来代替voxel,通过拉伸点云之后,求出每个点的位移向量,再把这个向量值转变为体积,最后使用这些向量来计算density新的位置. 制作步

ASIC设计中一种通用型并行设计方法

我是个"低调"的人,总不喜欢表达出来,对异性如此,对工作也是如此.在翔哥的鼓励下,决定把自己工作的一些经验和思考写下来,和同道们一起分享. ASIC设计中一种通用型并行设计方法: 1)流水网的概念提出 IC设计中的控制有串行和并行两种思想.状态机方法反应了串行控制思想,有软件的思路,比较好理解,新手比较喜欢用.流水线方法是并行处理的思想,比较抽象,因为其效率高,老手喜欢用.从"流水线"的名字就容易知道,它只是一维的一条线,一个设计中可以有很多条.小设计中可能很容易设

Houdini中全景摄像机shader立体左右眼成像方法

熟悉Houdini Shader部分的同学应该多多少少也了解camera自身也可以设定自己的shader.其中polar panoramic shader 能够非常方便的为艺术家渲染360全景视角的cg画面,但是这样渲染出来的画面只是单眼所看到的环境,如果引入立体双摄像机的渲染方法的话,默认的这个摄像机shader就会出现一个严重的问题,那就是所渲染出来的画面是分别以各自两台摄像机位置为原点所计算出来的.用文字说明可能有点绕口,看下图: 图片中我把摄像头在一个水平轴向上移动了一点,渲染出来的结果

SylixOS中GIC通用中断控制器(二)——GIC实现

1.概述 本篇文档主要介绍IMX6UL平台上基于SylixOS集成开发环境中GIC通用中断控制器的实现流程和方法. 2.GIC控制器基地址获取 GIC控制器基地址通过调用armPrivatePeriphBaseGet函数获得.如图 2.1所示,Ctrl+h局搜索armPrivatePeriphBaseGet函数,搜索结果如图 2.2所示. 图 2.1全局搜索armPrivatePeriphBaseGet函数 图 2.2 armPrivatePeriphBaseGet函数搜索结果 参考DDI046

使用ResultSet,写了一个通用的查询方法

此方法很烂,以后优化 /** * 通用的查询方法:SELECT */ @SuppressWarnings({ "unchecked", "rawtypes" }) public List testResultSet(String sql) { Connection connection = null; Statement statement = null; ResultSet rs = null; try { // 1.获取Connection connection

Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法

先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 2 的对应的属性赋值:属性即为 Map 的键,值即为 Map 的值. 使用 JDBC 驱动程序处理元数据 Java 通过JDBC获得连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列,数据类型,触发器,存储过程等各方面的信

Lua中的元表与元方法

前言 元表对应的英文是metatable,元方法是metamethod.我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算.在Lua中也有这个道理,两个table类型的变量,你是无法直接进行“+”操作的,如果你定义了一个指定的函数,就可以进行了.那这篇博文就是主要讲的如何定义这个指定的函数,这个指定的函数是什么?希望对学习Lua的朋友有帮助. Lua是怎么做的? 通常,Lua中的每个值都有一套预定义的操作集合,比如数字是可以相加的,字符串是可以连

为什么Java中有些接口没有任何方法

由于Java不支持多重继承,即一个类只能有一个父类,为了克服单继承的缺点,Java语言引入了接口这一概念.接口是抽象方法定义的集合(接口中也可以定义一些常量值),是一种特殊的抽象类.接口中只包含方法的定义,没有方法的实现.接口中的所有方法都是抽象的.接口中成员的作用域修饰符都是public,接口中的常量值默认使用public static final修饰.由于一个类可以实现多个接口,因此通常可以采用实现多个接口的方式来间接的达到多重继承的目的. 在Java语言中,有些接口内部没有声明任何方法,也

Android应用程序通用自动脱壳方法研究

Author: @爱博才会赢 本文为乌云峰会上<Android应用程序通用自动脱壳方法研究>的扩展延伸版. 0x00 背景及意义 Android应用程序相比传统PC应用程序更容易被逆向,因为被逆向后能够完整的还原出Java代码或者smali中间语言,两者都具有很丰富的高层语义信息,理解起来更为容易,让程序逻辑轻易暴露给技术能力甚至并不需要很高门槛的攻击者面前.因此Android应用程序加固保护服务随之应运而生.从一开始只有甲方公司提供服务到现在大型互联网公司都有自己的加固保护服务,同时与金钱相