ホーム > カテゴリ > HTML5・JavaScript >

OpenToonzの入射光エフェクトをJavaScriptに移植する[C++/OpenCV]

アニメーション製作ソフトウェアの「OpenToonz」(オープンソース)のプラグインである「入射光」(light incident)エフェクトをJavaScriptに移植する事を試してみました。

入射光エフェクトとは雲の隙間から光の帯が大地に降り注ぐ光景です。天使の梯子とも呼ばれています。次の画像はOpenToonzの入射光エフェクトです。

OpenToonzはアニメ製作ソフトですので、このエフェクトはアニメーションにする事が可能です。OpenToonzのインストールや日本語化は「OpenToonz」の日本語化とプラグインのインストールの記事をご覧ください。

まずは結果から

GIFアニメ変換[入射光/光の帯エフェクト]

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::mt19937_64
std::uniform_real_distribution
std::cauchy_distribution

が出現しました。

これは「メルセンヌ・ツイスター」「離散一様分布」「コーシー分布」で乱数や乱数テーブルを生成するものです。

「メルセンヌ・ツイスター/離散一様分布/コーシー分布」はJavaScriptには無いので、自分で関数をつくろうにも理数系の大学/大学院レベルの数学みたいですので、断念。

苦肉の策としては、元のコードは乱数テーブルを使用して乱数を発生させているので、毎回、同じ値が発生します。そこに目を付けて、C++で乱数を発生させてその値をJavaScriptの配列としました。

またもや問題発生!

苦肉の策で前回は回避できましたが、しばらくするとまた問題が発生しました。

cv::ellipse

これはOpenCVという画像ライブラリのクラスのメンバメソッドです。内容は楕円を描画します。JavaScriptでは「CanvasRenderingContext2D.arc」で円を描画するできますが楕円は描画できません。

さらに悪い事にcanvas系はマルチスレッドのWeb Workerでは使用できません。ですので楕円の描画は、ImageData.dataを使用して一から自力で描画することになりました。

移植は難航するなあ。と私は独り言をいいはじめました。

ふう

元のソースコードはOpenCVを使用しているので、

cv::GaussianBlur

こんな風にフィルタ処理が出現します。これはガウシアンフィルタのぼかしなので、この程度ならばJavaScriptでこのメソッドと同様なコードを作成できます。

致命的な問題が発生!

しばらく、調子にのっていたらとんでもないコードが出現しました。

void generate_bloom{}

の関数の中に

cv::resize(..., cv::INTER_AREA)

がありました。リサンプリングによるリサイズは画像の縮小で既に関数を作成していたのですが、OpenCVをなめていました。

cv::INTER_AREAは「ニアレストネイバー法」「バイリニア法」「バイキュービック法」などではなく、

ピクセル領域の関係を利用したリサンプリングでモアレを避ける事ができる。

と書いてありました。googleで「INTER_AREA」を検索しても3000件しか検索件数がないぐらい、マニアックです。

generate_bloom()の関数で何をしてるかというと、光の強度(ブルーム)のエフェクトです。

[光の強度あり]

[光の強度なし]

で、関数の実装内容は

画像を1/2にした後に画像をぼかす

これを指定回数分(level)繰り返します。繰り返した後には今度は画像を2倍にして元のサイズまで繰り返します。(詳細はソースコード参照)

早い話、光の棒の「もやもや」の部分です。試しに「ニアレストネイバー法」で3回試してみると次のようになりました。

なんだか、モザイクのようになってしまいました・・・。

cv::INTER_AREAのリサンプリングが不明ですので、OpenToonzの入射光エフェクトのソースコードだけではなく、OpenCVのソースコードまで見る必要が出て来てしまいました^^;

とういうわけで、今回は発想を変えてOpenToonzでマスク画像を作成して光の棒を合成するという手法を使って、次のサイトを作成しました。

GIFアニメ変換[入射光/光の帯エフェクト]

今後、作業時間がとれましたらOpenCVのソースも覗いてみたいと思います。

コードの流れのまとめ

入射光の簡単なコードの流れです。

1.乱数テーブルを使用して光の棒の原型を作成する
2.光の棒をぼかす
3.光の棒にノイズ、幾何学模様、マスクを重ねる
4.光の棒に光の強度(ブルーム)を加える
5.元のイメージに光の棒を重ねる

間違ってたらごめんなさい><w





関連記事



公開日:2016年05月17日 最終更新日:2016年05月18日
記事NO:01956