OpenToonzの入射光エフェクトをJavaScriptに移植する[C++/OpenCV]
アニメーション製作ソフトウェアの「OpenToonz」(オープンソース)のプラグインである「入射光」(light incident)エフェクトをJavaScriptに移植する事を試してみました。
入射光エフェクトとは雲の隙間から光の帯が大地に降り注ぐ光景です。天使の梯子とも呼ばれています。次の画像はOpenToonzの入射光エフェクトです。
OpenToonzはアニメ製作ソフトですので、このエフェクトはアニメーションにする事が可能です。OpenToonzのインストールや日本語化は「OpenToonz」の日本語化とプラグインのインストールの記事をご覧ください。
まずは結果から
1枚の画像を入射光(光の帯)を使用したフェードインのGIFアニメに変換する事に成功しています。ただ、光の具合が少し違いますね。これは、元のエフェクトの「blurが0」「Bloomのlevelが0」だからです。
何故、黒のアルファブレンドを使用したフェードインなのかは、後々わかります。
元のソースコード(LightIncident)
入射光のソースコード
dwango_opentoonz_plugins/LightIncident/src/main.cpp
ソースコードは407行で大したコード量ではありません。
JavaScriptへ移植
さて、15年ぶりぐらいでC++のソースコードを読み込んでみました。当時はあまりみかけなかった「vector」「template」などがありましたが、C++自体のコードは特に問題はありませんでした。
問題発生!
C++からJavaScriptへ徐々に移植しはじめた所に問題が発生しました。
std::uniform_real_distribution
std::cauchy_distribution
が出現しました。
これは「メルセンヌ・ツイスター」「離散一様分布」「コーシー分布」で乱数や乱数テーブルを生成するものです。
「メルセンヌ・ツイスター/離散一様分布/コーシー分布」はJavaScriptには無いので、自分で関数をつくろうにも理数系の大学/大学院レベルの数学みたいですので、断念。
苦肉の策としては、元のコードは乱数テーブルを使用して乱数を発生させているので、毎回、同じ値が発生します。そこに目を付けて、C++で乱数を発生させてその値をJavaScriptの配列としました。
またもや問題発生!
苦肉の策で前回は回避できましたが、しばらくするとまた問題が発生しました。
これはOpenCVという画像ライブラリのクラスのメンバメソッドです。内容は楕円を描画します。JavaScriptでは「CanvasRenderingContext2D.arc」で円を描画するできますが楕円は描画できません。
さらに悪い事にcanvas系はマルチスレッドのWeb Workerでは使用できません。ですので楕円の描画は、ImageData.dataを使用して一から自力で描画することになりました。
移植は難航するなあ。と私は独り言をいいはじめました。
ふう
元のソースコードはOpenCVを使用しているので、
こんな風にフィルタ処理が出現します。これはガウシアンフィルタのぼかしなので、この程度ならばJavaScriptでこのメソッドと同様なコードを作成できます。
致命的な問題が発生!
しばらく、調子にのっていたらとんでもないコードが出現しました。
の関数の中に
がありました。リサンプリングによるリサイズは画像の縮小で既に関数を作成していたのですが、OpenCVをなめていました。
cv::INTER_AREAは「ニアレストネイバー法」「バイリニア法」「バイキュービック法」などではなく、
と書いてありました。googleで「INTER_AREA」を検索しても3000件しか検索件数がないぐらい、マニアックです。
generate_bloom()の関数で何をしてるかというと、光の強度(ブルーム)のエフェクトです。
[光の強度あり]
[光の強度なし]
で、関数の実装内容は
これを指定回数分(level)繰り返します。繰り返した後には今度は画像を2倍にして元のサイズまで繰り返します。(詳細はソースコード参照)
早い話、光の棒の「もやもや」の部分です。試しに「ニアレストネイバー法」で3回試してみると次のようになりました。
なんだか、モザイクのようになってしまいました・・・。
cv::INTER_AREAのリサンプリングが不明ですので、OpenToonzの入射光エフェクトのソースコードだけではなく、OpenCVのソースコードまで見る必要が出て来てしまいました^^;
とういうわけで、今回は発想を変えてOpenToonzでマスク画像を作成して光の棒を合成するという手法を使って、次のサイトを作成しました。
今後、作業時間がとれましたらOpenCVのソースも覗いてみたいと思います。
コードの流れのまとめ
入射光の簡単なコードの流れです。
2.光の棒をぼかす
3.光の棒にノイズ、幾何学模様、マスクを重ねる
4.光の棒に光の強度(ブルーム)を加える
5.元のイメージに光の棒を重ねる
間違ってたらごめんなさい><w