着色器语言
IdeaXR 所提供的着色语言是基于流行的 OpenGL 着色语言(GLSL)的简化。支持大多数数据类型和函数。有关着色器和 OpenGL 着色语言的总体介绍,请参考 The Book of Shaders。
支持的数据类型
类型 | 描述 |
---|---|
void | Void 数据类型,只对不返回任何内容的函数有用。 |
bool | 布尔数据类型,只能包含true 或者 false 。 |
bvec2 | 布尔值的二分量向量。 |
bvec3 | 布尔值的三分量向量。 |
bvec4 | 布尔值的四分量向量。 |
int | 有符号标量整数。 |
ivec2 | 有符号整数的二分量向量。 |
ivec3 | 有符号整数的三分量向量。 |
ivec4 | 有符号整数的四分量向量。 |
uint | 无符号标量整数,不能包含负数。 |
uvec2 | 无符号整数的二分量向量。 |
uvec3 | 无符号整数的三分量向量。 |
uvec4 | 无符号整数的四分量向量。 |
float | 浮点标量 |
vec2 | 浮点值的二分量向量 |
vec3 | 浮点值的三分量向量 |
vec4 | 浮点值的四分量向量 |
mat2 | 2x2 矩阵,按列的主要顺序。 |
mat3 | 3x3 矩阵,按列的主要顺序。 |
mat4 | 4x4 矩阵,按列的主要顺序。 |
sampler2D | 用于绑定2D纹理的采样器类型, 以浮点(float)形式读取。 |
isampler2D | 用于绑定2D纹理的采样器类型, 以有符号整型(int)形式读取。 |
usampler2D | 用于绑定2D纹理的采样器类型, 以无符号整型(uint)形式读取。 |
sampler2DArray | 用于绑定2D纹理数组的采样器类型, 以浮点数(float)形式读取。 |
isampler2DArray | 用于绑定2D纹理数组的采样器类型, 以有符号整数(int)形式读取。 |
usampler2DArray | 用于绑定2D纹理数组的采样器类型, 以无符号整数(uint)形式读取。 |
sampler3D | 用于绑定3D纹理的采样器类型, 以浮点(float)形式读取。 |
isampler3D | 用于绑定3D纹理的采样器类型, 以有符号整型(int)形式读取。 |
usampler3D | 用于绑定3D纹理的采样器类型, 以无符号整型(uint)形式读取。 |
samplerCube | 用于绑定Cubemaps的采样器类型, 以浮点(float)形式读取。 |
构建
接下来介绍一些常用类型的定义方法
向量
// 输入对应数量的标量
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// 也可以使用其他向量填充,或者向量+标量
vec4 a = vec4(vec2(0.0, 1.0), vec2(2.0, 3.0));
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// 整个向量使用同一个标量
vec4 a = vec4(0.0);
与glsl相同,向量类型的单个标量成员通过 "x" , "y" , "z" 和 "w" 成员访问。另外, 使用 "r" , "g" , "b" 和 "a" 也可以,而且是等效的。
vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
float b = a.r; //a.r = a.x = 0
矩阵
构建矩阵类型需要与矩阵相同维度的向量. 你也可以使用 matx(float)
语法构建一个对角矩阵. 相应地, mat4(1.0)
是一个单位矩阵.
mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 m4 = mat4(1.0);
矩阵也可以由另一维的矩阵建立. 有两条规则. 如果一个较大的矩阵是由一个较小的矩阵构建的, 那么额外的行和列将被设置为它们在单位矩阵中的值. 如果一个较小的矩阵是由一个较大的矩阵构建的, 则使用较大矩阵的顶部和左矩阵.
对于矩阵,使用m[column][row]
索引语法来访问每个标量,或者使用m[idx]
来通过行索引访问向量。
mat4 m4 = mat4(1.0);
mat2 m2 = mat2(m4); //取m4的左上角2x2矩阵
float a = m4[1][1]; //a = 1.0
数组
数组是存放多个相同类型变量的容器。
局部数组
局部数组在函数中声明。
void fragment(){
float arr[3];
}
要访问数组元素,使用索引语法:
float arr[3];
arr[0] = 1.0 //setter
COLOR.r = arr[0] //getter
数组还存在一个length内置函数,用于获取数组的大小。
float arr[] = {1.0,2.0,3.0}
for(int i = 0; i < arr.length(); i++){
//...
}
注意
如果你使用一个低于0或大于数组大小的索引,着色器将崩溃并中断渲染。为了防止这种情况,请使用length()
,if
或clamp()
函数来确保索引在0和数组的长度之间。
常量
在变量声明前使用const
关键字,可以使该变量成为不可变的,这意味着它不能被修改。常量必须在其声明时被初始化。
const vec2 a = vec2(0.0, 1.0);
vec2 b;
a = b; // invalid
b = a; // valid
常量可以在全局(任何函数之外)或局部(一个函数之内)进行声明。当你想在着色器中访问一个不需要的修改的值,你就可以在开头定义一个全局常量。它在所有着色器阶段共享,但是不能在着色器之外访问。
shader_type spatial
const float PI = 3.14159265358979323846;
运算符
以下是着色器中可用的运算符以及优先级列表:
优先级 | 类 | 操作符 |
---|---|---|
1 (最高) | 括号 | () |
2 | 单目运算符 | +, -, !, ~ |
3 | 乘除法 | /, *, % |
4 | 加减法 | +, - |
5 | 逐位移位 | <<, >> |
6 | 关系比较 | <, >, <=, >= |
7 | 相等比较 | ==, != |
8 | 按位与 | & |
9 | 按位或 | ^ |
10 | 按位异或 | ** |
11 | 逻辑与 | && |
12 (最低) | 逻辑或 | ** |
流控制
支持常见的流控制类型
// if and else
if (cond) {
} else {
}
// switch
switch(i) { // signed integer expression
case -1:
break;
case 0:
return; // break or return
case 1: // pass-through
case 2:
break;
//...
default: // optional
break;
}
// for loops
for (int i = 0; i < 10; i++) {
}
// while
while (true) {
}
// do while
do {
} while(true);
discard
片元和光照函数可以使用 discard
关键字. 如果使用, 则丢弃该片元并且不写入任何内容.
函数
函数的定义可以使用以下语法
ret_type func_name(args) {
return ret_type; // if returning a value
}
//具体的例子:
int sum2(int a, int b) {
return a + b;
}
如果你想调用一个函数,那么这个函数的定义必须在调用之前。
函数参数可以有特殊的限定符:
in : 表示参数仅用于读取(默认)。
out : 表示该参数只用于写入。
inout : 表示该参数以引用传递。
例如:
void sum2(int a, int b, inout int result) {
result = a + b;
}
Varying
要从顶点处理器函数往片元(或者光)处理器函数里发送数据,可以使用 varying。顶点处理器中的每一个图元顶点都是 varying 的,会为片元处理器中的每一个像素做插值。
shader_type spatial;
varying vec3 some_color;
void vertex() {
some_color = vec3(1.0);
}
void fragment() {
ALBEDO = some_color;
}
void light() {
DIFFUSE_LIGHT = some_color * 100;
}
也可以使用 varying 关键字将数据从片元处理器送往光处理器。
shader_type spatial;
varying vec3 some_light;
void fragment() {
some_light = ALBEDO * 100.0; // Make a shining light.
}
void light() {
DIFFUSE_LIGHT = some_light;
}
注意,在自定义函数或光处理器中是不能为 varying 赋值的。
shader_type spatial;
varying float test;
void foo() {
test = 0.0; // Error.
}
void vertex() {
test = 0.0;
}
void light() {
test = 0.0; // Error too.
}
##Uniform 将值传递给着色器。这些值对整个着色器来说是全局的,被称为 uniform。当一个着色器被分配给一个材质时,uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。
shader_type spatial;
uniform vec3 some_color;
你可以在编辑器中设置材质中的 uniform。
或者你可以通过 IVScript 来设置它们:
material.set_shader_param("some_color", Vector3(1,1,1))
注意
set_shader_param
的第一个参数是着色器中的 uniform
名称。它必须与着色器中的 uniform
名称完全一致,否则将无法被识别。
除了 void 之外,任何 GLSL 类型都可以成为 IdeaXR 还提供了可选的着色器提示,以使编译器了解 uniform 是用来干什么的。
shader_type spatial;
uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0);
以下是完整的提示列表:
类型 | 提示 | 描述 |
---|---|---|
vec4 | hint_color | 用作颜色 |
int, float | hint_range(min, max[, step]) | 用作范围(最小值/最大值/步长) |
sampler2D | hint_albedo | 用作反照率颜色 |
sampler2D | hint_normal | 用作法线贴图 |
sampler2D | hint_white | 作为值,默认为白色 |
sampler2D | hint_black | 作为值,默认为黑色 |
sampler2D | hint_aniso | 作为 FlowMap,默认为右。 |
IVRScript 使用的变量类型与 GLSL 不同,所以当把变量从 IVRScript 传递到着色器时,IdeaXR 会自动转换类型。以下是相应类型的表格:
IVRScript类型 | GLSL类型 |
---|---|
bool | bool |
int | int |
float | float |
Vector2 | vec2 |
Vector3 | vec3 |
Color | vec4 |
Transform3D | mat4 |
Transform2D | mat4 |
Uniform 也可以分配默认值:
shader_type spatial;
uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : hint_color = vec4(1.0);
内置变量
有大量类似UV
、COLOR
、VERTEX
的内置变量可用。具体有哪些变量可用取决于着色器的类型(spatial
、canvas_item
以及particle
)和所在的函数(vertex
、fragment
以及light
)。可用的内置变量列表见对应的页面:
内置函数
函数 | 描述 |
---|---|
vec_type radians (vec_type degrees) | 将度数转换为弧度 |
vec_type degrees (vec_type radians) | 将弧度转换为度数 |
vec_type sin (vec_type x) | 正弦 |
vec_type cos (vec_type x) | 余弦 |
vec_type tan (vec_type x) | 正切 |
vec_type asin (vec_type x) | 反正弦 |
vec_type acos (vec_type x) | 反余弦 |
vec_type atan (vec_type y_over_x) | 反正切 |
vec_type atan (vec_type y, vec_type x) | 将向量转换为角度的反正切 |
vec_type sinh (vec_type x) | 双曲正弦 |
vec_type cosh (vec_type x) | 双曲余弦 |
vec_type tanh (vec_type x) | 双曲正切 |
vec_type asinh (vec_type x) | 反双曲正弦 |
vec_type acosh (vec_type x) | 反双曲余弦 |
vec_type atanh (vec_type x) | 反双曲正切 |
vec_type pow (vec_type x, vec_type y) | 幂(x < 0 或 x = 0 且 y <= 0 时未定义 |
vec_type exp (vec_type x) | 以e为底的指数 |
vec_type exp2 (vec_type x) | 以2为底的指数 |
vec_type log (vec_type x) | 自然对数 |
vec_type log2 (vec_type x) | 以2为底的对数 |
vec_type sqrt (vec_type x) | 平方根 |
vec_type inversesqrt (vec_type x) | 反平方根 |
vec_type abs (vec_type x), ivec_type abs (ivec_type x) | 绝对值 |
vec_type sign (vec_type x), ivec_type sign (ivec_type x) | 如果 x>0 则返回1,小于0则返回-1 |
vec_type floor (vec_type x) | 向下取整 |
vec_type round (vec_type x) | 四舍五入 |
vec_type roundEven (vec_type x) | 四舍五入到最接近的偶数 |
vec_type trunc (vec_type x) | 截断 |
vec_type ceil (vec_type x) | 向上取整 |
vec_type fract (vec_type x) | 取小数部分 (返回 x - floor(x) ) |
vec_type mod (vec_type x, vec_type y) ,vec_type mod (vec_type x, float y) | 取余 |
vec_type modf (vec_type x, out vec_type i) | 返回 x 的小数部分,i为整数 |
vec_type min (vec_type a, vec_type b) | 返回a,b中的最小值 |
vec_type max (vec_type a, vec_type b) | 返回a,b中的最大值 |
vec_type clamp (vec_type x, vec_type min, vec_type max) | 将x的值限制在min和max之间 |
float mix (float a, float b, float c) | 线性插值 |
vec_type mix (vec_type a, vec_type b, float c) | 线性插值(标量系数) |
vec_type mix (vec_type a, vec_type b, vec_type c) | 线性插值(向量系数) |
vec_type mix (vec_type a, vec_type b, bvec_type c) | 线性插值(布尔向量选择) |
vec_type step (vec_type a, vec_type b) | b[i] < a[i] ? 0.0 : 1.0 |
vec_type step (float a, vec_type b) | b[i] < a ? 0.0 : 1.0 |
vec_type smoothstep (vec_type a, vec_type b, vec_type c) | 用于求解两个值之间的样条插值 |
vec_type smoothstep (float a, float b, vec_type c) | 用于求解两个值之间的样条插值 |
bvec_type isnan (vec_type x) | 如果标量或向量分量是 NaN 则返回 true |
bvec_type isinf (vec_type x) | 如果标量或向量分量是 INF 则返回 true |
ivec_type floatBitsToInt (vec_type x) | Float->Int 位复制,无转换 |
uvec_type floatBitsToUint (vec_type x) | Float->UInt 位复制,无转换 |
vec_type intBitsToFloat (ivec_type x) | Int-> Float 位复制,无转换 |
vec_type uintBitsToFloat (uvec_type x) | UInt->Float 位复制,无转换 |
float length (vec_type x) | 计算向量长度 |
float distance (vec_type a, vec_type b) | 计算向量a,b之间的距离 |
float dot (vec_type a, vec_type b) | 向量点乘 |
vec3 cross (vec3 a, vec3 b) | 向量叉乘 |
vec_type normalize (vec_type x) | 标准化为单位长度 |
vec3 reflect (vec3 I, vec3 N) | 计算反射向量 |
vec3 refract (vec3 I, vec3 N, float eta) | 计算折射向量 |
vec_type faceforward (vec_type N, vec_type I, vec_type Nref) | 如果 dot(Nref, I) <0, 则返回N, 否则返回-N |
mat_type matrixCompMult (mat_type x, mat_type y) | 矩阵分量乘法 |
mat_type outerProduct (vec_type column, vec_type row) | 矩阵外积 |
mat_type transpose (mat_type m) | 转置矩阵 |
float determinant (mat_type m) | 矩阵行列式 |
mat_type inverse (mat_type m) | 逆矩阵 |
bvec_type lessThan (vec_type x, vec_type y) | 执行两个向量的分量小于(<)比较 |
bvec_type greaterThan (vec_type x, vec_type y) | 执行两个向量的分量大于(>)比较 |
bvec_type lessThanEqual (vec_type x, vec_type y) | 执行两个向量的分量小于等于(<=)比较 |
bvec_type greaterThanEqual (vec_type x, vec_type y) | 执行两个向量的分量大于等于(>=)比较 |
bvec_type equal (vec_type x, vec_type y) | 执行两个向量的分量等于(==)比较 |
bvec_type notEqual (vec_type x, vec_type y) | 执行两个向量的分量不等于(!=)比较 |
bool any (bvec_type x) | 存在为true的组件时返回true |
bool all (bvec_type x) | 所有组件都为true是返回true |
bvec_type not (bvec_type x) | 反转布尔向量 |
vec_type dFdx (vec_type p) | 使用局部微分进行 x 的微分 |
vec_type dFdy (vec_type p) | 使用局部微分进行 y 的微分 |
vec_type fwidth (vec_type p) | x 和 y 的绝对导数之和 |