Definition:定义
VEX 是一种高性能的表达语言,在 Houdini 中很多地方都用到了,比如写 shader。
VEX 评估通常非常有效,其性能接近已编译的 C/C++ 代码。VEX 不是脚本的替代品 ,而是一种更小、更高效的通用语言,用于编写着色器和自定义节点。
VEX 松散地基于 C 语言,但从 C++ 和 RenderMan 着色语言中汲取灵感。
VECTOR EXPRESSION LANGUAGE:Houdini 主编码语言。
@ATTRIBUTES @=attributes
HScript:Houdini 遗留的编码语言
DATA-TYPES:数据类型
- floats 浮点数是可以处理十进制值的数字. eg.3.14,6.9,8.002, etc.
- integers 整数是不能处理小数的数字. eg.1,2,3,4,-5,-6,-50, etc.
- vectors 向量是带有3个分量的浮点数. eg.(0.5,1.3,6), (4,21,18.5), etc.
- strings 字符串是非数字字符. eg. "hello world", "my name is Adam" etc.
OPERATORS:运算
+ - * / %
NODE:节点
- attribwrangle
vex 载体
Alt+E (打开独立 VEX 窗口)
VEX
- 数据类型+自定义名称=自定义变量(
float x;) ;: 结束一行代码;camelCase:编码命名习惯,组合词第二个单词和之后的首字母大写float/integer/vector/string+ 自定义变量名称 = 属性名定义. (int integerVar = 1;)- 数据类型+
@:则表示具体的某种类型(比如i@:则属性attribute则会变成整数 /v@myFirstAttribute = x;) - Tab:首行缩进
//:跳过,bypass
if
- if(条件检查){如果条件通过则执行};
e.g.:
float yPos = v@P.y;
if(yPos > 0){
v@Cd.x = 0;
v@Cd.y = 0;
v@Cd.z = 1;
}
- if(条件检查){如果条件通过则执行} else{否则执行这里的操作};
e.g.
float yPos = v@P.y;
if(yPos >= 0){
v@Cd.x = 0;
v@Cd.y = 0;
v@Cd.z = 1;
}
else{
v@Cd.x = 1;
v@Cd.y = 0;
v@Cd.z = 0;
}
Boolean
A TRUE OR FALSE (1 OR 0)
数学符号
e.g.
if(yPos >= 0 && xPos >= 0){
v@Cd.x = 0;
v@Cd.y = 1;
v@Cd.z = 0;
}
==/=
请记住,== 用于检查是否有两个东西匹配,而单= 是让左边等于右边
FUNCTIONS:函数
在后台作为指令运行的代码片段,我们可以使用函数名调用它们。它们通常需要 Arguments,也就是运行时需要的信息。
set(float,float,float)
返回一个包含由参数定义的组件的向量。在这种形式中,它允许我们将 3 个浮点数转换为向量(函数名称颜色会显示为绿色)
e.g.
float yPos = v@P.y;
float xPos = v@P.x;
if(yPos >= 0){
v@Cd = set(0,0,1);
}
else{
v@Cd = set(1,0,0);
}
if(yPos >= 0 && xPos >= 0){
v@Cd = set(0,1,0);
}
vector set(float,float,float)
一个从浮点数到向量/矩阵的转换函数
e.g.
v@Cd = set(0,1,0);
rand(float)
从种子创建 0 到 1 之间的随机数。
e.g.
v@Cd = rand(0.2);
@Frame
从时间线获取当前帧号
e.g.
v@Cd = rand(@Frame);
@ptnum
当前处理的点的编号
v@Cd = rand(@ptnum);
fit(value,(original)float,(original)float,(new)float,(new)float)
在一个 value 里面将旧的最小/最大值与新的最小/最大值匹配
rint(float)
将一个浮点数舍入为一个整数。3.6 变成了 4
removepoint(geohandle, point_number)
基于点号从给定的地理句柄(geohandle)中移除给定的点
e.g.
vector colorRand = rand(@ptnum);
float coloringFrame = rand(@ptnum);
coloringFrame = fit(coloringFrame,0,1,1,240);
coloringFrame = rint(coloringFrame);
//above are all initialized variable
if(@Frame < coloringFrame && @P.y < 0){
v@Cd = colorRand;
}
else{
//this control the else remove
removepoint(0, @ptnum);
}
CHANNEL FUNCTIONS:通道函数
LINEAR INTERPOLATION
线性只是指直线或顺序方式,而插值则是对中间值的 “猜测”
- lerp(value1,value2,blend)
基于从 0 到 1 的混合值在两个值之间进行线性插值
e.g.
float blend = 0.35;
v@P = lerp(v@P, 0, blend);
Channel
- ch = Channel
(chf==浮点型通道,chi==整数型通道...)
- chf('string name')
在属性 wrangle 上创建一个浮点型滑块参数
e.g.
float blend = chf('blend_value');
v@P = lerp(v@P, 0, blend);
e.g.
vector color1 = chv('color_1');
vector color2 = chv('color_2');
float colorblend = chf('colorblend');
v@Cd = lerp(color1, color2, colorblend);
- chramp('string name', float driver)
在属性 wrangle 上创建 ramp 参数,该参数由驱动程序变量驱动
e.g.
float colorblend = chf('colorblend');
v@Cd = chramp('color_ramp', colorblend);
@Time
这是沿着时间轴以秒而不是帧计算的时间,它被计算为 currentFrame/frameRate 。
e.g.
float blend = @Time+@P.y+@P.x;
float positionBlend = chramp('position', blend);
v@P = lerp(v@P, 0, positionBlend);
v@Cd = chramp('color_ramp', blend);
Ramp
每个整数后都会重置,十进制。'same as'的意思是值相同。然而,它更像是一个循环 0.7 和 1.7、1 和 2 其实都是循环中的同一个位置。
LOOP:循环
Array
层级关系:ARRAY--COMPONENT--INDEX
用法:FLOAT ARRAY[]
e.g.
float myArray[] = {0, 2.5, 3, 0.95, 12};
@testAttribute = myArray[2];
f[]@testArray = myArray;
可以通过 index 更改 Array 中的值,并且多的 index 之前的内容自动补全为 0
e.g.
float myArray[] = {0, 2.5, 3, 0.95, 12};
myArray[3] = 6;
myArray[5] = 12;
myArray[8] = 90;
f[]@testArray = myArray;
foreach([element_type] value; array){}
遍历数组中的所有元素,并将每个索引处的数组值赋给数组值
e.g.
float myArray[] = {0, 2.5, 3, 0.95, 12};
float sumArray = 0;
foreach(float arrayValue; myArray){
sumArray += arrayValue;
}
@sum = sumArray;
- 扩展:(一个点与它周围的点共享属性)
float neighbours[] = {point1,point2,point3,point4};
foreach (point, neighbours){
//infect this point
}
for(int i; i<iterations; i++)
Tips:i=iteration
(int i): 迭代_计数器用于跟踪循环运行次数的变量的初始化(i<iterations): 条件_检查if是否为真的条件基本上就是循环中的if部分(i++): 增量_这是我们在代码运行后增加迭代计数器的地方
for(iterationVar; condition; increment){...}
运行若干次迭代,直到条件不再为真
e.g.
int sum = 0;
int numberOfIterations = chi('iterations');
for(int i = 0; i<numberOfIterations; i++){
sum += 2;
}
@sumAttribute = sum;
While
while (condition) ... end
while 语句测试条件,如果为真,则循环 while 和 end 语句之间的指令,然后重复。
e.g.
int trueCheck = 0;
float testValue = 0;
while(trueCheck==0){
testValue += 10;
if(testValue>1000){
trueCheck = 1;
}
}
@checkTest = trueCheck;
@valueTest = testValue;
案例:感染
e.g.
int numberOfpoints = chi('Number_Of_Points');
float radius = chf('search_radius');
if(@infected == 1){
int neighbourPointNumbers[] = nearpoints(0, @P, radius, numberOfpoints);
foreach(int pt; neighbourPointNumbers){
setpointattrib(0, 'infected', pt, 1, 'set');
}
}
CLARIFICATION
- toggle
e.g.
int toggle = chi('toggle');
vector color = {1,0,0};
if(toggle == 1){
color = set(0,0,1);
}
v@Cd = color;
- if(){}else if(){}
注意:如果 if 和 else 一起使用时,那么程序都会视每一个 else if 为单独的条件执行!避免了多个 if()else{} ;时只会执行最下面的那个。
e.g.
四季变化案例
int seasonNum = chi('season');
string seasonString;
if(seasonNum == 0){
seasonString = 'summer';
}else if(seasonNum == 1){
seasonString = 'autumn';
}else if(seasonNum == 2){
seasonString = 'winter';
}else if(seasonNum == 3){
seasonString = 'spring';
}else{
seasonString = 'summer';
}
s@season =seasonString;
e.g.
树叶季节颜色
string foundSeason = detail(0, 'season');
if(foundSeason == 'summer' || foundSeason == 'spring'){
v@Cd = {0, 1, 0};
}
if(foundSeason == 'winter' || foundSeason == 'autumn'){
v@Cd = {1, 0, 0};
}
Common Attributes
Houdini 可以识别常见 Atrributes ,并且直接使用 Atrribute 以引起变化。例如属性 Cd、P、N、v
v@v
控制 速度 的矢量属性,用于 DOP 网络和渲染器
e.g.
v@v = {0, 10, 0};
@pscale
用作 统一大小 的浮点值。主要用于实例化和 copy to point ,但也用于 DOPS 中的碰撞大小
e.g.
@pscale = rand(@ptnum)*0.05;
@scale
作为 缩放向量 的向量属性。主要用于实例化和复制到点。
e.g.
v@scale = rand(@ptnum)*0.05;
v@scale.y = 1;
@N
一个 控制法线 的矢量属性。用于基本的方向和照明计算
@id
一个整数属性,我们可以用来 唯一地识别 我们的点,即使点的数字是变化的。在 houdini 的许多 SOP 和 DOP 级节点中常用
e.g.
@id = @ptnum;
Indexing Variables
houdini 生成的属性,以跟踪元素的数量。总点数(i@numpt),当前点数(i@ptnum)等