LightPathExpressions

1. イントロダクション
2. 概要
3. 字句構造
4. 全体的な構文...
5. データ型
6. 言語構文
7. 標準ライブラリ関数
8. 形式言語文法
9. シェーダグループの記述
10. 用語集
11. **Light Path Ex...**

## OSL Light Path Expressions

プロダクションでは、レンダラーで何枚もの出力を同時に生成するのが一般的です。
例えば、すべての照明を含んだメインの**ビューティー**レンダリングだけでなく、ディフューズ照明のみ、スペキュラー照明のみ、反射のみ、一部のライトの寄与度のみを別々の画像で構成したいことがあります。それらの出力は、後のコンポジットの工程にて色々な方法で再合成させることが多いです。

たいていのレンダラーでは、シェーダ側でたくさんの出力パラメータ("Arbitrary Output Variables:任意の出力変数、いわゆる"**AOVs**)を定義してから、レンダラー側でそれらのAOV名と画像出力名をペアにすることが一般的です。
この手法の問題は、シーン内のすべてのシェーダで明示的に各AOVパラメータを指定(そして、正しく計算された照明成分をそこに格納する)する必要があるという事です。
これでは、AOVの数が増えるに連れてシェーダが非常に乱雑になってしまい、新しくAOVを追加する時にすべてのシェーダを修正しなければならないので作業に負担がかかります。

OSLベースのレンダラーでは、そのような出力によって実際にシェーダが乱雑にならないように、代わりに、どのライトパス(光路)がどのレンダラー出力に寄与させるのかをレンダラー側で指定することができます。
通常の正規表現と同様の表記を使ったLight Path Expressionsを使用することで、シェーダ側に全く何も変更を加えることなく新しく出力を指定することができます。
OSLシェーダはrawカラーではなくラディアンスクロージャを計算し、そのクロージャプリミティブ(例えば、ディフューズ、マイクロファセットなど)がそのシェーダの計算するライトの種類を把握しているので、こういうことが可能になっています。

### 正規表現

ここで紹介する正規表現は、シーン内のライト寄与度をフィルタリングして、選択したAOVにそれらの寄与度を反映させます。
正規表現を使用した経験がないのであれば、ここを参考にしてください。
ライトパスに使用する正規表現は、それとほぼ同様ですが、私たちが扱う特有の問題上、少し拡張をしています。
とはいえ、始めるにあたって、その拡張についてはあまり気にする必要はありません。

上図は、よく使われている正規表現によって生成されたフルレンダリング結果です:

```
"C[SGs]*D*<Ts>*[LO]"

```

この正規表現は、コースティクス以外のすべての成分を許可します。これが何を意味しているのかを簡単に説明していきたいと思います。

### ライト言語

既に正規表現に熟知しているのであれば、まず最初にライトパスに関する具体的な言語について説明する必要があります。
ご存知のとおり、レイトレーシングにおけるライトパスには、一連の光の**バウンス(照り返し)**や**スキャタリング(散乱)**があります。
これらは、標準的な後方レイトレーシングで、カメラから光線が発射されてライトまたは発光オブジェクトまで遡ります。
最終的には内部的にすべてを逆転させる場合もありますが、これらのパスはカメラから開始されるという一般的な慣例に従うことにします。
とはいえ、ユーザーはそのことについて気にすることはありません。
典型的なライトパスは、以下のステップを踏みます:

1. 光線がカメラから出発して、シーン内を通過します。
2. スペキュラーサーフェスに当たれば、その光線をシーン内で反射させます。
3. ディフューズサーフェスに当たれば、その光線をシーン内で反射させます。
4. 光源に当たれば、そのパスが終了します。

そのため、このライトパスの例では、光が到着したライトから2回のバウンスを経由してカメラに光が届くことになります。
このライトパスの各ステップのことを**イベント**と呼びます。つまり、ライトパスとは、それらの一連のイベントのことを指します。
ライトパスは必ずカメラから始まり、ライトまたは私たちが最終的に使用することになる何か特別な"ソース"オブジェクトで終了します。
ここではライトについて紹介するのが目的なので、ライトパスがライトで必ず終わるものとして仮定します。
イベント別に単一文字のエイリアスが用意されています。これを応用させることで、上記のライトパスを表現することができます:

```
CSDL

```

これは、**C**amera, **S**pecular, **D**iffuse, **L**ightの略です。

### イベント

#### イベントタイプ

イベントには、それ自体を定義するための特徴がいくつかあります。
その1つがイベントタイプです。このイベントタイプによって、停止した光もしくは移り変わった光の種類について知ることができます。
光は、カメラから出発して、サーフェス上で跳ね返り、ライトに到達します。現在のところ定義済みのイベントタイプの現行セットは以下のとおりです:

1. C: **C**amera:カメラ
2. R: Surface **R**eflection:サーフェス反射
3. T: Surface **T**ransmission:サーフェス透過
4. V: **V**olume ボリューム
5. L: **L**ight ライト
6. O: Emitting **O**bject 発光オブジェクト
7. B: **B**ackground 背景

この定義の最初の文字は、エクスプレッションを記述する時に使用するエイリアスです。
今のところ、**V**と**B**は無視してください。
**L**と**O**の違いについて注目することが重要です。
シェイプが光源としてタグ付けされていた時に**L**が現れます。つまり、光源から直接サンプリングされるので品質が良いです。
現在のところ、これらの光源は、球、円盤、四角形に制限されています。
とはいえ、任意のタイプの通常オブジェクトに発光値を割り当てることもできます。
このオブジェクトはシーンに光を放出しますが、そのオブジェクトで終了するライトパスは**L**ではなく**O**で終了します。
この方法であれば、**カスタムラベル**を使わなくても、この2種類の寄与度を区別することができます。
また、点群ベースの現在のSSS実装もエミッターとして動作することに注目してください。
そのため、そのSSS実装もOラベル下でレンダリングされます。

#### イベントスキャタリング(散乱)タイプ

これが適用された時にのみ、スキャタリング(散乱)ラベルが定義されます。
私たちは、このタイプを3つのラベルに分けて、さらに透明シャドウで便宜的に使用できるように特殊ラベルを加えました。

1. D: **D**iffuse 拡散
2. G: **G**lossy 光沢
3. S: **S**ingular, 特異(反射と屈折をひとまとめにしてこのように呼びます)
4. s: **s**traight (**S**の特殊ラベル。小文字であることに注意)

最初の3つがよく使用するラベルです。
これらのラベルは、それ自体のことを話します。
Singularラベルは、完全に鮮明な反射または屈折を意味し、Glossyラベルのようにぼやけません。
GlossyラベルはDiffseラベルとSingularラベルの中間のようなものです。
specialラベルは"s"(小文字)でstraightです。この光線はサーフェスに対して何の変化もなく通過します。つまり、光線の方向を変更しない完全な屈折のようなものです。
コースティクスに依存せずに透明なシャドウを計算できるように、私たちはそのような区別でタグ付けしました。

#### イベントを完全に記述する方法

ここまでで、私たちはライトパスのイベントで持つことができる異なる2つのラベルを見てきました。
それでは、イベントをどのようにして完全に記述すればよいでしょうか？
答えは簡単。それらの2つの基本ラベルを指定するだけです。
しかしながら、2つ目のラベル(スキャタリング)は必ずしも適用する必要がありません。
2つ目のラベルが必要ない場合には、それが未定義であることを示すために"x"文字を書きます。
それでは、これまでのライトパスのことについて振り返ってみましょう:

1. 光線がカメラから出発して、シーン内を通過します。
2. スペキュラーサーフェスに当たれば、その光線をシーン内で反射させます。
3. ディフューズサーフェスに当たれば、その光線をシーン内で反射させます。
4. 光源に当たれば、そのパスが終了します。

上記のことは、次のように書くことができます:

1. < C x > (カメラ)
2. < R S > (反射, 特異)
3. < R D > (反射, ディフューズ)
4. < L x > (ライト)

つまり、これらの文字を次々に並べて一列でライトパスを記述することができます:

```
<Cx><RS><RD><Lx>

```

以前のライト言語では、これほど具体的な指定をすることができませんでした:

```
CSDL

```

しかし、正規表現では、ほとんどの場合に短いバージョンを使用するため、心配はいりません。

#### ライトに対する正規表現

正規表現内の"."(ドット)はワイルドカードであることを覚えておいてください。
つまり、任意の文字が1回のみ合致する記号です。
"\*"は、その前のエクスプレッションを何回も繰り返すことができる修飾子です。
そのため、エクスプレッション"."は1回のみの任意のイベントに合致しますが、エクスプレッション".\*"は0回以上の任意のイベントの繰り返しに合致します。

ライト言語で使用する記号は、通常の正規表現で使用するような文字ではなくてイベントです。
そのイベントの表記は、2つのラベルを合わせた<ab>です。

それでは、カメラから開始し、ディフューズライト(透過を含む)のみに変わるライトパスに合致させたいとします。
この場合の例のライトパスは以下のとおりです:

```
<Cx><RD><TD><RD><Lx>

```

上記のようなライトパスに合致させるには、以下のようにエクスプレッションを記述します:

```
<Cx><.D>*<Lx>

```

この"< . D > \*"では、ディフューズとして(ワイルドカードの**.**の部分が)サーフェス反射または透過に合致し、**\***は、それを何度も反復することを意味します。
しかしながら、この表記をもっと単純化することができます。
シーン内でディフューズとして作用するすべてのラベルは、サーフェス反射と透過しかないことを私たちは既に知っています。
さらに、カメラとライトについても知っていて、他のラベルは未定義にするので、実際には以下のように記述することができます:

```
<C.><.D>*<L.>

```

ここでは、構文のテクニックを使っています。
"< . D >"や"< L . >"の代わりに"D"や"L"だけを宣言しても、パーサーはそれを理解することができます。
パーサーは、すべてのラベルに対して適切な位置を理解することができるので、以下のように記述することができます:

```
CD*L

```

これは、上記と同じ意味になります(画像を参照)。

これでタイピング時間が節約され、そのエクスプレッションのイベントがわかりやすくなりました。
しかし、ここで疑問が残ります。どうして前の複雑なエクスプレッションでパターンを定義したのでしょうか?
この最後の形式を常に使用してはいけないのでしょうか?
そうですねぇ、そのような簡素化されたエクスプレッションがレンダリングでは十分で、あなたもそれに満足していたとしても、別の状況を考えてみると良いでしょう。
例えば、すべてのディフューズのバウンスを取り込みたいけれども、透過寄与度について気にしない事としましょう。
要するに、すべてのものがチョークのようなマット反射だけにしたいということです。
そういった場合には、次のエクスプレッションを使用したいはずです:

```
C<RD>*L

```

見てのとおり、**C**と**L**に対して簡素化した形式のエクスプレッションを使用していますが、ディフューズバウンスに対しては複雑な形式のエクスプレッションで**R**eflective surface:反射サーフェスと**D**iffuse:ディフューズを指定しています。
他の例を挙げるために、同じ事をしてみますが、完全な反射と屈折だけを使ってやってみます:

```
CS+<RD>*L

```

正規表現に詳しくない人のために説明すると、"+"は1回以上の繰り返しを意味します。
つまり、このパターンは最低でも1回は反射や屈折をしたライトパスのみを表示します。この結果の画像が以下のとおりです:

右側の球が消えたことに注目してください。この原因は、それが**G**lossy(光沢)として分類されているからです。
"S"を"[SG]"に置換することで、両方のラベルが許可されるんで、その球を表示させることができます。
再度、例えば屈折だけが必要であれば、以下のようにエクスプレッションを展開します:

```
C<TS>+<RD>*L

```

そして、ランプと他のオブジェクトの反射が突然現れました...

#### 正規表現をAOVにマッピングする方法

では、任意の有効な正規表現をあなたが定義した任意のAOVにマッピングすることができるようになりました。
それでは、以下のようにレンダラーコマンドを実行することができます:

```
Output ("beauty.tif", "rgb", "C[SGs]*D*<Ts>*L");
Output ("diffuse.tif", "rgb", "CD+L");

```

注:例えば、レンダラーコマンドが**Output(filename, format, data-regex...)**で、そのコマンドをいくつも実行して複数出力するとします。以降の例でも、この表記を使用します。

複数の正規表現を同じ出力に指示できるように、上記の表記を拡張させてみましょう:

```
Output ("diffuse.tif", "rgb", "CDDL", "CDL");

```

このケースでは、1バウンスしたディフューズ間接照明とディフューズのみの直接照明を累積します。
これは、実際には次のように記述することができます:

```
Output ("diffuse.tif", "rgb", "CD{1,2}L");

```

#### 正規表現を使ってアルファチャンネルを生成する方法

新しいOSLインテグレータには特定のアルファ出力は用意されていませんが、アルファチャンネルを簡単に生成することができます。
実際にデフォルトのルールは、あなたの代わりにアルファチャンネルをセットアップしてくれるので、その挙動を見ていきましょう。
既にB(Background)という名前のラベルを見たことがあるでしょう。
このラベルは特別なソースの"ライト"であり、紛らわしいですが、完全にシェーディングされる背景ではありません(Lとして表現される通常のライトです)。
これは常に値が1.0の特別なソースなので、背景が見えてる部分の画像を計算することができます。
そして、実際にオブジェクトの背後に表示されるものとは別物です。
下図を見てください:

では、この背景の可視性を取得したいとしましょう。FLOAT AOVをセットアップして、以下の正規表現をそのAOVにマッピングするのでカラーは不要です:

```
Output ("vis.tif", "float", "C<Ts>*B");

```

このエクスプレッションに注目してください。
ここでは、一直線方向に通過して背景に到着する何か(透明度)が欲しいと仮定します。
小さいsラベルを追加することで、ここに標準の屈折を追加することができました。
しかし、これが最も一般的なセットアップです。これによって、下図の画像が得られます:

しかし、私たちが求めているアルファチャンネルは、実際には、この反転画像です。
後工程で反転させることもできますが、エクスプレッションの頭にびっくりマーク"!"を付けることで、その反転画像を出力できるようにルールを変更することができます。
それでは、以下のエクスプレッションを試してみてください:

```
Output ("vis.tif", "float", "!C<Ts>*B");

```

これで、求めている画像を取得することができました:

良いですね。アルファチャンネルを別のAOVとして取得することができましたが、それをRGBA AOVの実際のアルファチャンネルに格納するにはどうすれば良いでしょうか?
それは、そのAOVのエクスプレッションの前に標準rgbエクスプレッションを追加することで可能です。以下の例を見てください:

```
Output ("beauty.tif", "rgba", "C[SGs]*D*<Ts>*L !C<Ts>*B");

```

まず最初に、カラーコンポーネントに対して"beauty"エクスプレッションを書き出してから、アルファコンポーネントをアルファチャンネルにマッピングします。
ほら、どうでしょうか!

うまくいきました。この二重の正規表現のセットアップはRGB AOVにのみ有効です。

#### カスタムラベル

このラベルの基本セットを使用することで、標準画像コンポーネントの管理が良くなります。
しかし、シーン内で特定のオブジェクトやライトを分離させる必要があります。
これができるように、OSLには、シェーダから返されたクロージャに任意のラベルをタグ付けすることができます。その構文は以下のとおり:

```
Ci = Kd * diffuse(N, "label", "alice") + Ks * phong(N, exp, "label", "alice");

```

クロージャは表面の挙動を定義していることを覚えておいてください。
光はphongクロージャを通過することができ、phongは光沢反射なので、イベントはライトパスのGラべルを取得することになります。
さらに、"label", "alice"ペアのカスタムラベルを定義しているので、そこを通過した光はラベルとして"alice"を取得することになります。
そのため、イベントを以下のように記述します:

```
<RG>

```

これを次のように記述します ...

```
<RG'alice'>

```

既存の正規表現を使用することをためらう必要はないです。なぜなら、"<RG>\*"エクスプレッションは、まだそのイベントにマッチするからです。
"G"または"<RG>"を使ってイベントを定義する時には、最後に暗黙的な".\*"があるので、その正規表現は機能します。
そのため、内部的には、これは""のようなものです。余計なカスタムラベルのマッチングについて心配する必要はないです。
このエンジンは、あなたに代わってそれを世話します。
phongタグが付いたものだけにマッチさせるにはどうすればよいでしょうか? そうですねぇ、すぐにできるものとしては、正規表現にカスタムラベルを指定することです:

```
<RG'alice'>

```

これは、さらに"alice"ラベルを定義した""イベントにのみマッチします。少し標準正規表現のテクニックを使えば、"alice"タグ以外のタイプにがマッチさせることもできます:

```
<RG[^'alice']>

```

正規表現が初めてな人は、このドキュメントのトップに載せているリンクから正規表現のリファレンスを参照してください。
見てのとおり、カスタムラベルは常にシングルクォートで閉じます。
これは、closure引数によってカスタムラベルをサーフェスやライトに割り当てることができるシェーダです。
カスタムラベルと正規表現の力を使えば、シーン内で色々なマッチングを行なうことができます。
ライト"light1"で反射したaliceタグのオブジェクトの反射光だけが欲しいのであれば以下のように記述します:

```
C'alice'<L.'light1'>

```

'alice'だけを書くと、それは実際には内部的に"<..'alice'>"に展開されることを覚えておいてください。
そのため、すべてが"alice"としてタグ付けられている限りは、diffuse, specular, glossyにマッチします。
しかし、"light1"ラベルをライト以外の他のオブジェクトに使用しない場合では、なぜ余計な情報でスクリーンが汚れるのでしょうか:

```
C'alice''light1'

```

もう少し進んでみましょう。ここまでで、"wall"タグのオブジェクトによって反射したaliceの光すべてが欲しいです。以下のようにしてみてください:

```
C'alice''wall'+L

```

サンプルの画像を見てみましょう。これは昔ながらのコーネルボックスです:

このコーネルボックスでは、すべてのオブジェクトに対して同じシェーダを使用し、assファイルにインスタンスを作成した時にそれらのシェーダのパラメータを変更しています。
このシェーダの定義は非常に単純です:

```
surface matte (float Kd = 1, color Cs = 1, string label = "foo")
{
  Ci = Kd * Cs * diffuse (N, "label", label);
}

```

唯一新しい事といえば、クロージャに直接渡されるラベルパラメータがあるという事です。
そのパラメータは、ここでは唯一の項目であるdiffuseにタグを割り当てています。
そうすることで、以下のようにassファイル内にシェーダを作成することができます:

```
Parameter ("Cs", color(0.1, 1, 0.1));
Parameter ("label", "greenwall");
Shader ("surface", "matte", "green_layer");

```

ここでは、"greenwall"ラベルを渡しました。これは、画像内の緑の壁に適用されます。また、ライトシェーダに対しても同じことができます:

```
surface emitter (float Ke = 1, color Cs = 1, string label = "foo")
{
    Ci = Ke * Cs * emission("label", label);
}

```

そして、以下のように2つのライトシェーダをインスタンス化したり、天井に2つの矩形エミッターをインスタンス化します:

```
Parameter ("Ke", 100.0);
Parameter ("label", "light1");
Shader ("surface", "emitter");

...

Parameter ("Ke", 100.0);
Parameter ("label", "light2");
Shader ("surface", "emitter");

```

左側に**light1**、右側に**light2**があります。
これで、正規表現によるカスタムラベルのフィルタリングのテストの準備ができました。
一度greenwallから反射して、他のサーフェスで跳ね返ったすべてのライトを調べることができます。
以下がその正規表現です:

Output ("beauty.tif", "rgb", "CD\*'greenwall'L");

そして、下図が取得できた画像です:

しかし、おそらく緑の壁(非内部オブジェクトライト)から直接反射したライトだけが欲しいです。
そのために、**light2**(右側にあるライト)だけが必要だと仮定します。
そうすると、オリジナルライトほどのエネルギーを得ることができなくなりました。以下がそのセットアップです:

```
Output ("beauty.tif", "rgb", "C'greenwall''light2'");

```

そして、下図がその結果です:

または、赤い壁から反射し、ライトから直接反射していないディフューズサーフェス内のすべての光を取得してみようと思います(つまり、赤い壁が表示されなくなります)。
以下がそのセットアップです。"[^'redwall']+"に注目してください。これは"redwall"のタグが付いていない最低1つの反射要素で反射させます:

```
Output ("beauty.tif", "rgb", "C[^'redwall']+'redwall'L");

```

そして、下図がその結果です:

もちろん、赤い壁のシェーダには"redwall"のタグを付けて、緑の壁と同じことをしました。