Skip to main content

这是前一段时间在课上老师讲的一个技术点,对于理解houdini中的volume很有帮助,所以就梳理一下。

动态效果大家可以下载下面的工程查看下,我就不截取GIF图了,简单来说就是一个从内部核心向外推的效果。

链接:https://pan.baidu.com/s/11Bg6iIIt7G6O-MEEuNSTBA
提取码:8lq7

开始吧!

首先是给了两个volume场,一个是fog场,一个是SDF场。

可以使用一个vex函数将volume的体素信息转移到对应位置的点上进行查看。查看density属性后能发现:

density上的点中心都是1,只有外部边缘的一小层才会有一些小于1的数值,显示在Fog volume上的时候,内部都是实心的烟雾,只有外面有一层淡淡的减弱。也就是说,Fog volume的每个体素定义了Fog的浓度。

而SDF场中,内部的数值都是负数,外面会有一小层是正数。数值为0时表示SDF场的“壳”,非零数值代表了当前体素到离自己最近面的距离,负数表示在内,正数表示在外。这样只要通过简单的正负数判断我们就能得知当前位置是在SDF场的外面还是里面,这也是为什么我们经常拿SDF场作为碰撞检测体的原因。

还有一个要注意的是,不管是什么类型的volume,在houdini中都是primtive的存在,也就说它是最小的单元,比如点是一个primtive,一个primtive类型的球体也是primtive,它们都不能够再拆分了,这就意味着你不能打开volume去查看每一个体素的信息,只能在primtive中有一个volume名字的属性。

再下来,右边相同的三个节点中,通过创建的Fog场,分出了vel.x、vel.y、vel.z三个volume。因为fog场中,每个体素都只能承载一个float类型,而要创建速度场,需要三个相同的名称,后面分别带上.x、.y、.z才能够被houdini识别为一个矢量数据,当然在后续引用的时候,还是可以直接写作v@vel,而不用每次都些f@vel.x这种的。

name节点其实什么都没有做,只是把density改了一个名字,所以现在的vel三个通道都还是density的数值,因此即便merge后分别查看每个通道,其实都是一样的。primtive的作用是保证merge之后不显示vel场,只显示density就足够了。这里我们其实只是定义了一个叫做vel的矢量场,并没有进行任何操作,后面会有需要操作的地方。

左边的volume wrangle是把SDF场的每个体素信息传递给density的体素上。

@density = -volumesample(1,0,@P);

因为SDF场内部的数据是负值,所以这里写了一个负号,让数值变为正值。

注意这里使用的是volume wrangle,而不是point wrangle,但是还是使用了一个@P属性,这里的@P就不再是每个点的位置,而是每个体素的位置了。

之后将这四个volume都merge在一起,因为之前使用了primtive节点,让vel.x/y/z不可见,所以这里能看到的就只有继承了SDF体素数值的density了,能看到density不再是实心的,而是内部数值更高更浓,逐渐递减到外面的0,这个和SDF场体素的数值是一一对应的。

接下来就是设置vel场的体素数值,这里用到了volumegradient函数。这个函数可以获取volume内体素的浓淡的梯度变化数值,并返回一个vector数值,一个带有方向性的vector数据。可以想象下地形的梯度图,而这个函数标记的就是低到高的方向。

这里前面用的v@vel,其实也就是把返回的vector类型分别给到了vel.x/y/z三个float类型的volume上。那么此时vel场的数值的意义,就是标识了当前体素低浓度朝向高浓度的方向。

接着就是偏移volume了。

@density = volumesample(0,”density”, @P+v@vel*@Time*2);

这段vex很简单,但是可能会有一些抽象。

@density自然就是当前的density场,如果写作
@density = volumesample(0,”density”, @P);
将当前的density场向自身取值,按理说是不会有任何改变的,因为每个体素都向自己的位置取值,取到的还是自身的数值。

但是现在将自身的@P偏移了一个数值,方向是vel方向,所以density的取值就变为了向周围浓度高方向的体素取值,后面又乘了一个时间,那么这个取值在获取周围浓度高的体素数值后,每一帧都会向之前的方向再一次偏移数值然后取值,而因为density一开始就是内部厚,外部稀疏,偏移一定程度的时候,就偏移到了外部原本是0的位置,运动起来后就有了一个自内向外推的效果。

这里要注意的是,不管第几帧,每一次偏移都是根据原始density的分布去偏移,而不是第二帧是根据第一帧的结果去偏移,不是的。因为我们没有用到solver,houdini不记得上一帧发生了什么,所以每次我们都是根据上一个节点返回的结果去取值,而上一个节点的结果是一直不变的。之所以有动画,是因为vex中我们乘以了时间,所以相当于第一帧我们取了偏移1的位置,第二次我们取了偏移2的位置,连续起来是动了,但是两个帧之间是没有任何关系的。

不知道我有没有说明白…我应该说的非常明白了!

(完)

Leave a Reply