OSLの次の一歩のためのメモ
早いものでアドベントカレンダーも間近の季節になりました。
大昔BlenderでOSLを使ってみる一歩一歩なんてエントリーを書いたこともあったので、ちょっとOSLについてメモのようなものをまとめておこうと思いました。
ちなみにBlenderのバージョンはこれを書いた時点での安定版である2.79bを基にしています。
情報源
まずは個人的によく参照する情報源です。
BlenderマニュアルのOpen Shading Languageの項
Docs » Render » Cycles Renderer » Nodes » Open Shading Language
言わずと知れたBlender公式マニュアルのOSLの項目です。最小限で全部英語ではあるものの、Script Nodeの使い方、簡単な例、Shaderノードに相当するclosureのリスト、 getattribute("アトリビュート名", return_var)
で取得できるもののリスト、それからシェーダー中からレイを飛ばす trace
関数まで説明されています。
Open Shading Language のGitHub
OSLの公式リポジトリです。最新のSpecificationがあります。
言語自体の仕様は全てここに書かれています。
また、ありがたいことにKaz-Kitaguchi氏によるSpecificationの非公式日本語訳というものもあります。参考にさせていただきましょう。
CyclesのOSL関連の実装部分のソース
blender_source/intern/cycles/kernel/osl/
いうまでもなくBlenderおよびCyclesはオープンソースなので実装部分のソースを見ることができます。
ダウンロードページのOS自動判別のダウンロードボタンの下の『macOS,Linux,and other versions』などと書かれたプルダウンからソースのアーカイブを落とすことができます。
このアーカイブを展開したものをblender_sourceとして、CyclesでのOSLの実装部分のソースが上記のパスになります。特に重要なのはosl_service.cppです。ここに大抵のOSLの関数に対応するCyclesの実装が書いてあります。ファイルの上の方にアトリビュートが定数で並んでいますが、ファイル内を検索してみると実は非対応などというものもあったりします。
マテリアルノードのOSL実装
blender_source/intern/cycles/kernel/shaders
この中にはCyclesのノードを全てOSLで実装したものがあります。OSLモードで使われているものでしょう。
ちょっとしたテクニックから、Blender固有の事情になるUVや頂点カラーが複数ある場合の取得方まで意外と参考になったりします。
余談ですがoslutil.hにある float wireframe(string edge_type, float line_width, int raster)
などはいかにもOSLを生かしたものという感じで面白いです。
Tips
小ネタをいくつか。
大域変数
Specificationの6.5 Global variablesで説明されている大域変数の表です。 何が定義されてるんだっけとよく参照するので書き出してみます。
変数 | 大雑把な説明 |
---|---|
point P |
位置 |
vector I |
入射ベクトル |
normal N |
スムースシェーディングなどが適用された法線 |
normal Ng |
Pの表面上の本当の法線 |
float u, v |
面上の2D座標で表したP |
vector dPdu, dPdv |
Pの表面上の接ベクトル |
point Ps |
ライティングしようとしている位置 |
float time |
今の時間 |
float dtime |
このサンプルがカバーしている時間 |
vector dPdtime |
単位時間でのPの移動量 |
closure color Ci |
入射輝度 |
これらについては使ったことがなくて本当に入ってくるかは知らないものもあります。 OSLの仕様上はこれらの変数には書き換え可能なものもあるのですが、Cyclesでは基本的に読み取り専用と考えておけば良かったような気がします(要出典)
getattribute()
BlenderのScript NodeのページにOSLで取得できるアトリビュートの一覧があると上に書きました。 しかしどんな型の変数で受け取れば良いのかが書かれていません。 大方想像はつくと思いますが、せっかくなので上記のマテリアルノードのOSL実装の中から探して並べてみました。
name | 受け取る型 |
---|---|
geom:generated |
point |
geom:uv |
point |
geom:dupli_generated |
point |
geom:dupli_uv |
point |
geom:trianglevertices |
point[3] |
geom:numpolyvertices |
int(※現状は必ず3) |
geom:polyvertices |
point[N] |
geom:name |
string |
geom:is_curve |
float |
geom:curve_intercept |
float |
geom:curve_thickness |
float |
geom:curve_tangent_normal |
normal |
object:location |
point |
object:index |
float |
object:random |
float |
material:index |
float |
particle:index |
float |
particle:age |
float |
particle:lifetime |
float |
particle:location |
point |
particle:size |
float |
particle:velocity |
vector |
particle:angular_velocity |
vector |
path:ray_length |
float |
path:ray_depth |
int |
path:diffuse_depth |
int |
path:glossy_depth |
int |
path:transparent_depth |
int |
path:transmission_depth |
int |
"UV Mapの名前" | point |
"Vertex Colorの名前" | color |
object:index
とmaterial:index
がfloatだったりと注意したいところがいくつかありそうです。
ちなみに探してみるとここに上がっていないものも取得する方法が見つかったりします。
trace()
とgetmessage()
OSLはシェーダーの中でレイを飛ばすtrace(point position, vector direction, ...)
と、そのレイがヒットした先の情報を取得するgetmessage("trace", "attribute名", attributeに合った型の変数)
という強力な機能があります。
ただ少し制限があり、仕様では存在するtraceの"shade"と"traceset"の2つのオプションは無効になっています。 またそれに伴ってレイのヒット先からは取得できないアトリビュートがいくつかあります。 ドキュメントに書いてあるようにライティングに使うのではなく、周囲の調査に使ってアンビエントオクルージョンのように使えるかもね、というものです。
とはいえアイデア次第です。
関数の引数は参照扱い
最後にちょっとした仕様のことを。
Specification の 6.4.1 Function calls に、OSLはC言語風ですが関数に渡される引数は全て参照扱いです、と書いてあります。 わかる人にはこの解説で十分ですが、超訳して言うと、引数を再利用する形で処理を書くと呼び出し元の値も変更されますということです。
// 例えばこんな関数を書くと color makeRed(color c) { c = color(1.0, 0.0, 0.0); return c; } void someProcess() { color white = color(1.0, 1.0, 1.0); color red = makeRed(white); // <- これでwhiteは(1.0, 0.0, 0.0)になる }
知っていれば複数の値を変更したい時などに便利な仕様ですが、知らないとびっくりします(笑
まあそもそもモダンな言語では引数は変更不可能だったりするのでこういう書き方は悪習の類ですよね…