最新物理ベースレンダラー Mitsuba2を触ってみる (4) 偏光レンダリング編

# Mitsuba2とは
アカデミック向けのフリーの物理ベースレンダラー。微分可能レンダリングや偏光レンダリングなどの新機能がある。
他に書いた関連記事は以下です。

 - [最新物理ベースレンダラー Mitsuba2を触ってみる(1) 導入編](https://qiita.com/kurilab/items/34089636fa570d9a7939)
 - [最新物理ベースレンダラー Mitsuba2を触ってみる(2) Pythonから動かす編](https://qiita.com/kurilab/items/2ad1dd3d0676c1cf4c74)
 - [最新物理ベースレンダラー Mitsuba2を触ってみる(3) 微分可能レンダリング編](https://qiita.com/kurilab/items/5f270ebf8ee28a25cd92)
 - [BRDFと偏光BRDF(pBRDF)の違い](https://qiita.com/kurilab/items/f9e48045bd1b9c130d0b)
 
[導入編](https://qiita.com/kurilab/items/34089636fa570d9a7939)のMitsuba2のインストールまで終わっているという前提です。

# 偏光レンダリングとは
光の偏光情報を追跡してレンダリングを行うことです。
人間は偏光を知覚できないので、フォトリアルな画像をレンダリングするためには偏光を考慮する必要はありません。
なので**従来のレンダラーは光の偏光状態を全て「無偏光」であると仮定してレイトレーシングしていた**ことになります。
しかし近年、[偏光を測定できるカメラが市販](https://mavic.ne.jp/industrialcamera-color-polarization/)されたりしてきて、偏光は実世界で気軽に計測できるものになり、その偏光情報は材質や形状に関する追加の情報をもたらしてくれる事がわかってきました。なので偏光は、インバースレンダリングをする上でも強力な情報となります。
このような理由からMitsuba2では偏光をサポートをすることを決定したとのことです。

まず偏光についておさらいをします。
偏光とは、光が偏って（進行方向に対して垂直に）振動する性質のことです。カメラに詳しい方ですとPLフィルタをご存知かと思います。
フィルターを回転させることによって[水面の反射などをカットすることができます](https://www.kenko-tokina.co.jp/imaging/filter/pl/)、これも偏光の原理を利用しています。
![plfilter.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/b87af634-d90b-fc03-4240-8c010990a61b.jpeg)

下図の左は直線偏光、右は楕円偏光の例です。これに対して無偏光の光は振動に偏りがなく無規則に振動しています。
![pol0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/0efb8503-5b78-aa26-f402-cb0ebf5ed5ff.jpeg)
偏光の状態は4次元のストークスペクトル$\boldsymbol{\rm{s}}=[s_0, s_1, s_2, s_3]$で表現されます。
$s_0$：輝度（振幅）
$s_1$：直線偏光の水平成分(0°)と垂直成分(90°)の差、$s_1=1$は完全水平偏光、$s_1=-1$は完全垂直偏光
$s_2$：直線偏光の45°成分と135°成分の差、$s_2=1$は完全45°偏光、$s_2=-1$は完全135°偏光
$s_3$：円偏光の右回り成分と左回り成分の差、$s_3=1$は完全右回り円偏光、$s_3=-1$は完全左回り円偏光
![pol1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/c0b7af3c-6bb2-1f93-71b4-e431bc9649f6.jpeg)


ここで、$s_0 \geq \sqrt{s_1^2+s_2^2+s_3^2}$を満たす必要があります。

従来のレンダラーでは偏光状態を考慮しない（無偏光）ので、$\boldsymbol{\rm{s}}=[1,0,0,0]$だったことになります。
レンダリング中は、光はオブジェクトと相互作用し、偏光状態が変化します。
この変化をミューラー行列$\boldsymbol{\rm{M}} \in \mathbb{R}^{4 \times 4}$で記述します。
光がオブジェクトで反射や透過をするとき、入射光のストークスベクトル$\boldsymbol{\rm{s}}_i$と出射光$\boldsymbol{\rm{s}}_o$のストークスベクトルの関係性は以下になります。

```math
\boldsymbol{\rm{s}}_o = \boldsymbol{\rm{M}} \boldsymbol{\rm{s}}_i
```

これらを考慮した入射ベクトル$\boldsymbol{\rm{\omega}}_i$、出射ベクトル$\boldsymbol{\rm{\omega}}_o$との関係性は下図になります。
![pol2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/b93d0a7c-b3b5-bafb-a796-fbd93a4fae56.jpeg)


さて、物体における入射と出射のモデルと言えば、BSDF[^1]（反射のみならBRDF[^2]）が有名だと思います。入射・出射ベクトルが求まったらその比（反射率みたいなもの）である**スカラー値**が求まるパラメータ付き関数のことで、一般的なレンダラーはいくつものBSDF関数を持っています。
偏光を考慮する場合にこのBSDFがどうなるかというと、スカラー値ではなく上記の$4 \times 4$の**ミューラー行列**を返すようになります。これをpBSDFといいます。Mitsuba2では、直線偏光フィルタや波長板（リターダー）などに加えて、偏光フレネル方程式に従うpBSDFの実装がされています。

# pBSDF(pBRDF)についての詳細
少し学術的な込み入った話になります。
まとめ始めたら量がかなり多くなったので[別記事にしました](https://qiita.com/kurilab/items/f9e48045bd1b9c130d0b)。

# 偏光レンダリングを動かす
偏光レンダリングは微分可能レンダリングと違ってGPUである必要はありません、なのでMacOSでも可能です（まだ確かめていません）。以下はWindowsでの手順です、Linux, MaxOSもほぼ同じだと思います。

[導入編](https://qiita.com/kurilab/items/34089636fa570d9a7939)でも述べましたが、mitsuba2のコンパイル時に、偏光レンダリングが可能なようにバリアントを`mitsuba.conf`に設定する必要があります。まずは`scalar_rgb_polarized`で良いと思います。
偏光レンダリングをするためには、シーンファイルさえ偏光に対応して記述すれば良いので、特にコード側で用意をする必要はありません。なので今回は簡単のため、Pythonを通さずに実行ファイル(`mitsuba.exe`)を直接コマンドラインから叩きます。

偏光レンダリングをするためのサンプルはまだ公式が用意してくれていないので、[私がテスト用に作ったシーンファイルをGithubに置きました](https://github.com/tkuri/mitsuba_data)。MatpreviewPolar_mitsuba2.0をフォルダごと実行ファイルが存在するフォルダ`./build/dist/`に置いてください。

まずは何も考えず動かしてみます。

```
.\mitsuba.exe .\MatpreviewPolar_mitsuba2.0\matpreivew_polar.xml -m scalar_rgb_polarized
```
第一引数がxml形式のシーンファイルで、オプション`-m scalar_rgb_polarized`はレンダリングモードです。バリアントで設定した構成を指定する必要があります（ここでレンダリングモードを指定しないでレンダリングすると、偏光を考慮しないレンダリングがなされます。
少し時間がかると思います。偏光を考慮すると必要な計算量が増えるので、必然レンダリング時間が1.5~2倍になります。
`.\MatpreviewPolar_mitsuba2.0\matpreivew_polar.exr`という下記のようなexr形式の画像が生成されれば成功です。
exrは小数32bitx3ch形式のRGB画像です、Photoshopなどで開けます。[Venetaビューア](https://sourceforge.net/projects/veneta-viewer/)や[DJV](https://darbyjohnston.github.io/DJV/)等のフリーのソフトもあります。
![pol3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/8afc8219-4bf4-d245-08af-368b56ab4b7d.jpeg)

オブジェクトの前に3枚の偏光板を回転させて配置させています。2枚の偏光板をスリット方向に90度回転させ重ねると光を通さないのに、その間にもう一枚偏光板を斜めに挟むと光が通る様になるというシーンになります（少し分かりづらいと思います）。

シーンファイル`matpreivew_polar.xml`の偏光部分の記述を解説します。

```xml
    <bsdf type="polarizer" id="pol00"> # 直線偏光子のBSDF(pBRDF)を宣言
        <spectrum name="theta" value="0"/> # 直線偏光子の角度
    </bsdf>
```

BSDF(pBSDF)のタイプを宣言します。直線偏光子は`polarizer`です。このBSDFを後からオブジェクトが利用できるようにidをつけます。
thetaは光軸を中心とした偏光子の回転角度（度単位）を示しています、ここでは0に設定します。
偏光子を回転させるためには、このthetaを変化させる方法とオブジェクトそのものを回転させる方法の2種類があります。

```xml
    <shape type="rectangle" name="polar_film0"> # 矩形形状を宣言
        <transform name="to_world"> # 座標変換
            <scale x="0.5" y="0.5"/> # スケールの変換
            <translate x="0" y="0" z="-1.5"/> # 平行移動
            <rotate z="1" angle="0"/> # 偏光子の回転（polar_film0"はしない）
        </transform>
   	<ref id="pol00"/> # 先に宣言したBSDFを参照
    </shape>
```

最も奥の偏光板のオブジェクトについて宣言します。
`shape type="rectangle"`と指定することで矩形オブジェクトが生成されます。わかりやすいように名前`polar_film0`をつけます。
`transform name="to_world"`はオブジェクトの座標変換を表します。
`scale x="0.5" y="0.5"`で、オブジェクトのスケールをx,yそれぞれ1/2にします（rectangleはzにもともと成分を持たないため指定しなくて良いです）。
`translate x="0" y="0" z="-1.5"`でオブジェクトをz方向に-1.5移動します。手前に持ってくるイメージです。
`rotate z="1" angle="0"`はz軸に対し偏光板を回転させる操作です。polar_film0では何もやっていませんが、後２つのオブジェクトではここで回転角度を指定して回転させます。
最後に`ref id="pol00"/`で、このオブジェクトが既に定義したBSDF`pol00`を使うことを指定します。
各座標系のイメージは下図のとおりです。mitsuba2の座標系は右手系です。
![pol4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215987/e971e34a-cc43-fd13-cb91-c4891f5e3c15.jpeg)
polar_film0と同じようにpolar_film1, polar_film2について記述します。ほとんど変わらないですが、ずらすためにtranslateの値を変えていたり、偏光板を回転させるためにrotateの値を45°、90°と変化させています。

```xml
    <shape type="rectangle" name="polar_film1">
        <transform name="to_world">
            <scale x="0.5" y="0.5"/>
            <rotate x="1" angle="180"/>
            <translate x="0.2" y="0.5" z="-1.6"/> # 見やすいように偏光子をずらす
            <rotate z="1" angle="45"/> # 偏光子を45°回転させる
        </transform>
   		<ref id="pol00"/>
    </shape>

    <shape type="rectangle" name="polar_film2">
        <transform name="to_world">
            <scale x="0.5" y="0.5"/>
            <rotate x="1" angle="180"/>
            <translate x="0.2" y="-0.2" z="-1.7"/> # 見やすいように偏光子をずらす
            <rotate z="1" angle="90"/> # 偏光子を90°回転させる
        </transform>
   		<ref id="pol00"/>
    </shape>
```

# まとめ
Mitsuba2で新たに可能になった偏光レンダリングについて、その原理とサンプルコードの実行までをまとめました。
従来は完全に無視してきた偏光情報を考慮してレンダリングが出来るようになることで、今までではできなかったシミュレーションなどができるようになると思います。

導入編からここまでお付き合いありがとうございました。
今回解説したこと以外でも、Mitsuba2はテンプレートメタプログラミングを駆使して非常に効果的な実装がされていたり、pybind11を使いC++のクラスをPythonでオーバーライドして書けるようにして開発者は必要な機能をPythonで実装できるなど、物理ベースのレンダラーとして様々な革新的なことを行っています。[ぜひソースコードを直接見てください](https://github.com/mitsuba-renderer/mitsuba2)。

追加記事として、偏光BSDF(pBRDF)についてその詳細をまとめました。

 - [BRDFと偏光BRDF(pBRDF)の違い](https://qiita.com/kurilab/items/f9e48045bd1b9c130d0b)

以上です。

[^1]: 双方向散乱分布関数 (BSDF、bidirectional scattering distribution function)
[^2]: 双方向反射率分布関数 (BRDF、Bidirectional Reflectance Distribution Function)