Copernicusで直接トゥーンレンダリングを行う

![rubberToy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/4aaa11e0-7611-2e3e-102b-6efb65610ba3.gif)
# はじめに
Houdini 20.5からベータ版として新たな画像処理フレームワーク`Copernicus`が追加されましたね
実用性があるかはさておき、勉強がてらMantraやKarmaを使わずCopernicusのみで簡単なトゥーンレンダリングを行ってみました
まだ私もCopernicusを触り始めたばかりなので何かおかしい点があるかもしれません。気付いた点があればご指摘いただけると幸いです。

本記事で使用しているHoudiniのバージョンは`20.5.278`です
本記事で作成したHIPファイルは[こちら](https://drive.google.com/file/d/1kWNPBnN7hk6S0G_Xu285KE8hk_hJ-os6/view?usp=sharing)です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/06d9732e-ae28-3457-2cf3-e400288963fb.png)

# ベースシーンの作成
カメラがラバートイのまわりを旋回する簡単なシーンを用意しました。ライトの種類は`Distant`です
![flipbook.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/3767b2fe-f895-a6ff-4f41-355306da9fa9.gif)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/ad5c4215-3e96-381d-a181-6ffc1fc3d47d.png)

# ジオメトリのラスタライズ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/ee558e42-50a6-5c3b-054e-c0f9cbf81ed1.png)
このセクションではジオメトリの情報をレイヤ（画像のPlane）としてラスタライズします。
一番はじめの手順にして今回のCopernicus解説のなかで一番面白い機能です。

まずCOP Networkを作り、[SOP Import](https://www.sidefx.com/docs/houdini/nodes/cop/sopimport)でジオメトリをCOPに持ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/ddcc30f6-44d9-379c-8b32-09b695290863.png)

[Rasterize Setup](https://www.sidefx.com/docs/houdini/nodes/cop/rasterizesetup.html)はジオメトリの座標を調整したり、法線やDepthを作成したりするノードです。
今回のラバートイには法線アトリビュートが無かったので、ここで作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/944a9066-6a2f-0e78-5bd2-421b70cc5e05.png)

[Camera Import](https://www.sidefx.com/docs/houdini/nodes/cop/cameraimport.html)でカメラをCOPに持ってきます。
ディスプレイフラグをONにすると、カメラがある位置に黒い平面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/69732bfc-6763-aded-6239-e765c51ac7a6.png)

[Rasterize Geometry](https://www.sidefx.com/docs/houdini/nodes/cop/rasterizegeo.html)はあるカメラから見たジオメトリの情報をレイヤにラスタライズするノードです。
`Quick Setups↓`をクリックするとラスタライズする情報の一例が選択できます。ジオメトリに存在するアトリビュートであればここに無いアトリビュートもラスタライズできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/5ea7b5a4-bbbb-ad54-f59f-6cf9eed4f954.png)
今回はQuick Setupsから`N` `Alpha` `Depth from eye`を選択しました。
NがAttirbutesの一番上にあるのでレイヤにカメラから見たラバートイの法線が表示されていることがわかります。
これまでもSOPでpointを作ってrayを飛ばしてprimuvでアトリビュートを参照して...みたいなことで似たことはできましたが、COPだけでその処理が行えるのは便利ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/bbb90939-7f73-9ed2-69d4-6b911cb57a6b.png)

[Match Camera](https://www.sidefx.com/docs/houdini/nodes/cop/matchcamera.html)は、あるレイヤを他のカメラ空間に変換するノードですが、二番目の`camera_ref`入力に何も指定しないとXY平面に戻されます。
これですべてのレイヤをXY平面に戻します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/e97eb298-5c5e-f335-ab10-40a062ea987a.png)
（ちなみにこの変換をレイヤごとではなく一括で行う方法ってあるんでしょうか...ご存じの方がいれば教えて下さい）

# シェーディングと着色
このセクションではシェーディングの計算とかをやっていきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/af857b5a-69e1-62da-ad23-450bf15cba7d.png)
## ライト方向ベクトルの定義
シェーディングを計算するためにはライト方向ベクトルが必要なのですが、Copernicusではライトの情報は取得できないようです。普通にConstantで特定の向きのライトを定義してやってもいいのですが、直感的じゃないし調整もしづらいので、obj階層のライトの回転を参照することにします。

まず普通にobj階層でdistantライトをTransform無しで作成すると、ライトは(0,0,-1)の方向を向いていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/23fd5967-1181-f932-9f46-0b052c5f0821.png)

ライト方向ベクトルは光子が光源から進む方向ではなく、照らされる面から光源に向けたベクトルなので、Constantでライトの向きに`-1`を掛けた(0,0,1)を入力します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/a301a18b-48b1-9c27-7c20-e73086efc437.png)

[Vector Transform](https://www.sidefx.com/docs/houdini/nodes/cop/vectorxform)でライト方向ベクトルを回転させます。このとき`Rotate`チャンネルでobj階層のライトの回転を参照することで、回転が連動するようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/4e5a18ed-ca73-0c9e-d028-71b4746c0bb7.png)
つまりCOPのカメラ方向ベクトルをハンドルで制御できることになります
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/044fc0a7-5a03-2613-bedf-bb2178a54344.gif)

## 内積の計算
[Dot Product](https://www.sidefx.com/docs/houdini/nodes/cop/dot)で法線とライトの内積を計算します。これで光源と同じ向きの面は`1`、正反対の面は`-1`の値が返されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/b46aa73c-420e-6c0b-aa56-e298b2935b0a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/1f033f5f-792d-2fe3-5abf-fc7173c43ec1.png)
(画像引用元：[【連載】Unity時代の3D入門 – 第5回「拡散反射ライティング」](https://blog.applibot.co.jp/2017/09/29/tutorial-for-unity-3d-5/))

Lambert shadingの説明はggればいくらでも出てくるので、詳しく知りたい方は調べてみてください。

## 色の調整
色を調整しやすくするために色のレンジを`(-1,1)`から`(0,1)`に[Remap](https://www.sidefx.com/docs/houdini/nodes/cop/remap.html)します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/38172736-7742-bf19-c23e-d5a962e84276.png)

[Ramp](https://www.sidefx.com/docs/houdini/nodes/cop/ramp.html)で色のグラデーションを変えます。今日は猛暑日だったので涼しげな色にしてみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/2985d035-8fd5-2b30-9a30-080fc3e649cc.png)
Rampパラメータの右上に並んでるアイコン達の一番左のアイコンをクリックすると補間モードが変えられます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/5a733f87-6e72-0243-ebb1-a9aff419e062.png)

# 背景色の合成
Constantで背景色を定義します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/7a1cff28-cd07-5365-2255-26c9a8daae14.png)

[Blend](https://www.sidefx.com/docs/houdini/nodes/cop/blend.html)のModeを`Over`にして前景レイヤーを背景レイヤーに重ねます。
maskには[ジオメトリのラスタライズ](#ジオメトリのラスタライズ)のときに作ったAlphaを接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/0cc5e887-4d66-6724-3a7a-824df13be278.png)

# エッジの作成
Copernicusでエッジを作成するにあたり、複数のノードがあるようです。
今回はとりあえず無難にDepthから作成することにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/23eefcde-023b-e926-ef39-039d2483e11b.png)

Depthを[Edge Detect by Depth](https://www.sidefx.com/docs/houdini/nodes/cop/edgedetectdepth.html)に接続するとエッジが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/de1a0de3-7d76-5673-e6d1-5639e3820d17.png)

エッジのところは真っ白にしたかったのでremapでホワイトレベルを下げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/29d3751a-bbd6-b6d2-5216-2e0613b396ea.png)

いい感じにラインの色を定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/e0d59798-39d6-01c8-1772-ae6334eb9426.png)

Blendノードを使い、エッジをシェーディングしたラバートイに重ねたら出来上がり！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638853/ce8e9cac-4e1c-396f-af5f-bbbb8e9b93e7.png)

# おわりに
今後の正式版実装が楽しみなフレームワークです:eyes:
従来のCOPよりも遊びがいがありそうです。
[OpenCL](https://www.sidefx.com/docs/houdini/nodes/cop/opencl)ノードもあるのでゴリゴリコードを書くのも楽しそうですね。