自習室

こもります

FFmpegで WebM の半透過動画を作成し、Chromeで再生する

はじめに

f:id:AMANE:20141216172917j:plain

ウェブアプリで画面全体にわたるようなリッチなエフェクトを掛ける手段のひとつとして、デザイナさんに作ってもらう動画(たとえばAfter Effectsで)をそのまま使う、なんてことは出来ないかと考えてトライしてみました。

HTML要素の上に半透過の動画を <video> 要素で置くことで、もとのHTMLを動画で演出、みたいなことができています。

f:id:AMANE:20141216134814p:plain

できあがった物はこちらで確認出来ます

ブラウザは Chrome しか対応しておりません!ご注意。

環境

ちなみに、WebM を透過して表示できるのは、PC/MacChrome だけのようです。悪しからず。

Firefox@Mac だと、アルファチャンネルが捨てられて、全画面カラフルに塗りつぶされます

Safari@Mac だと、そもそも再生されません

元ネタ

今回の記事は、こちらの記事を足がかりに、追加でいろいろ調べてまとめた物になります。

動画の作り方

普段からデザイナさんに After Effects で動画を作ってもらっているので、この記事では After Effects を使ってにアルファ付き WebM 動画を作ってみます。

もしかしたら最新の After Effects では既にアルファ付き WebM の書き出しに対応しているかもしれませんが、私が使える 5.5 では出来なかったので、エンコードには FFmpeg に梱包されている vp8 (libvpx) を利用します。

f:id:AMANE:20141216152454p:plain

After Effectsコンポジション作成

新規プロジェクトから普通にコンポジションを作成し、そこに要素・アニメーションを加えていきます。半透過のテストをしたかったので、アルファが変わったり、淡いグラデーションがかかっているような動画を作ろうと思い、今回はこちらの記事を参考にサンプルを作ってみました

WebM は 60fps にも対応しているので、コンポジションのフレームレート設定を 60fpsにしました。

背景はデフォルトの黒です。

f:id:AMANE:20141216173304j:plain

After Effects でアルファ付き QuickTime Movie 書き出し

FFmpegでWebMにエンコードする元になる、アルファ付きの動画を出力します。AVI と QuickTime形式がアルファ付きに対応しているようですが、AVIだと尋常じゃない容量になるので、QuickTimeで書き出しますことにします。

対象のコンポジションを選択した状態で コンポジション > レンダーキューに追加 を選ぶと、レンダーキューウィンドウにひとつ、レンダリング処理が追加されます。

そのレンダーキューの "出力モジュール" を選択して、下記のような設定にします。ポイントは以下の3点です

  • 形式: QuickTime
  • チャンネル: RGB + アルファ
  • カラー: ストレート(マットなし)

f:id:AMANE:20141216142307j:plain

カラーを「合成チャンネル(マットあり)」 にすると、アルファチャンネルだけで無く、RGBのレイヤーも調整された動画が描き出されます。 出力された MOV ファイル単体を QuickTime で再生するときはこれで良いのですが、WebM にエンコードして他の画面に合成した際に、色味が暗い感じになってしまいます。

ストレートアルファとプリマルチプライド・アルファ(合成チャンネル)の違いは、こちらのページが詳しいです

あとは、出力先で適当に場所を決め、ファイル名を決めてレンダリングを行います。

FFmpeg で WebM にエンコード

インストール

FFmpegは別途インストールしておいてください。 Windowsの場合は、binaryをダウンロードしてきて、どこか適当な場所に解凍し、システム環境変数の path に、ffmpeg/bin のパスを通すと、コマンドプロンプト/PowerShell からコマンドをたたけるようになります。

エンコード

先ほど出力した mov ファイルのあるディレクトリに移動後、以下のコマンドでエンコードを行います。

ffmpeg -i webmtest.mov -crf 10 -b:v 1M webmtest.webm

オプションはいまいちよく理解出来ていないのですが、下記サイト様に説明が載っています。要は、オプション指定しないと結構画質が悪くなるので、VBR(可変ビットレート) の目標値 (1Mbits/s) と、VBRする際の最低品質保障 (4-63 での 10) を指定してね、という感じみたいです。

これでアルファ付きの WebM 動画ファイルが書き出されました。このファイルは、 Chrome にぽいっと投げ込むと、その場で再生してもらえます。が、背景がとくにないので真っ黒に表示され、このままでは透過できているのか分かりません。

f:id:AMANE:20141216173420j:plain

再生

というわけで、背景と適当なHTML要素を加えた上に透過動画をおいてみたのが、一番初めの作例になります。

再掲 → webm transparent test

画質優先で連番.tga を使う場合

AfterEffects から QuickTime(MOV) で書き出した時点で、圧縮率を最低にしたとしても、最終的なWebMにたどり着く前に何かしらの圧縮が入ってしまっていることになります。画質最高とは言えません。

そこで、一度無圧縮のアルファチャネル付き連番画像を書き出し、それから アルファ付き WebM をエンコードする、というパスを取ることもできます。

f:id:AMANE:20141216152504p:plain

連番 TGA 書き出し

新しいレンダリングキューを作成し、出力モジュールを以下の様に設定します。

  • 形式:Targa シーケンス
  • チャンネル: RGB + アルファ
  • カラー: ストレート(マットなし)
  • 形式オプション
    • 解像度 32bits/pixel (アルファチャネル付き)
    • 圧縮はなし!

その上でレンダリングすると、だだーっとTGAファイルが書き出されます。1920x1080、32bit ですと 1枚 8MB ほどもあるのでかなりつらい感じになります。。。

f:id:AMANE:20141216150852j:plain

連番 TGA から WebM にエンコードする

入力を、 MOV から 連番tga に変えるだけです。

ffmpeg -r 60 -i webmtest_%05d.tga -crf 10 -b:v 1M webmtest.webm

tga ファイルのサイズは膨大になりましたが、vp8 としては同じ圧縮を行うので、最終的に出てくる WebM ファイルの容量は、MOV を経由した場合と同程度になります。

さいごに

Chrome 限定な上、 WebM ってぶっちゃけそんなに流行っている気配も無いので、この手法もこれといって使えるシーンが無い可能性は大きいと思います。

けどせっかくちゃんと出来るようになったので、まとめておきました。ちゃんちゃん。

(タブレットで) 鏡餅を光らせて新年を迎えましょう | 実装編

はじめに

前回は、おうちハック Advent Calendar 2014 向けとして記事を書きました。そちらでは全体の紹介と、取り組んだ背景みたいなところにフォーカスして書いたので、今回は自分向けに実装で手こずったところ等をメモしていきます。自分用のメモなので雑多でごめんなさい。

目次

  • socket.io でルーム管理。おもち側が閉じたら、繋いでいるコントローラに伝達する仕組みなど
  • 様々なブラウザ・解像度への対応 (軽めに…
  • さくらのVPSに Node.js アプリをデプロイする際のTips
  • そのほか細かい技

おさらいのコード

記事中にコードを展開していくとバカ長くなりそうなので、言及場所にリンクを貼るスタイル。

おさらいの動画

おさらいのシステム図

f:id:AMANE:20141225104949p:plain

socket.io でルーム管理。おもち側が閉じたら、繋いでいるコントローラに伝達する仕組みなど

前回の記事でもちらっと書きましたが、今回の案件、本来なら(おそらく) WebRTC を使ってやるべき案件です。光る端末に対し、リモコンが直接指示を出します。基本は1対1で、始まった後は間にサーバが挟まっている必要はありません。

しかし今回は、自分の不勉強のため短いスパンで WebRTC を使いこなせそうになかったので、使ったことのあるsocket.io を使い、間にサーバが挟まるシステムにしました。そのため、利用者が増えると明らかに不利です。アドベントカレンダー向けのネタアプリですので勘弁してください。

そんな中でも一応こだわって頑張った辺りについてメモしておきます。

room名によるおもちの重複防止

おもちとコントローラの対応付けは、socket.ioが備えているroomの仕組みを使って行っています。roomは一般的なチャットルームを実装するための基本機能なのですが、

  • 存在しないルーム名でjoinしたら、新たにルームが作られる
  • 既存のルーム名でjoinしたら、既存のルームに入る

という動作になっております。

OMOCHI HACK ではこの仕組みを利用しつつ「おもちは必ずオリジナルな名前のルームとして作って、そこにコントローラが名指しでjoinする」仕組みとし、おもちの名前は重複を許さないことにしました。

  • おもち側で存在しないルーム名で入ろうとしたら、joinする → 新たにルームが作られる
  • おもち側で既存のルーム名で入ろうとしたら、既に使われている名前であると警告を出し、再入力を促す

この辺りの処理は、 server.js L62 - L112 あたり にコーディングしてあります。socket.ioのルーム管理とは別に、独自に現存するおもちのリストを管理することで実現しています。

おもち死亡のおしらせをコントローラ側に通知する

通常利用だと、おもち側が閉じるケースとして以下の様な物があります

  • ページをリロードする
  • ブラウザの戻るボタンを利用するなどページ遷移を行う
  • タブを閉じる
  • スマホを放置してしまい画面がロックされる
  • スマホで別のアプリを開く
  • 別の名前のおもちを作り直す

これらを検出したうえで、以下の事をやらなければなりません

  • ルーム(おもち)をリストから削除する(利用された名前がたまってしまわないよう)
  • おもちが消えたことをコントローラに通知する
window.onpagehide を利用する
  • ページをリロードする
  • ブラウザの戻るボタンを利用するなどページ遷移を行う
  • タブを閉じる

この3パタンについては、window.onpagehide で検出できます。はじめ window.onbeforeunload で書いていたのですが、Safari@iOS8 ではこのイベントが使われなくなって居ることを知り、pagehide に書き換えています。

この辺りの処理は、light.js L133 あたり にコーディングしてあります

socket.io の disconnect イベントを検出する
  • スマホを放置してしまい画面がロックされる
  • スマホで別のアプリを開く

この2パタンの場合、私が調べた限りでは ウェブアプリがそれを検出して何かする、というのが出来ませんでした。しかし、socket.io の方で、クライアント(ここではおもち)が不活性な状態を検出してdisconnect イベントとして処理する仕組みがあるようで、それを利用する事にしました。

具体的には、disconnectイベントを送ってきたsocketのidを調べて、それがおもちリスト(コード内では lightlist )に存在するか調べ、していたら削除する、と言う物です

この辺りの処理は server.js L49 - L55 あたりにコーディングしています

イベントを拾えるケース
  • 別の名前のおもちを作り直す

最後のパタンは、サーバ側で処理しています。自分が既におもちとしてroom名を持っていて、それと同じ名前で作ろうとした場合は、何もせず既存のroomを利用します。新しい名前を指定した場合は、さっきまで使っていたroomを破棄し、新しいroomを作ります。

この辺りの処理は、 server.js L105 - L109 あたりでコーディングしています。

おもちが消えたことをコントローラに通知する

上記パタンでおもちが消えたとき、コントローラ側は、カラーコントロールのUIがdisableされ、再度おもちの名前入力を要求される画面になります。これで、接続が切れたことをコントローラ側からも認識出来ます。この辺りの処理は sever.js L138controller.js L123 にコーディングしてあります。

f:id:AMANE:20141226202130p:plain

様々なブラウザ・解像度への対応 (軽めに…

ちまたのウェブデザイナ、ウェブコーダ様方にあたられましては、日々多様化するディスプレイの解像度や、ブラウザごとの微妙に異なるCSSの解釈に日々頭を悩ませていることと存じ上げますが、私も今回のアプリを作り上げるに当たりまして、「出来る範囲で・時間が許す範囲で」広めに対応を取ってみたところです

動作確認しているのは以下の環境

です。すべて最新の物です。

レスポンシブ的CSS切り分け

  • @media screen and (min-width: 320px) and (max-width: 480px) 的な物を使う

vendor prefix

-webkit-transform -moz-transform とか。

ブラウザごとの対応状況を調べられる。おなじ webkit でも、Chromeではつかえて、 safari@iOS じゃ使えない、とかかなりよくある。

Chrome for Androidborder-radius が微妙に効かない件

コントローラの円形のカラーピッカーを描くのに <div> に対して四隅50% でradius をかけようとしたが、Androidでだけうまくいかない。

f:id:AMANE:20141225220038j:plain

ここを参考に修正

.selector {
/* だめ */
border-radius: 50%;

/* だめ */
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;

/* OK */
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 49.9%; /* ここがポイント */

/* これでもOK */
border-radius: 50% 50% 50% 49.9%;
}

この記事では Galaxy S4 で、 と言っている。私の場合は Nexus7 (2013)。機種依存かもしれない。

さくらのVPSに Node.js アプリをデプロイする際のTips

基本

forever さまさまです。

この記事では /etc/nginx/conf.d/ 内にリバースプロキシの設定を書いていますが、

  1. /etc/nginx/sites-available/ 内に今回のアプリ用の新しい設定ファイルを作っておき
  2. /etc/nginx/sites-enabled/シンボリックリンク しておくのがいまどき、なはず

linux - What is the different usages for sites-available vs the conf.d directory for nginx - Server Fault

自分が作ったサイトの設定ファイルはこんな感じ

# /etc/nginx/sites-available/omochi_hack

upstream omochi {
    server localhost:3000;
}

server {
    listen 80; 
    server_name omochi.izmiz.me;

    proxy_redirect                          off;
    proxy_set_header Host                   $host;
    proxy_set_header X-Real-IP              $remote_addr;
    proxy_set_header X-Forwarded-Host       $host;
    proxy_set_header X-Forwarded-Server     $host;
    proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://omochi/;
    }
}

VPSのポート設定

そういえば以前iptablesで厳しくポートを絞っていたのを忘れていました。express が ポート3000番でサーバをあげているので、あけてあげなくちゃいけません。

sudo ufw allow 3000/tcp

おなまえ.com のDNS設定

リバースプロキシに設定した omochi.izmiz.me がちゃんと解決出来るよう、DNSに覚えていただきます

おなまえ.com > ログイン > 今回のドメイン > (タブ)ドメイン設定 > DNS関連機能設定 > 今回のドメインを選んで「次へ進む」 > DNSレコード設定

ログインした状態で直リンク > https://www.onamae.com/domain/navi/dns_controll/input

普通にたどるととんでもない階層にいます。ほとんどここしか使わないのに…!

ここに、 nginx の sites-availabe に設定した server_name を登録します。

以上

以上で、 http://omochi.izmiz.me にアクセスすると、 さくらVPS内の expressが立てている 3000 番ポートで動いている OMOCHI HACK アプリにアクセス出来るようになりました。

そのほか細かい技

今回のアプリで参考にさせていただいたその他のノウハウをまとめておきます

Canvas + image でカラーピッカー

letter-spacing したテキストをセンタリングする

index.html L25 - L32

letter-spacing: 1em すると、文末にも1文字スペースが空いてしまうので、その分文末に負のマージンを与えるか、文の頭に正のマージンを与える

ブラウザの「ホーム画面に追加」を利用する

これをしてホーム画面からアイコンタップで起動すると、全画面でアプリが開きます。ここで登録されるアイコンについての覚え書き

log4js でログ取り

ロギングのセッティングを json で外部化出来るのが素敵。

内で1行のテキストを垂直方向にセンタリング

.selector {
  height: 100px;
  line-height: 100px; // heightline-height に同じ値を指定する
}

JavaScriptHSV -> RGB 変換

jQuery.delay() が便利

jQuery .val() で form の値を取得

jQueryセレクターを取得

$something.selector で取得 出来るのだけど、 deprecated. どうしましょう。今回使ってしまいました。

カラーホイールの絵の描き方

さいごに

今回、Advent Calendar締切ドリブンで、UI、通信、サーバサイド、サーバへのアプリのデプロイ と1人で通貫でやってみました。できあがった物はまぁしょぼいのですが、個人的には非常に大変でした。良い経験になりました。

(タブレットで) 鏡餅を光らせて新年を迎えましょう

この記事は おうちハック Advent Calendar 2014 25日目(なんと最終日!)の記事です。 前日は @K-A-Z さんの 「Arduino+Raspberrypi+GoBotで恋人たちの聖夜を支援する - Qiita」 でした。

ベッドイン検知、まじめなライフログとして王道な利用法も出来る気がします。わくわくしますね。

はじめに

f:id:AMANE:20141224220523j:plain

今年は業務でウェブのクライアント…特にUI周りを作る機会が多かったのですが、自分でサーバサイドから通信そしてUIまでアプリを全部作りきる、ということをやったことが無かったので、アドベントカレンダー締切ドリブンでやってみようと決めました。

正直、アドベントカレンダーだったら何でも良かった のですが、リストをざーっと見てみると、どうも敷居が高い感じでちょいと難しい。そんななか「おうちハック」という響きの緩さに「これならいけるんちゃうか…」とつられてみたところです。ネタはそこから考えました

おうちハック Advent Calendar さん、登録した時点で最終日しか空きがなかったので、クリスマスは終わったことにして日本人らしくお正月ネタにしよう、おもちじゃん、光ったら素敵じゃん、やったー!\(^_^)/

というわけで、おもちを光らせることにしました。

できたものの紹介

使い方を動画で紹介します。

※注記: OMOCHI 単体でも、じんわりと色と背景が変わっていくオートモードでお楽しみいただけます!

公開してます

PCからももちろん利用できます。ノートPCならお餅をのっけるのも可能ですね!

負荷とかセキュリティとかはわかりません!重かったり落ちてたらすみません。

OMOCHI HACK

システム構成

構成は下図のようになっています。OMOCHI側、CONTROLLER側のUI、ブラウザでそれぞれのアドレスにアクセスするとウェブアプリとして表示されその場で使用できます。OMOCHI側は新規作成時に固有の名前をつける必要があります。

f:id:AMANE:20141225104949p:plain

上の動画にもありましたとおり、コントローラからおもちの光らせ方をコントロールできます。この用法だと 「明らかにWebRTCだろ」 というおしかりを受けそうですが、甘んじて受け入れます。WebRTCに一瞬トライしたのですが、 難しそうだったのでパスしました 。来年頑張ります!

ソースコード

あげてあります。

使った鏡餅

厳密にはこれではないのですが、10インチiPadには「5号」サイズの鏡餅が良かったです!この大きさになると台座も付いてくるので、トップの写真のようにグッと雰囲気が増します。

一応まじめらしい背景

今回のプロジェクトの背景として「おうちハック」の視点から見て2点、抜き出してまとめてみました。

余ったスマホ活用

ここ数年で一気にタブレットスマートフォンが普及し、世代交代も速いため、1人1台や2台にとどまらず、古くなって引出にしまっている端末もたくさん持っている様な状況が(一部のギークの間では)普通になってきたように思います。

Arduino などを使うと従来より非常に簡単にフィジカルな入出力が可能で、しかもネットにつながったツールを作り出すことが出来ますが、入出力の自由度を下げてタッチや液晶表示などに限定して良いケースなら、余剰のスマホや利用頻度のひくいタブレット超高性能 なネットワークのノードになります。そしてソフトウェアのコーディングだけで済むので、敷居もグッと下がります。

こういったムーブメントは既にかなり広がってきていて、余ったスマホを監視カメラ代わりに利用するのは非常にメジャーです。また、高性能なプロセッサを小型な信号処理部として活用することで、筋電義手を材料費3万円ほどで作ろう、というプロジェクトも動いています。

余ったスマホ、スゴイです。専用のデバイスを買わなくても、家に有るもので皆さん楽しめます。

つながり感通信

おうちをハックするので、ハックして/されて嬉しいのが自分だけじゃつまらないです。家には自分の家族も居ますし、また、遠く離れた生まれ故郷の実家には、自分の両親もいます。そんな 「おうち」どうしをつなげるような道具 が欲しいなぁ、と思い、今回ネットワーク越しのコントローラ機能を実装しました。

似たようなプロジェクトで有名どころだと、NTT研究所の一連の研究があります。

また、商品として出ている物もあります。

今回の実装はセキュリティ的にアレゲな感じもしますが、勘弁して下さい。

手軽にパーソナルなクラウド

つながり感通信に関してもう一歩踏み込んでみます。

おうちハック的なプロジェクトは(そんな中から大当たりする物も出てくると信じていますが)、2014年現在は、デバイスやネットワークと使いこなす技術がこなれてきたことによって、多少なりとも技術が使える人が、個人の小さな満足を満たす仕組みを作りやすくなったことから発生している物が多いように思います。俗に言うMAKERSですね。

そういうパーソナルで小規模なサービスをIoTとしてネットワーク上で利用しようと思うと、気軽にクラウドのサーバを利用できることは非常に便利です。今回はさくらのVPSで運用していますが、heroku 的なものも便利だと思います。

実装の話 / さいごに

ざっくり言うと上の図に書いたようなシステムで動いているのですが、自分の技術まとめとして書きます。

ですが、今回は既に記事が長くなってしまっているので、次回に持ち越させてください。

Advent Calendar的にはこんな終わらせ方で良いのかな?? それではみなさん、メリークリスマス & 良いお年を~

UnityでOpenCVを どう使うか - OpenCVSharp vs OpenCV dllを使う環境 vs スタティックライブラリ

はじめに

最近何編かにわたって、Unityで OpenCV を使う方法についてまとめてきました。

扱った手法は大きく分けて三つあります

  1. OpenCVSharp を使う
  2. OpenCV のDLLを使う前提で、必要な関数をラップした Native Pluginを作る
  3. OpenCVの staticlib を必要な分だけ取り込んだ Native Plugin を作る

どの手法を使うか、ちょっと考えてみました

それぞれの特徴

1. OpenCVSharp

長所

  • C# だけですんで楽
  • DLLのビルドとかない

短所

  • C++OpenCVに慣れているとちょいちょい混乱する。ちまたのリファレンスが基本 C++なだけに。
  • C++ のライブラリを全部ぶっ込み、OpenCVSharp の DLLもぶっこみ、とする必要があるのでオーバーヘッド大。特に配布を考えると。
  • あくまで有志の方が作った物で、公式の最新について行けるとは限らない

2. OpenCV のDLLを使う前提で、必要な関数をラップした Native Pluginを作る

長所

  • (OpenCVが事前にインストールしてある人にとっては) 配布物が最小になる
  • 標準の書き方 = C++ で書ける
  • 公式の最新をそのまま使える

短所

  • 利用者がOpenCVをインストールするか、配布物一式にOpenCVのDLLを同封する必要がある
  • ビルドの設定がちょっと複雑
  • デバッグが面倒

3. OpenCVの staticlib を必要な分だけ取り込んだ Native Plugin を作る

長所

  • OpenCVのDLLを使う必要が無いことまで加味すると、Native Plugin の容量が最小になる
  • そのため、おそらく起動も速い
  • 標準の書き方 = C++ で書ける
  • 公式の最新をそのまま使える

短所

どう使うか?

こう考えました。

  • OpenCV のたくさんあるリファレンスなどを生かせるので、標準の書き方(C++)にしておきたい
  • いろいろ取り込んでアプリが肥大化するのはかっこよくない

短所については

  • ビルドの複雑さは慣れでカバー

というわけで、 3. staticlib を使ってDLLを作る が良いと考えました。

デバッグが面倒、という件も

  • 一応前回の記事でクリアできたと思っている
  • そもそもDLLにする主旨は、役割の分担を明確にし極力疎結合にすることです。データの受け渡しなど界面をしっかりと設計し、OpenCVで行う画像処理などは C++ 単体でテストしておいて、使えることを確認した後Unityで使うようにすべきでしょう。つまりデバッグは普通にC++で完結して行う。

という感じの進め方で良いと思っています。

(前回の記事)

Unity + Visual Studio で、Native Plugin をデバッグする (Windows)

つづき

前回、 Visual Studio を使って Managed(C#)なPluginを作ってUnityVSを使ってデバッグする方法について書きました。

今回はその続きで、Native(C++) な Plugin をつくってデバッグする方法について書きます。そもそもOpenCVを使いたくてプラグイン化をしているので、今回が本命です。

環境

GitHubにあげました

記事はmanaged/nativeで2回に分けましたが、プロジェクトは一つにまとめてあります。

Native Plugin の場合

UnityVS/MS 公式によれば、 UnityVSとしてはサポートしていないことになっています。

今回 Unity 向けに調べるまでは知らなかったのですが、もともとVisual Studioには、Windows上で実行中のプロセスにアタッチしてデバッグを行う機能があります。

やってみます。

実行中のプロセスにアタッチ

デバッグしたい Native Plugin のプロジェクトを選択した状態で、

DEBUG > Attach to Process...

と選び、開いたウィンドウで、 Unity.exe を選択します

f:id:AMANE:20141215223451j:plain

その状態でブレイクしたい箇所にポイントを張って、 Unity Editor から Play すれば、ブレイクしてくれます。

f:id:AMANE:20141215224559j:plain

コールスタックを見ると、 Unity.exe!MonoBehaviour::Update() からDLLの GetRandom() 関数が呼び出されていることがわかっておもしろいです。

ブレイクされないときがあった

私のリポジトリからCloneしたもので、正しくブレイクに引っかかったくれなかった場合は、一度Unityを閉じてから、Visual Studio の方で Plugin をビルドし直し、Unityの Assets/Plugins のdllを上書きしてから再度実行すると、引っかかるようになります。

最後に

これで Native Plugin もデバッグができるようになりました。前々回の記事OpenCV を Native Plugin 化しましたが、こちらの場合でもちゃんとブレイクできることを確認しています。

Unity + Visual Studio で、Managed Plugin をデバッグする (Windows)

はじめに

前回、OpenCV を 必要な分だけ Native Plugin の DLL としてまとめて利用する方法について書きました。

C++のライブラリを使ってC++のコードを書いて、Native Plugin として利用できるのは良いのですが、Unity Editor での実行中に、C++側のデバッグができるんかい?という疑問がわきました。

今回はそのあたりを調べてみました。ついでにManaged Pluginのデバッグの仕方についても調べたのですが、分量の関係で Managed/Nativeで2回に分けることにしました。

環境

今回もがっつり Windows + Unity Pro 限定な内容になっておりますがご容赦ください。

題材

今回の記事は、Live Training "WRITING PLUGINS" の作例に対してデバッグできるよう改変を加えています。プロジェクトの作り方、コードの書き方、ビルドの仕方はこちらを参考。

GitHubにあげました

記事はmanaged/nativeで2回に分けましたが、プロジェクトは一つにまとめてあります。

まずは Managed Plugin から

UnityVSでできるんじゃないか?と天啓を受けて調べてみたら、確かにありました。

Managed(C#) Plugin は 上記事に書いてあるとおりでできますが、目立ったところだけこちらにメモっておきます。ちなみに、最後のデバッグの開始部分は、上のリンクと少し異なることをしています。上のリンクの仕方でもできますが、本記事のやり方の方が少し楽なので紹介いたします。

Target Framework: .NET Framework 3.5

Unity の Mono が .NET で言うところの 3.5 相当らしいので、そうしておきます。これをやらなくてもDLLのビルドはできるのですが、プロセスにアタッチしようとした際にエラーが出て止まります。

厳密には Unity 3.5 .net SubsetBase Class Libraries を選ぶのが正しい ようです。.NET と Mono は微妙に異なる部分もあるようですし。

PROJECT > PROPERTIES > Application > Target frameworks:

f:id:AMANE:20141215213807j:plain

出力先を Assets/Plugins にする

いつも通りの $(SolutionDir)\(Configuration)\ な場所に出力したdllらをあとから Assets/Plugins にコピーすればいいかと思ったら、VSプロジェクトでのファイル構造などデバッグに必要な情報が、DLLファイルと一緒に書き出される .pdb ファイルに含まれているようで、ビルド→出力 の時点で、利用する場所に書き出される必要があるようです。

PROJECT > PROPERTIES > Build > (下の方)Output > Output path

f:id:AMANE:20141215214510j:plain

ブレイクポイントを使ったデバッグ

あらかじめ Solutionの Properties から、現在選択しているプロジェクトが実行されるようにしておくのがおすすめです。

f:id:AMANE:20141215214829j:plain

Unity Editorも起動した状態で、VSの方で今回デバッグしたいManaged Plugin のプロジェクトを選択し DEBUG > Attach Unity Debugger を実行すると、下のようなウィンドウが開きます。もしここが空欄だった場合は、一度 Unity の方で Play しておくと、出るかもしれません。

f:id:AMANE:20141215215355j:plain

これで該当の Unity の Project 名を選択すると、Unityに対してVSのデバッガがアタッチされ、待機状態になります。この状態でブレイクポイントを張って、Unity Editor に戻って Play します。確かに DLL 化された部分のコードでブレイクできています。

f:id:AMANE:20141215215826j:plain

つづきます

と、Managed な Plugin のデバッグ法について整理してみましたが、実際にManaged Plugin を作るシーンが余り思いつかないので、今回は助走です。

本命の Native Plugin のデバッグは次回の記事で扱います。

UnityでOpenCVを Native Plugin にして利用する (Windows)

はじめに

UnityでOpenCVを使いたい動機と下調べについては前回の記事に書きました

UnityでOpenCVSharpをつかってOpenCVする。マルチスレッドにもしてみる。 (Windows) - 自習室

今回は、OpenCVを native plugin 化して使う方法についてまとめます、というか、 @hecomi 先生のをなぞった時に出た問題点をまとめておきます

環境

参考にした記事

基本(ただしMac)

この記事で、@hecomi 先生が OpenCVを native plugin 化して使う方法についてまとめてくださっています。基本はこれですが、内容がMacでbundleな感じです。

Unity で OpenCV で作成したテクスチャをネイティブプラグイン経由で利用してみた - 凹みTips

公式の基本(ただし英語)

Windowsでのやり方は、公式のLive Trainingで簡潔に説明されていました。前半はC#でManagedなpluginの説明で、23分あたりから、C++で作る Nativeなプラグインの説明になっています。ランダム関数を回して適当に数値を返すだけのプラグインを例にしているので、英語ですがわかりやすいと思います。

Live Training: Introduction to Plugins - YouTube

dllを作ること自体は Visual Studio Expressでもできますが、作った native Pluginを使うことは、Unity Proでないとできません。このあたりはUnityの厳しいところですね。

応用 Windows8.1 で、DllNotFoundException への対処付き

また、 @hecomi 先生が別の記事で、OpenCVのスタティックライブラリをラップしたdllにする方法についてもまとめくださっています。この記事で通常のlibを使ってdllにするとDllNotFoundException が出る、と報告されていますが、私のところでは解消できましたので後ほど紹介します。

Unity と OpenCV を組み合わせて現実・仮想双方を加工した AR な世界を Oculus Rift 越しに覗いてみた - 凹みTips

やること

ここでは試しに、先述の Windows8.1 の環境下で、@hecomi さんの一つ目の記事でやられていた、 OpenCVでカメラキャプチャーしてウィンドウを開いて表示しつつ、Unity内でテクスチャとして利用する、をやってみようと思います。

手順

プロジェクトの作成

このあたりは、Unity - Writing Plugins を参考に。

  • Visual Studio
  • VC++ / Win32 Application を
  • Application type: DLL
  • Additional Options: EmptyProject でつくる

f:id:AMANE:20141212212952j:plain

OpenCV周りのプロジェクト設定をする

PROJECT > Properties からいろいろ下記のような設定します

追加のインクルードディレクトリ

OpenCV関連のヘッダを読めるようにします。適宜OpenCVをインストールした場所に読み替えてください

f:id:AMANE:20141208233647j:plain

OpenCVのスタティックリンクライブラリを利用する

OpenCVをせっかくdllとしてラップするので、別途 OpenCVのdll群を使わなくてもよいようにしたいです。ユーザにOpenCVをインストールさせたり、完成したアプリのフォルダ内にdllをたくさん含んだ状態で配布するのはちょっといけてない感じです。

そこで、OpenCVからリリースされている <OPENCV_ROOT>\build\x86\staticlib.lib ファイルたちを利用するように指定します。これを使って以下ので順でdllを作成すると、すべての .lib ファイルが一つのdllに固められて、そのdllだけをユーザに渡せばいい状態になります。

f:id:AMANE:20141212213158j:plain

ランタイムライブラリをマルチスレッド(/MT)に変更

ここは @hecomi さんの受け売りです。

f:id:AMANE:20141212212939j:plain

DLL化のためのおきまりのコードを書く

#include <iostream>

#define DLLExport __declspec (dllexport)

extern "C"
{
    DLLExport int GetRandom()
    {
        return rand();
    }
}

先ほど動画を貼ったLive Trainingでは、上記のようなコードで ランダムな数値をはき出すだけの機能を dll 化しています。同様の手法で@hecomiさんのこちらの記事では、OpenCVを使ってカメラをたたいたり、別ウィンドウで表示したり、Unityのテクスチャとして使えるようにしています。コードは @hecomiさんの記事を参考にされてください

DLL化のために必要なスタティックライブラリをリンクする

私の環境ですと、 @hecomiさんの記事 の通り だとライブラリが不足しているようで、unresolved external symbol エラーが大量発生します。エラーは下記のようなものたちで、多くはOpenCVのDLL本体に含まれているもののようです。これらもリンクするようにして、最終的には以下のような感じになります。

f:id:AMANE:20141212213644j:plain

このあたりは、こちらの記事様で勉強させていただきました

ビルド→Unityで使う

BUILD -> BUILD <Project Name>

で、ビルドされ、ReleaseなりDebugなりのフォルダに .dll ファイルが生成されます。

これを、Unityで Assets/Plugins/hoge.dll の位置に置きます

dllで定義された関数をUnityで利用する際は、以下のようにDllをインポートし関数を宣言するコードを用意し、オブジェクトにアタッチして使います。これは先述のUnity公式Live Trainingでの例です。

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class TestNative : MonoBehaviour
{
    [DllImport("RandomNumberDLL")]
    private static extern int GetRandom();

    // Use this for initialization
    void Start ()
    {
        print("Native Random Number: " + GetRandom());
    }

}

同様に @hecomi さんの記事を参考に、dllに定義したOpenCVを使った関数を呼び出してください

その2:スタティックライブラリを使わない場合

先述の Unity公式 live Trainingと同じやり方になります。

通常のライブラリファイルをリンクする

ProjectのPropertiesで staticlib フォルダを参照するように指定している追加のライブラリディレクトリを、 lib フォルダを参照するように書き換えます。

f:id:AMANE:20141212220007j:plain

ランタイムライブラリを /MD にする

f:id:AMANE:20141212220134j:plain

システム環境変数OpenCVのDLLのありかを通す

Path に以下の二つを記入します (F:\develop\opencv\ の部分は、ご自身の環境に合わせてください)

F:\develop\opencv\build\x64\vc12\bin; 
F:\develop\opencv\build\x86\vc12\bin;

はじめ x64 のものだけを追加していたのですが、 dll を 32bit でビルドしてたため、DllNotFoundException が発生しました。 x86 のbinもPathに通したら、エラーがなくなりました。

ビルドなど以下略

ここから先は、staticlibを使う場合と同じです

最後に

これで、 @hecomi さんの記事のように、カメラ画像がテクスチャに貼れて、同時に別ウィンドウが開きカメラ画像が見られたらOKです。

@hecomi さんありがとうございました。