【转载】[Houdini 笔记] VEX 并不可怕

772 阅读4分钟

原文链接:《Houdini 笔记_VEX 并不可怕》 | 作者:一只飞奔的猪

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

  1. (int i): 迭代_计数器用于跟踪循环运行次数的变量的初始化
  2. (i<iterations): 条件_检查 if 是否为真的条件基本上就是循环中的 if 部分
  3. (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 语句测试条件,如果为真,则循环 whileend 语句之间的指令,然后重复。

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(){}

注意:如果 ifelse 一起使用时,那么程序都会视每一个 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 以引起变化。例如属性 CdPNv

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 的许多 SOPDOP 级节点中常用

e.g.

@id = @ptnum;

Indexing Variables

houdini 生成的属性,以跟踪元素的数量。总点数(i@numpt),当前点数(i@ptnum)等