houdini中不得不掌握的技术集锦(2) 不要使用stamp函数
—— 微资讯 · 微课程 ——
利用零碎时间,走上超神之路!
原文地址:http://www.tokeru.com/cgwiki/index.php?title=Copy,_Stamps,_Rotation,_Hscript,_Vex,_a_ramble
由CG猎人独家翻译,转载请注明
通过一个看似简单实则包含N多重要知识的案例讲述了copy复制中控制旋转,方向,大小和动画等技巧。解释了为什么不要使用hscript和copy中最长用的copy,并且使用属性的方法完美替代了stamp函数的功能,只得大家好好学习。
Gery Jaeger 在sidefex的邮件中展示下面这个从无到有的圆圈效果,询问有没有更好的方法制作这个效果。我也做了下面这个场景,觉得做一些笔记和说明会挺好的,对于新人来说做一些基本的知识介绍。
当我看到Gary的视频,我把他拆分成下面一些步骤:
使用circle圆圈,使用copy节点形成多个圆圈;
单独选择每个拷贝的圆圈
在使用copy节点把box拷贝到circle上
很简单,但是有2点序号考虑好
刚开始是最忌的状态,然后完成对齐
刚开始每一circle只有一个box,后面慢慢生长紧密排列成一个圆形。
我最开始完成了基本的设置,然后专注也解决那两个棘手的问题。文件设置很简单,这个把我们带向第一个主要的问题,控制拷贝boxes的旋转问题。
大纲
1控制旋转
2开始使用随机旋转后面对齐
3不要在用hscript
4认真思考点
5Vex 和point wrangle节点
6 使用resample节点来制作平均的box
7删除点
8copy sop和stamps,不要使用它
9更深入的工作,最后的工作。
控制旋转
上面这个就是默认以cicle在旋转,并且使用copy节点拷贝到box到它上面
boxes现在在做一些有趣的旋转,但是不可控,并不是我们最后想要的结果
这个奇怪效果的原因很简单:copy节点需要查找点的某些属性来做旋转控制,目前circle上没有这些点
是时候添加一些属性了。如果我们添加一个N属性(代表法线),copy的box会沿着法线排列。添加一个normal节点似乎是最直接选择,直接在circle后面添就行了。
恩,没有发生什么改变;查看geometry spreadsheet,你可以看@N这个属性是被创建了,但是所有的元素的值都是0.看起来normal节点对于曲线没有什么作用。好吧,在houdini总是有其选择。添加一个facet节点,然后激活post compute normal 后期计算法线会让这个circle实体化,但是他们的法线是{0,-1,0},但是这个不好,我们希望boxes是向外,不是向下的。
polyframe节点可以对曲线创建切线和法线,我们试一下这个节点。
不错,我们有些效果。默认情况下也是给我们的法线方向是{0,-1,0},这个不是我们想要的。我试着在tangent 名字下输入名字为N,让切线变成法线,但是这个效果也不好。后面激活bitangent name改变为法线,这时候法线是对的,但是这个解决了我们的旋转问题吗?
部分解决。一开始他们感觉是对的(我这里添加了更多的box来说明)但是一旦circle开始旋转,有些box开始自旋,其他一些保持垂直向上。为什么呢?
这个和在maya中使用orient约束时候产生的aim+up的问题一样。一个单一的方向不够定义一个稳定的旋转,你可以举起你的手,但是你可以旋转你的椅子,面向任何一个面。你可以永远都指向上面,但是你的身体方向是没有定义的。
使用一个copy节点,如果你使用@N,你需要使用令一个属性 @up,让他形成一个固定的定向。让这个简洁清晰,我们让这个up反向垂直于法线。polyframe似乎可以让我做个,所以我们们使用它,把之前的法线N,写成up,看看我们能得到什么。
成功了,boxes现在稳定了,看起来像个圆圈了。
开始是速记旋转,后面是对齐
最暴力的方法是直接k关键帧。使用transform节点,我早期的测试,在第0帧设置一个较大的值,在第24帧设置为0,这时候个可行的方法。但是在houdini中让你着迷的是程序化,可重复使用的方法,我们称为优雅的解决方法。
所以,我们会使用一些表达式,虽然说我尽量避免使用hcript,但是暂时用一下,之后会介绍一个更好的方法
首先,我们要生成一个随机的x旋转值,在rotx输入框中设置
rand(2342)
这个结果是0-1的随机值,使用2342作为一个随机种子值来生成一个随机数。通常说来,rand()函数一般用来每一帧生成一个随机值,或者其他一些变化的事情。但是在这里,我们只需要一个不变的随机值(2342这个不是特殊的数字,我们可以任意数字或者使用$PI 也就是π)
因为我们做的是旋转,所以设置范围0-360会更加有用
rand(2342)*360
好点,那么怎么让他有动画呢?如果我们把这个值乘以1,那么结果是不变的,如果我们乘以0,那么结果就是0,如果我们乘以一个从依据时间帧1-0渐变的数,那么就获得了动画。
rand(2342) * 360 * <ramp from 1 to 0 over framerange>
比如我们想这个过程有30帧,那么我们可以把当前帧除以30,给我们0-1的过程
帧0的结果是0/30=0
帧15的结果是15/30=0.5
帧30的结果是30/30=1
所以这样应该可以($F代表的是当前帧)
rand(2342) * 360 * ( $F / 30 )
有效果,我们可以得到旋转,但是它是从0度开始而不是最后停在0度,而是一直在旋转。首先,让我们需要渐变的值的范围是0-1,使用功能clamp。
rand(2342) * 360 * clamp( $F / 30, 0, 1 )
效果好些,但是现在结果是从0开始的,我们的效果是最终降为0,这时候我们使用1-(称为compliment)
rand(2342) * 360 * ( 1 - clamp( $F / 30, 0, 1 ) )
成功了,如果我们需要其他旋转值,那么我们修改2342这个种子值,或者让这个动画快点或者慢点,其实就是修改30这个帧。
不要再用hscript
目前看起来很好,为什么不继续用呢?事情是这样的,如你也想y和z轴通道也有这个随机控制,你必须单独在另外两个通道中输入这些代码。
这个让我不爽,也希望你有这种感觉,主要有下面几个原因
一眼看下去,所有大代码都压缩在一起,你看不到他们在做什么
你不能一下全部编辑,你必须单独编辑
houdini,特别是wacom手绘板的方式,很难直接点击就可以编辑,你要全选表达式或者点击属性才能全选。
在一个选框中编辑然后tab跳转到另外一个选框的功能通常是失败的,有时候你放大了某个输入框,那你就不知道是哪个通道了
Hcrsipt比较倾向于单行写代码,对于稍微复杂的代码不好阅读(他们虽然可以分成不同的段落,但是用户好像都是在一个条线上写)
他并不比maya的表达式好多少,不管是可读性还是性能方面。
基于点思考
houdini非常鼓励你对点的操控(当然houdini用户也很喜欢控制点)。我看到很多houdini用户千方百计也会在点层级进行操作。但是maya用户直接控制物体的transform或者更高层级的物体,这是比较明显的选择。
我们目前的设置是有一个circle圆环,然后把box拷贝到圆环上的点。如果你删除一般半的圆,那么你也只能得到一半的box。如果你一直删除点,最后只保留一个点,那么就只剩下一个box
所以,一个点和一个box,然后链接到copy节点,会创建一个box的拷贝
现在同样的魔法,我们对circle也做同样的copy操作
一个点,一个circle连接到copy节点,会生产一个拷贝的circle
我们会在copy box的copy节点之前插入这个节点,我使用point generate 节点来创建这个点。
需要注意的是,pointGenerate 节点默认情况下创建5000个点,但是我们仅仅需要一个点;
为什么那么麻烦?因为我们现在有可以控制circle的点。当我们给这些点添加属性,我们有快速而强大的控制能力。
使用VEX语言和point wrangle节点
在point generate节点和copy节点间插入一个point wrangle节点
如果你查看参数窗口,你会发现中间有一个很大的代码输入区域。这个已经比hscript中的的输入框有很大的优势了。你可以看到你在做什么。
让我们开始先做一些简单代码:让circle向上移动(把之前用来制作圆圈旋转的transform节点删除,现在我们不需要它了)
@P.y = 0.5;
一旦你点击编辑窗口外面或者ctrl+enter,cirlce圆圈会向上跳动5个单位。我在houdiniVEXpage 页面讨论过VEX语言(之后文章会翻译)。但是现在做个非常简单的介绍。@ 是属性的标志;@P是点的位置属性,这个你可以在geometry spreadsheet中查看到。因为@P是一个带有xyz三个元素的适量,我们一直通过@P.y直接设置y轴元素的值。
好的,使用下面比较复杂的代码进行替换
float end;
float ramp;
vector rot;
end = chi('end');
ramp = @Frame/end;
ramp = 1 - ramp;
ramp = smooth(0,1,ramp);
rot = vector(rand(@ptnum))*2*$PI;
@orient = eulertoquaternion(rot*ramp,0);
@pscale = (@ptnum+1)/float(@numpt);
按输入框右边的小按钮会自动创建一个名字为end的滑竿属性。设置值为30,然后拖动时间线。你应该看得到圆圈在旋转,最后面在30中停止,让我们一步步地分析上面的代码:
float end;
float ramp;
vector rot;
创建浮点类型的点的变量,命名为end,ramp和一个矢量叫做rot
end = chi('end');
把自定义的通道end的值赋予给代码中定义的end的变量(当你点击那个小按钮,houdini会扫描整个VEX 代码查看ch()调用的参数,如果这个通道不存在,houdini会自动创建。chi()创建整数类型,ch()创建浮点类型数据。
ramp = @Frame/end;
ramp = 1 - ramp;
ramp = smooth(0,1,ramp);
这个和之前hscript中的表达式类似,我们获得当前帧(VEX中使用的是@Frame而不是$F),适配到1,反转,不用clamp直接限制,而是一个类似的函数smooth进行光滑过度,这个会生产一个很不错的渐入渐出效果。
有时候你会发现通过这些分行,这很容易读取,但是你也可以一行写完,但是没必要。
一些事情单独处理,一步步处理
@ptnum是当前点的id,因为目前我们只有一个点,所以序号是0
rand(@ptnum)生成一个随机的数值,类似于hscript,范围是0-1
vector(rand(@ptnum))生成一个取值范围在{0,0,0}到{1,1,1}之间的随机值,因为我们对于rot旋转是xyz三个元素控制。
vector(rand(@ptnum))*2*$PI 把获得的0-1的矢量,然后乘以0-2pi,为什么我们要使用2*PI呢?因为大部分的VEX的旋转函数使用的是弧度制而不是角度制2*PI的弧度=360°角度。所以我们确保随机值是在360都的旋转值里。
这个随机矢量最后赋予个rot这个变量,通过ramp进行缩放控制,这样结果就是从最开始的最大旋转至到最后衰减为0;
@orient = eulertoquaternion(rot,0);
我之前提到过copy节点需要一些属性控制旋转。@N和@up是其中一个方式,但是我更加倾向于使用那个@orient.@orient是一个四元素,一个4个之的矢量,这个你也许在旋转的时候听说过。他比rotation 函数更加好的关键在于他是独立存在的。仅仅靠@orient这个属性就可以给你一个稳定的旋转。
不好的地方是四元素对于人们来说不好理解。幸运的是,我们不需要理解它,Vex有很多方法把其他值转化为四元素,其中一个是eulertoquaternion()函数,也就是欧拉角转四元素。提供一个欧拉矢量值,旋转顺序是0代表的是xyz,这就可以创建一个四元素。
@pscale = (@ptnum+1)/float(@numpt);
获取点的id,除以全部点,把结果赋予给@pscale,确保我们不会有值为0的数,所以+1;因为@ptnum和@numpt都是整数,所以结果是整数形式,这不是我们想要的,所以在@numpt中通过float函数进行转化为浮点形式。
为什么是@pscale,为什么需要这么多额外的工作呢?下面就是为什么,选择point generate 节点,让场景播放,然后开始增加点的是数量,看看动态图中的效果
@pscale是另外一个copy节点可以识别的节点,他控制缩放,当我们添加更多的点,每个点活的更多的@pscale值,在0-1间细分。所以,如果是10个点会有pscale的值为0.1,0,2,0.3.。。。。。0.9,1.0.这意味着我们们获得10个等比例缩放的圆。
因为选择是被rand(@ptnum)随机控制,每个点都有它各自的随机旋转值,意味着每个圆圈都有自己的随机旋转值。
当这个圆圈的copy节点传递到第二个copy节点,继续按照之前的设定工作;查找每个点,并且把每个box拷贝到点上面,使用@N 和@up来旋转它。
使用resample节点来获得平均细分的点
resample节点连接一条曲线,然后沿着曲线添加更多的细分点。在默认设置上,在曲线每隔0.1个单位上添加额外的点。在第二个copy节点上插入resample节点,因为这是在circle节点拷贝完成之后加的重新采样,结果是每个圆圈都是等比细分。越外面的圆圈有更多的点,越内部的圆圈有更少的点。
刚好我们的box的大小是0.1,所以大小间隔刚好适合。
删除点
有了上面制作的经验,删除点应该是很容易的事情,我们会重新使用之前ramp的时间控制。这次包含对比圆圈的属性。
如果你想起曲线,曲线上第一个点的位置序号是0,最后一个点的序号是所有点的数量-1.要实现从0-1的渐变,我们使用之前相似的技术。使用当前id除以所有点的数量,我们会获得沿着曲线的一个uv坐标,实际上是u坐标。@ptnum和@numpt都是整数形式,所以你至少需要把其中一个转化为浮点,最后结果的类型才可能是浮点类型。
float u = @ptnum/float(@numpt);(只是提醒一下,@numpt是VEX语言中代表所有点的数量)所以目前我们有点的 u的值范围是0-1,同样有一个时间值0-1.所以我们需要比较的是u的值是否小于timer的值,如果是,删除点。
float end; float ramp; end = chi('end'); ramp = @Frame/end; ramp = 1 - ramp; ramp = smooth(0,1,ramp); float u = @ptnum/float(@numpt); if (u < ramp) { removepoint(0, @ptnum); }
最后一个函数需要两个变量,第一是指代哪个几何体,第二个是这个几何体上的哪个点?在这个案例中,我们使用的无论那个接在input1中的节点,其实是第0input。这个新的wrangle节点是插在resample节点,确保最后可以一个个删除box。
现在基本上完成,但是还不是很正确。让我们暂时关闭旋转看看发生了什么事情。
圆圈是一次删除而不是同时并行删除,这是因为我们在本地创建的@u属性,所以点的序号是基于所有圆圈的点而不是当个圆圈的点。
要修改这个问题,其实有很多方法可以实现:
1我们可以使用 connectivity 节点,可以把不相连接的物体识别出来,这里可以把每条曲线识别出来,给每个圆圈的点一个相同的@class属性,我们可以把这个属性考虑进计算@u;
2有个vex函数可以高数你当前点属于的多边形/曲线,查看当前曲线点的数量而不是左右点的数量;
3我们可以在最原始的circle中生成@u这个属性,这个属性会被拷贝到每个circle中,resample会自动帮我们插值计算点中的@u属性值,无论我们插入多少个点。
4uvtexture节点,设置模式为 rows and columns可以基于连续的曲线创建uv;确保是在point点层级,然后拷贝@uv中的第一个元素到@u这个是我们需要的,比如@u=@uv[0].
当然,第三种方法最方便,我们可以在原始circle下面添加一个wrangle节点,然后输入代码
看起来效果如何?
copy 节点和stamps函数,不要使用它
目前我们做这些事情都可以使用hscript完成,主要是使用stamp()函数。回到第一章我们有一个圆圈,一个transform和一个copy节点。如果我们要copy节点复制多个circle,他们旋转是一模一样的,因为transform在copy之前起作用。
stamp 表达式让你可以反方向操作,你可以通过copy节点创建一个随机旋转命名为$RANDROTX ,然后在rotx参数面板中你要使用下面的表达式
stamp('../copy1','$RANDROTX',0)这个表达式意思是会查找copy中定义的 $RANDROTX的值,如果不能获得这个值,那么使用默认0。copy节点知道哪个copy节点在运行所以知道当前旋转的随机值。我希望你和我一样对这个感觉不爽。houdini对比maya来说是非常简洁直接的从上到下的流程,这种倒过来的技术感觉不太对。更值得担心的是:如果你场景太大,copy中的stamp会一下子很卡,这个不是任何人想要的结果。虽然这个设置会和你的hip文件一起保存,但是尽量避开它。它有自己的作用,但是任何时候你能更好的从上到下流程创建的时候都不要使用它。更进一步工作,最后的提示我们在两个地方使用到ramp 时间控制,其实更好它存储为点的属性(提示,如果想在右边链接的copy的属性也可以传递下来,那么激活 use template point attribute使用模板中的点属性)旋转看起来有点无聊,它是否可以被一个更好的函数替代?或者被手key关键帧替代?那个反删除有点太完美了,他们在开始的时候需要有不一样;反删除的结尾也是太完美了,他们在不同的circle中应该在不一样的点结束也许有些反删除的速度要快于其他?当做debugging的时候,你可以在wrangle中插入一个中间属性,可以在geometryspreadsheet中快速查看。可以方便制作,比如@r=ramp,当你完成制作时候可以删除它。houdini允许你可以在点添加任意数量的属性(我看过一些实际工作中一个点有30多个属性)但是让geo更加干净整洁是有好处的,只有当你真正需要的时候才使用属性,不要太吝啬,也不要太疯狂。
—— CG猎人——
微信号:Hunter_CG
QQ-540710114
为您提供最具前景的咨询与最适合的分类技术与学习教程