Houdini VEX 学习笔记 ( 一)
//VOP
//Wrangle
1 vector min; 2 vector max; 3 getbbox(min,max); 4 float thetamax = 4; 5 matrix3 matrix_ini = {{1,0,0},{0,1,0},{0,0,1}}; 6 float rotAngel = fit(@P.y,min.y,max.y,0,1); 7 8 rotate(matrix_ini,rotAngel*thetamax,{0,1,0}); 9 @P = @P * matrix_ini ;
//Define a label deftype , a tab Bend , deftype is a menulist
1 #pragma label deftype "Operation" 2 #pragma label offset "Offset Value" 3 #pragma group XFORM offset deftype 4 5 #pragma choice deftype “0” “Bend" 6 #pragma choice deftype "1" "Twist" 7 #pragma choice deftype "2" "Taper"
// houdinivex 中的一个小片段,比较有用,摘抄下来
//会以当前点为基准,查找最近的10个点的速度,最后更新加速度
1 float max_radius = 99999; 2 int max_neighbours = 10; 3 int handle = pcopen(@OpInput1,"P",@P,max_radius,max_neighbours+1); //因为我们查找的是第一输入端(@OpInput1),所以会把自己算进去。因此需要11个点 4 5 int near_pt; 6 int near_count = 0; 7 vector near_v; 8 vector accum_v = {0,0,0}; 9 while( pciterate(handle)) 10 { 11 pcimport(handle,"point.number",near_pt); //如果是自己,就跳过 12 if( near_pt == @ptnum ) 13 continue; 14 15 pcimport(handle,"v",near_v); 16 accum_v += near_v; 17 near_count++; 18 } 19 if(near_count != 0) 20 { 21 accum_v /= (float)near_count; 22 v@accel += (accum_v - @v) * 0.4 ; 23 }
// houdini 中使用copy节点进行模型替代粒子时,会遇到这样的问题,可以设置N为速度的方向,但是up方向不好确定(不能一直是{0,1,0}),下面是一个简单的方法
1 v@N = normalize(@v); 2 v@up = {0,1,0}; 3 v@side = normalize(cross(@N,@up)); 4 v@up = normalize(cross(@N,@side)); //side跟N是正交的,可以求出side后,利用它得到新的up
//如何求出两个向量a,b之间的夹角,并且判断b是在a的左边,还是右边,下面摘抄的代码简单的实现了这一个效果
// a = {0,1,0} b = {1,2,0} b可以随机,只要在xy平面上
1 // 返回 a b 之间的夹角, 0 到 pi 2 float angle_between(vector a; vector b) { 3 return acos(dot(normalize(a), normalize(b))); 4 } 5 // 2d xy 平面 6 // b在a左边,返回 -1 ; b在a右边,返回1。观察方向是从Z轴负方向。 7 int left_or_right(vector a; vector b) { 8 vector cp = normalize(cross(normalize(a), normalize(b))); 9 if (cp.z < 0) 10 return 1; 11 else 12 return -1; 13 } 14 vector a = point(0, "P", 1) - point(0, "P", 0); 15 vector b = point(1, "P", 1) - point(1, "P", 0); 16 float ang = angle_between(a, b); 17 int d = left_or_right(a, b); 18 if (d == -1) { 19 printf("direction: left\n"); 20 printf("degrees : %d\n", degrees(ang)); 21 }else { 22 printf("direction: right\n"); 23 printf("degrees: %d\n", degrees(ang)); 24 }
// 学习VEX时整理的一个snippet, 代码实现了Noise的全部功能
1 float turb_noise(vector sample_point; //采样点 2 vector frequency; // 频率 3 vector offset; // 偏移 4 float roughness; // 当前这一层noise的值的倍数,用于加到累积和中。 5 float lacunarity; // 每加一层noise,在计算下一层noise时会把频率乘lacunarity 6 int octaves; // 多少层noise 7 float exponent) // 倍增,可以平滑曲线(把直线平滑成曲线) 8 { 9 float sum = 0; 10 float weight = 1.0; 11 vector samp_p = sample_point * frequency + offset; 12 13 for(int i = 0; i<octaves+1; i++) 14 { 15 sum += pow(noise(samp_p),exponent) * weight *4; 16 samp_p *= lacunarity; 17 weight *= roughness; 18 } 19 return sum; 20 } 21 vector frequency = chv("frequence"); 22 vector offset = chv("offset"); 23 float roughness = chf("roughness"); 24 float lacunarity= chf("lacunarity"); 25 int octaves = chi("octaves"); 26 float exponent = chf("exponent"); 27 float scale = chf("scale"); 28 float value = turb_noise(@P,frequency,offset,roughness,lacunarity,octaves,exponent); 29 @P.y += value;
在Houdini中得到的效果如下:
Vex 中利用通配符判断点在多个组内
在vex中没有函数pointgroupmask,注:某一个指定组时可用inpointgroup,这里讨论的是多个组。可以在wrangle中新建一个string 类型,利用表达式函数pointgroupmask函数表达式选出组,wrangle中代码如下:
1 int result = 0;
2 string chosengroups[] = split( chs("maskparm") );
3 foreach(string group;chosengroups)
4 {
5 result = inpointgroup(0,group,@ptnum);
6 }
Vex 中引用全局变量
使用$符号
1 float nf = `$NFRAMES`; 2 printf("%s\n",nf);
返回帧数:
240
利用Vex后期加点
在做特效的时候经常要用一些曲线辅助,有时候曲线是之前调过的(一堆transform,edit节点飘过),只想在后面加点,下面vex可以帮助我们做到这些
注:已经把它升级为houdini shelf 工具,更方便 ,见帖:http://www.cnblogs.com/peng-vfx/p/5193750.html
1 int pt = addpoint(geoself(),vector( point(geoself(),"P",npoints(geoself())-1) ) + set(0,0,0.1) ); 2 int vt = addvertex(geoself(),0,pt);
下面图片是前后对照:
粒子沿曲线运动
实现了Boids Behavior 的 Separation、Alignment、Cohersion这三个基本功能后(http://www.red3d.com/cwr/boids/),根据一些艺术效果可能需要集群沿着曲线运动,Houdini 中有popcurveforce节点,个人感觉用起来不方便,自己写了一段vex,个人感觉用起来挺顺手。
1 float min_dist = ch("min_dist"); // 参数最小距离 2 float max_dist = ch("max_dist"); //参数最大距离 3 int handle; 4 //near point on curve 5 int index = 0; 6 int old_index = i@init_goal; //记录单个个体追寻的曲线上点的序号 7 //curve length 8 float curve_length = 0; 9 v@steer_before = v@steer_cur; 10 handle = pcopen(1,"P",@P,10000,30); 11 vector steer_attract = set(0,0,0); 12 vector steer_dir = set(0,0,0); 13 int near_pt = i@init_goal; 14 int sum = 0; 15 if(pcnumfound(handle)>0 ) 16 { 17 while(pciterate(handle)) 18 { 19 int source_curve = 0; 20 pcimport(handle,"curve_id", index); 21 pcimport(handle,"source_curve",source_curve); //提取曲线的id 22 pcimport(handle,"N",steer_attract); 23 pcimport(handle,"P",steer_dir); 24 pcimport(handle,"sum",sum); 25 pcimport(handle,"perimeter",curve_length); 26 //if(index >= i@init_goal && (index-near_pt<3.6)) 27 if((index > i@init_goal) && (i@source_curve==source_curve) ) //是集群沿着曲线往前走 , 用i@source_curve记录追随那条曲线id 28 { 29 if(index - near_pt <=3) //每个substep不能走超过3个点 30 { 31 //near_pt = index; 32 //steer_dir = normalize(steer_dir - @P)* fit(length(steer_dir - @P),0,1,0,10); 33 i@init_goal = index; 34 break; 35 } 36 } 37 else 38 continue; 39 } 40 } 41 //found no points //如果没有发现点,执行下面代码 42 if(@init_goal<= old_index) 43 { 44 @init_goal+=1; 45 int num_pt = findattribvalcount(1,"point","curve_id",@init_goal); 46 for(int i=0;i<num_pt;i++) 47 { 48 int temp_pt = findattribval(1,"point","curve_id",@init_goal,i); 49 int source_cur = point(1,"source_curve",temp_pt); 50 if(source_cur == i@source_curve) 51 { 52 steer_dir = point(1,"P",temp_pt); 53 steer_attract = point(1,"N",temp_pt); 54 break; 55 } 56 } 57 } 58 v@steer_r = steer_dir; 59 /** 下面if这段代码不需要60 if(@Frame>6) 61 { 62 steer_dir += (rand(@id*7.4)-0.5)*0.003*{0,1,0}*index; 63 } 64 ***/ 65 vector dir = (steer_dir - @P)*( float(index)/sum ); 66 float curve_ratio = float(index)/sum ; 67 vector f1= normalize(dir)*chf("strength")*(rand(@id*6.7)*0.6+0.4); 68 vector f2= normalize(steer_attract)*chf("strength")*(rand(@id*6.7)*0.6+0.4)*(curve_ratio*0.5+0.8); 69 //v@steer_attract = lerp(f1,f2,fit(curve_ratio,0,0.25,0,1)); 70 v@steer_attract = lerp(f1*14,f2*70,fit(length(steer_dir-@P),0.08,0,0,1)); 71 v@steer_attract *= (rand(@id*104.6)+0.01);