前言
开放的着色器编程语言 (OSL)是一个由 Sony Pictures Imageworks (索尼影业旗下的 Imageworks 公司)开发的着色器编程语言,OSL 的表面和体积着色器,定义了物体的表面和体积,如何使用重要性采样来发散光线,因此,OSL 语言很适合支持光线追踪和全局光照的物理渲染器,比如:Arnold 渲染器。这种语言同时也被 V-Ray 3 和 Blender 里面的 Cycles 渲染器(从 Blender 2.65 开始)支持。
在 Arnold5.0 中,OSL 着色器可以实现简单的纹理图案到闭包材质,非常牛逼。它是一种替代 C++的着色器方案,并拥有简单高效的编写方法。
PS:新版 Arnold 添加了 OSL 节点可以使用;
OSL 安装
只需要将 OSL 着色器(.OSL,.MTD)放置在 plugins/C4DtoA/shaders 目录下即可使用,重启 Arnold 会自动加载生成节点,和其他着色器一样使用;
如果有 UI 资源文件(.Res,.h,.STR),可放置在/plugins/C4DtoA/res 中;
还可以使用 ARNOLD_PLUGIN_PATH,来指定着色器搜索路径。
使用 Kick 命令可以检测着色器及其参数:
$ kick -info gamma node: gamma type: shader output: RGB parameters: 3 filename: gamma.oso version: 4.2.12.0 Type Name Default ------------ -------------------------------- -------------------------------- RGB Cin 0, 0, 0 FLOAT exponent 1 STRING name
OSL 局限性
着色器名称必须唯一,否则不会加载,可以通过日志看到记录;
目前只支持一个输出参数,不支持多个输出。
OSL 工作流
OSL 着色器一般为.OSL 文件,每个文件对应一个阿诺德着色器节点(可以在节点编辑器中找到),看例子:
Gamma.osl
shader gamma( //与文件名一致 color Cin = color(0, 0, 0), float exponent = 1, output color Cout = color(0, 0, 0)) { Cout = pow(Cin, 1/exponent); }
着色器问见面 Gamma.osl,所以 shader 的名称需要与其一致;
着色器定义的输入输出参数很像 Arnold C++的着色器节点,但语法更紧凑;
这里 Cin 和 gamma 是输入参数,而 Cout 是输出参数,所有参数必须用默认初始化值。
OSL 编译
阿诺德会自动搜索到着色器并进行自动编译,编译后在.OSL 文件的同目录中,会多出一个.OSO 文件.
如果没有对应的.OSO 或.OSL 被修改,通常会进行自动编译。
你也可以使 Arnold 的 OSLC 程序手动编译。
$ oslc gamma.osl Compiled gamma.osl -> gamma.oso
.OSO 文件包含了与操作系统和 CPU 指令无关的中间代码。
在渲染的时候,.OSL 会动态的将着色器节点转换为机器代码,并且最大化的优化着色器,从分利用着色器节点的参数。
手动加载 OSL 着色器
除了前面的可以自动搜素之外,还可以通过 OSL 节点手动加载着色器。
你可以将表达式插入到着色器节点中,让它加载别的着色器,从而进行更多的控制。
OSL 使用 shadername 参数来指定加载的着色器,不需要.osl 或.oso 后缀;
一旦用 shadername 参数,OSL 就会被添加到节点同,同时具有 param_ 前缀;
osl { name myshader shadername somefolder/test //加载着色器 param_value 0.5 }
当然你也可以直接嵌入.OSL 代码
osl { name myshader code "shader test(float value = 0, output float result = 0)//嵌入着色器 { result = value * 10; }" param_value 0.5 }
支持特性
参数
通常情况下,OSL 的默认参数会被转换成相应的 Arnold 参数,因为 Arnold 只目前只支持一个输出参数,因此如果有多个输出参数,将以第一个为准。
OSL 类型
|
Arnold 类型
|
---|---|
int |
INT |
int (with metadata) |
BOOLEAN |
int (with metadata) |
ENUM |
float |
FLOAT |
color |
RGB |
color |
RGBA |
point |
VECTOR |
vector |
VECTOR |
normal |
VECTOR |
point |
POINT2 |
matrix |
MATRIX |
array of any type |
ARRAY |
closure color |
CLOSURE |
struct |
POINTER |
布尔和枚举参数使用的是 OSL 元数据的整数创建的。
#define OPTION_A 0 #define OPTION_B 1 #define OPTION_C 2 shader example( int booleanvalue = 0 [[ string widget = "boolean" ]], int enumvalue = 0 [[ string widget = "popup", string options = "OptionA|OptionB|OptionC" ]]) { if (booleanvalue) ... if (enumvalue == OPTION_B) ... }
属性
节点参数和用户数据可通过 getattribute()方法获得。
// 对象参数 int id; getattribute("id", id); // 对象用户数据 color Cs; getattribute("Cs", Cs); // 来自其他节点的参数 int AA_samples; getattribute("options", "AA_samples", AA_samples);
标准参数
使用以下属性,如果 getattribute()函数指定 objectname 参数,就会检索特定于该对象的值。如果没有指定特定对象,则表示当前对象。
名称
|
类型
|
描述
|
---|---|---|
"geom:type" | string | 对象类型 |
"geom:name" | string | 对象名称 |
"geom:bounds | point[2] | 世界空间中的对象边界框 (min, max) |
"geom:objbounds" | point[2] | 对象空间中的对象边界框 (min, max) |
标准相机属性
使用以下属性,如果 getattribute()函数指定了 objectname 参数,并且它是有效摄像机的名称,则检索特定于该摄像机的值。如果没有指定特定的摄像机,则表示全局摄像机或默认摄像机。
名称
|
类型
|
描述
|
---|---|---|
"camera:screen_window" | int[2] | 图像分辨率 |
"camera:pixelaspect" | float | 像素长宽比 |
"camera:projection" | string | 摄像机类型 |
"camera:fov" | float | 视野 |
"camera:clip_near" | float | 近端修剪距离 |
"camera:clip_far" | float | 远端修剪距离 |
"camera:clip" | float[2] | 近端和远端修剪距离 |
"camera:shutter_open" | float | 快门打开时间 |
"camera:shutter_close" | float | 快门关闭时间 |
"camera:shutter" | float[2] | 快门打开和关闭时间 |
"camera:screen_window" | float[4] | 屏幕窗口 (xmin, ymin, xmax, ymax) |
着色器全局
Arnold 支持大多数 OSL 着色器全局格式,如 P, u, v, N, Ng 和 time。它们的含义与 c++ shading API 中的含义相同。
目前不支持 Ps(用于光滤镜)、surfacearea()、dtime 和 dPdtime 着色器全局。
所有着色器全局都被认为是只读的,我们不支持对 Ci 和 P 进行写操作以输出闭包或置换。应该使用输出参数。标准相机属性
使用以下属性,如果 getattribute()函数指定了 objectname 参数,并且它是有效摄像机的名称,则检索特定于该摄像机的值。
如果没有指定特定的摄像机,则表示全局摄像机或默认摄像机。
纹理
通过内置的 texture()和 gettextureinfo()函数访问纹理。函数的作用是:接受一个可选的 colorspace 参数来表示要转换的纹理的颜色空间。
注意,与 Arnold c++ shading API 不同的是,为了与 OSL 标准保持一致,纹理原点假设位于左上角而不是左下角。
为了匹配,在 UDIM 纹理的情况下,v 坐标可以翻转到 1 - v,或者 floor(v) + 1 - mod(v, 1)。
// 目标纹理颜色 color tex = texture(filename, u, v); // 查询纹理分辨率 int resolution; gettextureinfo(filename, "resolution", resolution);
体积通道
体积通道可以通过使用对象空间坐标的 texture3d()获得。
point Po = transform("object", P); float density = texture3d("density", Po, "interp", "bicubic");
闭包
支持以下闭包,与c++ shader API中的闭包相匹配。
closure color emission(); closure color background(); closure color diffuse(normal N); closure color oren_nayar (normal N, float sigma); closure color translucent(normal N); closure color microfacet(string distribution, normal N, vector U, float xalpha, float yalpha, float eta, int refract); closure color microfacet(string distribution, normal N, float alpha, float eta, int refr); closure color reflection(normal N, float eta); closure color reflection(normal N); closure color refraction(normal N, float eta); closure color transparent(); closure color holdout(); closure color empirical_bssrdf(vector mfp, color albedo); closure color volume_absorption(); closure color volume_emission(); closure color volume_henyey_greenstein(color absorption, color scattering, color emission, float g);
示例着色器输出一个闭包:
shader simple_diffuse( color albedo = 0.8, float opacity = 1.0, output closure color result = 0) { result = opacity * albedo * diffuse(N) + (1 - opacity) * transparent(); }
跟踪
支持 OSL trace()函数,用于跟踪射线。不支持执行着色的阴影参数。
traceset 参数是受支持的,默认情况下使用一个包含的 traceset,如果 traceset 名称的前缀是- character eter,则为 exclusive。
有关命中的信息可以使用 getmessage()检索,支持命中、hitdist、P、N、u、v 以及对命中对象的任意用户数据和参数查找。
int hit = trace(origin, direction, "traceset", tracesetname); if (hit) { // query hit distance float hitdist; getmessage("trace", "hitdist", hitdist); // query parameter on object that was hit string name; getmessage("trace", "name", name); // query user data on position where object was hit color Cs; if (getmessage("trace", "Cs", Cs)) ...; }
光线类型
OSL raytype()函数返回 1,如果被着色的光线是给定类型的,返回 1,
返回 0,则射线不是这种类型,或者射线类型名称不被渲染程序识别。
支持射线类型
|
---|
'camera" |
"shadow" |
'diffuse_transmit" |
'specular_transmit" |
'volume_scatter"
|
'diffuse_reflect"
|
'specular_reflect"
|
'subsurface"
|
OSL 性能
OSL 和 c++着色器可以链接到一个单一的着色器节点。
然而,在将 OSL 着色器的输出连接到 c++着色器时,会有少量的开销(可能是总渲染时间的 1- 2%)。
为了使 OSL 优化器能够进行积极的全节点优化,应该使用尽可能多的 OSL 着色器节点。
评估着色器发生在第一次编译和优化时。这增加了第一个像素的时间,但可以在减少渲染时间的整体上得到回报。
调试
OSL_OPTIONS 环境变量可以用来调试着色器中的常见错误或者打印更详细的信息:
# 查找超出边界访问的数组、NaN/Infs 和未初始化变量的使用的昂贵代码 OSL_OPTIONS="range_checking=1,debug_nan=1,debug_uninit=1" #为每个已编译的着色器发出消息 OSL_OPTIONS="compile_report=1" # 每个线程应该处理调用数量的警告 OSL_OPTIONS="max_warnings_per_thread=100"
更多参考信息
Open Shading Language 介绍
Open Shading Language 规范
本文译自 Arnold 官方文档:OSL Shaders
333666
65666666666666666666
666666