原视频:https://www.youtube.com/playlist?list=PLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI
Bili:Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili
Houdini版本:19.5
1、函数概述
函数可以理解为,把一个经常使用的代码块\动作提取出来(百度解释:是一个用于执行特定的任务并返回结果)。
文档速查:
点击直达VEX Functions
点击直达VEX language reference
点击直达VEX contexts
(快速打开文档:在Houdini中点击右上方的?号,复制地址到浏览器打开即可)
2、无返回值函数void function
以 append()函数为例,官方文档解释截图如下:
append()意思为,将 ‘值’ 添加到数组或字符串中
eg.创建类型为detail的attributewrangle节点(void_function),写入下面代码:
int values[] = array(); //创建一个空数组
for(int i=0; i<100; i++){
append(values, i); //依次将i值加入到数组
}
i[]@values = values;
//数组values[]值为0~99
3、有返回值函数return function
以len()函数统计长度函数为例 ,官方文档解释
eg.创建类型为detail的attributewrangle节点(vreturn_function),写入下面代码:
int values[] = array(); //创建一个空数组
for(int i=0; i<100; i++){
append(values, i); //依次将i值加入到数组
}
int size = len(values); //len函数返回一个整数int值,值为参数的长度
i@size = size;
//结果为:size值为100
4、函数练习1—寻找最近点
eg.使用nearpoints()函数寻找最近点看,点击查看函数官方文档说明。
创建3个节点:小球Sphere、Scatter(ForceTotalCount改为100)、类型为detail的attributewrangle节点(function_parameters1)。将它们依次连接。再添加一个Add节点,点击+号添加1个点,并对点进行移动(可直接拖动或直接在属性面板更改位置)
在AttributeWrangle节点(function_parameters1)中写入以下代码:
常用使用方法一:
vector pos = point(1, 'P', 0);
int npt = nearpoint(0, pos);
i@npt = npt;
setpointattrib(0, 'Cd', npt, set(1, 0, 0)); //距离Add最近的点设置为红色
常用使用方法二:先对点进行分类或筛选,对方法一代码进行修改如下
vector pos = point(1, 'P', 0);
int npt = nearpoint(0, '@P.x>0.0', chf('dist'));
//此处的参数('@P.x>0.0')可以使用Group节点替代
i@npt = npt;
setpointattrib(0, 'Cd', npt, set(1, 0, 0));
结果为:
5、函数练习2—相交点
使用 intersect()函数 寻找相交点,点击直达官方文档说明。
intersect()函数常用于太阳/灯光照射物体在地面的对象上的投影。
intersect() 函数:计算光线与几何图形的第一次交点。与图元primitive相交则返回相交点primtnum,不相交返回-1
geometry:与第几个输入点的对象相交,0为第一个输入点对象,1位第二个输入点对象…
vector orig :射线原点
vector dir :射线方向和距离(向量长度)。
&p :射线与primitive相交的点。
&u,&v,&uvw :射线与primitive相交的UV(3D物体在2D平面的展示?)。
eg.创建3个节点:圆环torus(向上稍微移动并稍微旋转)、Scatter、类型为Points的attributewrangle节点(output_parameter)。将它们依次连接。再添加一个grid1节点,连接在attributewrangle节点(output_parameter)的第二个输入点:
在AttributeWrangle节点(function_parameters1)中写入以下代码:
vector p;
float u, v;
int inter = intersect(1, @P, set(0, -10, 0), p, u, v);
//圆环torus的所有点(参数:@P),沿-Y轴方向发射距离为10的射线,检测相对对象为第二个输入点的对象(第一个参数:1)
//P、U、V:有相交,则被重写为相交点的P、U、V
@P = p;
i@inter = inter;
//属性inter为相交的primtnum点,没相交的点值为-1
结果为:圆环被投射在网格Grid 上,下图为前后结果对比:
6、自定义无返回值函数(点击查看官方文档说明)
创建类型为detail的attributewrangle节点(user_defined_void_function),写入下面代码:
//无返回值函数基本用法: void 函数名(变量类型;变量类型;……)
void sprialPos(float ang, rad; vector pos){
vector newpos = pos + set(cos(ang), 0, sin(ang)) * rad;
int pt = addpoint(0, newpos);
}
vector pos = (0, 0, 0);//point(1, 'p', 0);
for(int i=0; i<100; i++){
//调用函数,函数名字、变量类型一一对应
sprialPos($PI * chf('ang')*i, i*chf('rad'), pos); //循环内,角度ang、半径rad递增
}
结果随角度ang、半径变化。
下图为半径不变,滑动角度ang(值0~1)的结果:(如果把点相连,结果就更直观了,可以去看第十四节笔记的【5、练习2——Sprial Rotation螺旋旋转】的结果)
7、自定义有返回值函数
与【6、自定义无返回值函数】类似,对其代码进行修改
//Function 返回的数据类型 函数名(变量类型;变量类型;……)
function vector sprialPos(float ang, rad; vector pos){
vector newpos = pos + set(cos(ang), 0, sin(ang)) * rad;
return newpos;
//返回一个向量
}
vector pos = (0, 0, 0);
for(int i=0; i<100; i++){
//调用函数,函数名字、变量类型一一对应
vector npos = sprialPos($PI * chf('ang')*i, i*chf('rad'), pos);
int pt = addpoint(0, npos);
}
函数需要与return配合使用
代码运行结果与【6、自定义无返回值函数】相同。
eg扩展.对上面的函数代码加入if-lese条件判断,使它在0~180°、180°~360°之间执行不同动作
//Function 返回的数据类型 函数名(变量类型;变量类型;……)
function vector sprialPos(float ang, rad; vector pos){
float val = ang % ($PI *2.0); //余数运算 $PI *2.0 = 360°
if(val < $PI){
vector newpos = pos + set(cos(ang), 0, sin(ang)) * rad;
return newpos;
}else{
vector newpos = pos - set(cos(-ang), 0, sin(-ang)) * rad;
return newpos;
}
//返回一个向量
}
vector pos = (0, 0, 0);//point(1, 'p', 0);
for(int i=0; i<100; i++){
//调用函数,函数名字、变量类型一一对应
vector npos = sprialPos($PI * chf('ang')*i, i*chf('rad'), pos);
int pt = addpoint(0, npos);
//循环内,角度ang、半径rad递增
}
结果:生成的点相似镜像,感兴趣可自行尝试。
8、自定义输出参数函数
在函数变量内使用 export关键字+数据类型+变量名字(与【7、自定义有返回值函数】类似)
eg、对【6、自定义无返回值函数】的代码进行修改
//与【7、自定义有返回值函数】相似 多了个export关键字来定义变量
//void 函数名(变量类型;变量类型;……; export 数据类型 参数名字)
void sprialPos(float ang, rad; vector pos; export vector spos){
vector newpos = pos + set(cos(ang), 0, sin(ang)) * rad;
spos = newpos;
}
vector pos = (0, 0, 0);
for(int i=0; i<100; i++){
vector spos;
//调用函数,函数名字、变量类型一一对应
sprialPos($PI * chf('ang')*i, i*chf('rad'), pos, spos);
//可直接使用函数的变量spos,相当于返回值(需自定义)
int pt = addpoint(0, spos);
}
代码运行结果与【6、自定义无返回值函数】相同。
9、导入外部函数—houdini外部函数
可以理解为导入脚本,使用【#include "脚本名字"】导入。houdini的外部函数在【安装目录\vex\include】内,格式为【.h】结尾。
eg.以函数groom.h为例,使用其中的【调整prim曲线长度】功能,其函数声明为:
如下图创建节点并进行设置:对字体设置为描边并进行重新采样(均匀分布并增加点,使用measure节点对字体描边进行测量)
创类型为Pirmitives的attributewrangle节点(include_external_function1),写入下面代码:
#include "groom.h"
//使用函数前,需使用#include关键字导入脚本
//根据具体用法调用函数
adjustPrimLength(0, @primnum, @perimeter, min(@perimeter, chf('length')));
//@perimeter为measure节点测量字体描边长度值
//min(a,b)函数取括号内最小值
结果为:字体随数值length变大逐渐显现
10、导入外部函数—自定义函数
使用【#include "全路径/脚本名字"】导入脚本。
eg.把【7、自定义有返回值函数】的函数代码
void sprialPos(float ang, rad; vector pos; export vector spos){
vector newpos = pos + set(cos(ang), 0, sin(ang)) * rad;
spos = newpos;
}
使用记事本保存命名为【 sprialPos】,并将格式改为【.h】,
创建类型为detail的attributewrangle节点(include_external_function2),写入下面代码:
#include "D:\HoudiniProject\Strongest VEX\sprialPos.h"
//脚本路径在当前项目下,路径可写为 "$HIP\sprialPos.h"
vector pos = (0, 0, 0);
for(int i=0; i<100; i++){
vector npos = sprialPos($PI * chf('ang')*i, i*chf('rad'), pos);
int pt = addpoint(0, npos);
}
代码运行结果与【6、自定义无返回值函数】相同。