GWTがブラウザの拡張現実を解き放つ方法
公開: 2022-03-11GWT Web Toolkitに関する前回の投稿では、GWTの長所と特徴について説明しました。これにより、一般的な考え方を思い出して、JavaソースコードをJavaScriptにトランスパイルし、JavaライブラリとJavaScriptライブラリをシームレスに組み合わせることができます。 GWTによって生成されたJavaScriptが劇的に最適化されていることに注目しました。
今日の投稿では、もう少し深く掘り下げて、GWTToolkitの動作を確認したいと思います。 GWTを利用して、ブラウザで完全にJavaScriptでリアルタイムに実行される拡張現実(AR)Webアプリケーションという独特のアプリケーションを構築する方法を示します。
この記事では、GWTがWebRTCやWebGLなどの多くのJavaScript APIと簡単に対話できるようにし、ブラウザーでの使用を意図していない大規模なJavaライブラリであるNyARToolkitを利用できるようにする方法に焦点を当てます。 GWTによって、私のチームとJooinkの私がこれらすべての要素を組み合わせて、今すぐブラウザーで試すことができるマーカーベースのARアプリケーションであるペットプロジェクトPicshareを作成する方法を紹介します。
この投稿は、アプリケーションを構築する方法の包括的なウォークスルーではなく、GWTを使用して、一見圧倒的な課題を簡単に克服する方法を紹介します。
プロジェクトの概要:現実から拡張現実へ
Picshareは、マーカーベースの拡張現実を使用します。 このタイプのARアプリケーションは、シーンでマーカー(このような特定の、簡単に認識できる幾何学的パターン)を検索します。 マーカーは、マークされたオブジェクトの位置と方向に関する情報を提供し、ソフトウェアが追加の3D風景を現実的な方法で画像に投影できるようにします。 このプロセスの基本的な手順は次のとおりです。
- カメラへのアクセス:ネイティブデスクトップアプリケーションを処理する場合、オペレーティングシステムはデバイスのハードウェアの多くへのI/Oアクセスを提供します。 Webアプリケーションを扱う場合も同じではありません。 ブラウザは、ネットからダウンロードされたJavaScriptコードの「サンドボックス」のようなものとして構築されており、元々はWebサイトがほとんどのデバイスハードウェアと対話できるようにすることを目的としていませんでした。 WebRTCは、HTML5のメディアキャプチャ機能を使用してこの障壁を打ち破り、ブラウザが特にデバイスカメラとそのストリームにアクセスできるようにします。
- ビデオストリームを分析する:ビデオストリームがあります…今何ですか? 各フレームを分析してマーカーを検出し、再構築された3Dワールドでのマーカーの位置を計算する必要があります。 この複雑なタスクはNyARToolkitの仕事です。
- ビデオの拡張:最後に、合成3Dオブジェクトが追加された元のビデオを表示します。 WebGLを使用して、最終的な拡張シーンをWebページに描画します。
GWTでHTML5のAPIを活用する
WebGLやWebRTCなどのJavaScriptAPIを使用すると、ブラウザーとユーザーの間で予期しない異常な対話が可能になります。
たとえば、WebGLはハードウェアアクセラレーションによるグラフィックスを可能にし、型付き配列仕様の助けを借りて、JavaScriptエンジンがほぼネイティブのパフォーマンスで数値計算を実行できるようにします。 同様に、WebRTCを使用すると、ブラウザーはコンピューターハードウェアから直接ビデオ(およびその他のデータ)ストリームにアクセスできます。
WebGLとWebRTCはどちらもJavaScriptライブラリであり、Webブラウザに組み込む必要があります。 最新のHTML5ブラウザーのほとんどは、両方のAPIを少なくとも部分的にサポートしています(こことここで確認できます)。 しかし、Javaで書かれたGWTでこれらのツールをどのように活用できるでしょうか。 前回の投稿で説明したように、GWTの相互運用性レイヤーであるJsInterop(GWT 2.8で正式にリリースされたもの)は、これを簡単なものにします。
JsInteropをGWT2.8で使用するのは、コンパイラーの引数として-generateJsInteropExports
を追加するのと同じくらい簡単です。 使用可能なアノテーションは、gwt gwt-user.jar
にバンドルされているパッケージjsinterop.annotations
で定義されています。
WebRTC
例として、最小限のコーディング作業で、GWTを使用してChromeでWebRTCのgetUserMedia
を使用すると、次のように書くのと同じくらい簡単になります。
Navigator.webkitGetUserMedia( configs, stream -> video.setSrc( URL.createObjectURL(stream) ), e -> Window.alert("Error: " + e) );
クラスNavigator
は次のように定義できます。
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="navigator") final static class Navigator { public static native void webkitGetUserMedia( Configs configs, SuccessCallback success, ErrorCallback error); }
興味深いのは、インターフェースSuccessCallback
とErrorCallback
の定義です。どちらも上記のラムダ式によって実装され、 @JsFunction
アノテーションによってJavaで定義されています。
@JsFunction public interface SuccessCallback { public void onMediaSuccess(MediaStream stream); } @JsFunction public interface ErrorCallback { public void onError(DomException error); }
最後に、クラスURL
の定義はNavigator
の定義とほぼ同じであり、同様に、 Configs
クラスは次のように定義できます。
@JsType(namespace = JsPackage.GLOBAL, isNative = true, name="Object") public static class Configs { @JsProperty public native void setVideo(boolean getVideo); }
これらすべての機能の実際の実装は、ブラウザのJavaScriptエンジンで行われます。
上記のコードはGitHubのここにあります。
この例では、わかりやすくするために、廃止されたnavigator.getUserMedia()
APIが使用されています。これは、Chromeの現在の安定したリリースでポリフィルなしで機能する唯一のAPIであるためです。 本番アプリでは、adapter.jsを使用して、新しいnavigator.mediaDevices.getUserMedia()
APIを介して、すべてのブラウザーで均一にストリームにアクセスできますが、これは現在の説明の範囲を超えています。
WebGL
GWTからWebGLを使用することは、WebRTCを使用することと大差ありませんが、OpenGL標準の本質的な複雑さのために、少し面倒です。
ここでのアプローチは、前のセクションで行ったアプローチを反映しています。 ラッピングの結果は、 Picshareで使用されているGWT WebGL実装で確認できます。これは、ここにあります。GWTによって生成された結果の例は、ここにあります。
WebGLを単独で有効にしても、実際には3Dグラフィックス機能は提供されません。 グレッグタバレスが書いているように:
多くの人が知らないのは、WebGLが実際には2D APIであり、3DAPIではないということです。
3D演算は、他のコードで実行し、WebGL用に2D画像に変換する必要があります。 3DWebGLグラフィックス用の優れたGWTライブラリがいくつかあります。 私のお気に入りは視差ですが、 Picshareの最初のバージョンでは、より「日曜大工」のパスをたどり、単純な3Dメッシュをレンダリングするための小さなライブラリを作成しました。 ライブラリを使用すると、遠近法カメラを定義し、オブジェクトのシーンを管理できます。 こちらからお気軽にチェックしてください。
GWTを使用したサードパーティのJavaライブラリのコンパイル
NyARToolkitは、拡張現実アプリケーションを構築するためのソフトウェアライブラリであるARToolKitの純粋なJavaポートです。 このポートは、Nyatlaの日本の開発者によって作成されました。 元のARToolKitとNyatlaバージョンは、元の移植以降多少異なっていますが、NyARToolkitは引き続き積極的に維持され、改善されています。
マーカーベースのARは専門分野であり、ここで明らかなように、コンピュータービジョン、デジタル画像処理、および数学の能力が必要です。
ARToolKitのドキュメントから転載。
ARToolKitのドキュメントから転載。
ツールキットで使用されるすべてのアルゴリズムは文書化されており、よく理解されていますが、最初から書き直すのは長くてエラーが発生しやすいプロセスであるため、ARToolKitなどの既存の実績のあるツールキットを使用することをお勧めします。 残念ながら、Webをターゲットにする場合、そのようなものはありません。 最も強力で高度なツールキットには、主にHTMLドキュメントとデータの操作に使用される言語であるJavaScriptが実装されていません。 これは、GWTがその非常に優れた強みを証明する場所であり、NyARToolkitをJavaScriptに単純にトランスパイルし、ほとんど手間をかけずにWebアプリケーションで使用できるようにします。
GWTを使用したコンパイル
GWTプロジェクトは本質的にJavaプロジェクトであるため、NyARToolkitを使用することは、ソースパスにソースファイルをインポートすることだけです。 ただし、GWTコードからJavaScriptへの変換はソースコードレベルで行われるため、コンパイルされたクラスを含むJARだけでなく、NyARToolkitのソースが必要であることに注意してください。
Picshareが使用するライブラリはここにあります。 これは、ここにアーカイブされたNyARToolkitビルドのlib/src
およびlib/src.markersystem
内にあるパッケージにのみ依存しています。 これらのパッケージをコピーしてGWTプロジェクトにインポートする必要があります。
これらのサードパーティパッケージを独自の実装とは別に保持する必要がありますが、NyARToolkitの「GWT化」を進めるには、ソースを探す場所をGWTコンパイラに通知するXML構成ファイルを提供する必要があります。 パッケージjp.nyatla.nyartoolkit
に、ファイルNyARToolkit.gwt.xml
を追加します。
<module> <source path="core" /> <source path="detector" /> <source path="nyidmarker" /> <source path="processor" /> <source path="psarplaycard" /> <source path="markersystem" /> </module>
ここで、メインパッケージcom.jooink.gwt.nyartoolkit
で、メイン構成ファイルGWT_NyARToolKit.gwt.xml
を作成し、XMLファイルから継承してクラスパスにNyatlaのソースを含めるようにコンパイラーに指示します。

<inherits name='jp.nyatla.nyartoolkit.NyARToolkit'/>
実はとても簡単です。 ほとんどの場合、これですべてですが、残念ながらまだ完了していません。 この段階でスーパー開発モードを使用してコンパイルまたは実行しようとすると、非常に驚くべきことに、次のようなエラーが発生します。
No source code is available for type java.io.InputStream; did you forget to inherit a required module?
この理由は、NyARToolkit(つまり、Javaプロジェクト用のJavaライブラリ)が、GWTのエミュレートされたJREでサポートされていないJREのクラスを使用するためです。 これについては、前回の投稿で簡単に説明しました。
この場合、問題はInputStream
および関連するIOクラスにあります。 たまたま、これらのクラスのほとんどを使用する必要はありませんが、コンパイラーにいくつかの実装を提供する必要があります。 NyARToolkitソースからこれらの参照を手動で削除することに多大な時間を費やすことができますが、それはおかしなことになります。 GWTは、より良いソリューションを提供します。 <super-source>
XMLタグを介して、サポートされていないクラスの独自の実装を提供します。
<super-source>
公式ドキュメントに記載されているように:
<super-source>
タグは、コンパイラにソースパスを再ルート化するように指示します。 これは、GWTプロジェクトに既存のJava APIを再利用したいが、元のソースが利用できないか、翻訳できない場合に役立ちます。 これの一般的な理由は、GWTによって実装されていないJREの一部をエミュレートすることです。
したがって、 <super-source>
はまさに私たちが必要としているものです。
GWTプロジェクトにjre
ディレクトリを作成できます。ここに、問題の原因となっているクラスの実装を配置できます。
java.io.FileInputStream java.io.InputStream java.io.InputStreamReader java.io.StreamTokenizer java.lang.reflect.Array java.nio.ByteBuffer java.nio.ByteOrder
java.lang.reflect.Array
を除いて、これらはすべて実際には使用されていないため、ダムの実装のみが必要です。 たとえば、 FileInputStream
は次のように読み取ります。
package java.io; import java.io.InputStream; import com.google.gwt.user.client.Window; public class FileInputStream extends InputStream { public FileInputStream(String filename) { Window.alert("WARNING, FileInputStream created with filename: " + filename ); } @Override public int read() { return 0; } }
コンストラクターのWindow.alert
ステートメントは、開発中に役立ちます。 クラスをコンパイルできる必要がありますが、実際に使用しないようにしたいので、クラスが誤って使用された場合に警告が表示されます。
java.lang.reflect.Array
は実際に必要なコードによって使用されるため、完全にダムではない実装が必要です。 これは私たちのコードです:
package java.lang.reflect; import jp.nyatla.nyartoolkit.core.labeling.rlelabeling.NyARRleLabelFragmentInfo; import jp.nyatla.nyartoolkit.markersystem.utils.SquareStack; import com.google.gwt.user.client.Window; public class Array { public static <T> Object newInstance(Class<T> c, int n) { if( NyARRleLabelFragmentInfo.class.equals(c)) return new NyARRleLabelFragmentInfo[n]; else if(SquareStack.Item.class.equals(c)) return new SquareStack.Item[n]; else Window.alert("Creating array of size " + n + " of " + c.toString()); return null; } }
これで、 GWT_NyARToolkit.gwt.xml
モジュールファイルに<super-source path="jre"/>
を配置すると、プロジェクトでNyARToolkitを安全にコンパイルして使用できるようになります。
GWTですべてを接着する
今、私たちは次のような立場にあります。
- WebRTCは、Webカメラからストリームを取得して
<video>
タグで表示できるテクノロジーです。 - WebGL、HTML
<canvas>
でハードウェアアクセラレーションされたグラフィックスを操作できるテクノロジー。 - NyARToolkitは、画像を(ピクセルの配列として)取得し、マーカーを検索し、見つかった場合は、3D空間でのマーカーの位置を完全に定義する変換行列を提供できるJavaライブラリです。
現在の課題は、これらすべてのテクノロジーを統合することです。
これを実現する方法については詳しく説明しませんが、基本的な考え方は、ビデオ画像をシーンの背景(上の画像の「遠い」平面に適用されたテクスチャ)として使用し、3Dデータ構造を構築することです。 NyARToolkitの結果を使用して、この画像を宇宙に投影できるようにします。
この構造により、マーカー認識のためにNyARToolkitのライブラリと対話し、カメラのシーンの上に3Dモデルを描画するための適切な構造が得られます。
カメラストリームを使用可能にするのは少し注意が必要です。 ビデオデータは、 <video>
要素にのみ描画できます。 HTML5の<video>
要素は不透明であり、画像データを直接抽出することはできません。そのため、ビデオを中間の<canvas>
にコピーし、画像データを抽出して、ピクセルの配列に変換し、最後に変換する必要があります。それをNyARToolkitのSensor.update()
メソッドにプッシュします。 次に、NyARToolkitは、画像内のマーカーを識別し、3D空間内のその位置に対応する変換行列を返す作業を実行できます。
これらの要素を使用すると、ライブビデオストリームのマーカーの真上に3Dで合成オブジェクトを配置できます。 GWTの高性能のおかげで、計算リソースが豊富にあるため、WebGLシーンの背景として使用する前に、セピアやぼかしなどのビデオ効果をキャンバスに適用することもできます。
次の要約コードは、プロセスのコアを説明しています。
// given a <canvas> drawing context with appropriate width and height // and a <video> where the mediastream is drawn ... // for each video frame // draw the video frame on the canvas ctx.drawImage(video, 0, 0, w, h); // extract image data from the canvas ImageData capt = ctx.getImageData(0, 0, w, h); // convert the image data in a format acceptable by NyARToolkit ImageDataRaster input = new ImageDataRaster(capt); // push the image in to a NyARSensor sensor.update(input); // update the NyARMarkerSystem with the sensor nyar.update(sensor); // the NyARMarkerSystem contains information about the marker patterns and is able to detect them. // After the call to update, all the markers are detected and we can get information for each // marker that was found. if( nyar.isExistMarker( marker_id ) ) { NyARDoubleMatrix44 m = nyar.getMarkerMatrix(marker_id); // m is now the matrix representing the pose (position and orientation) of // the marker in the scene, so we can use it to superimpose an object of // our choice ... } ...
この手法を使用すると、次のような結果を生成できます。
これは、 Picshareを作成するために使用したプロセスであり、マーカーを印刷したり、モバイルに表示したり、ブラウザーでマーカーベースのARを試してみたりすることができます。 楽しみ!
最後に
Picshareは、Jooinkの私たちにとって長期的なペットプロジェクトです。 最初の実装は数年前にさかのぼりますが、それでも印象的な速さでした。 このリンクでは、2012年にコンパイルされ、触れられたことのない以前の実験の1つを見ることができます。 サンプルには<video>
が1つしかないことに注意してください。 他の2つのウィンドウは、処理の結果を表示する<canvas>
要素です。
GWTは2012年でも十分に強力でした。GWT2.8のリリースにより、JsInteropとの相互運用性レイヤーが大幅に改善され、パフォーマンスがさらに向上しました。 また、多くの人を祝うために、はるかに優れた開発およびデバッグ環境であるスーパー開発モードも取得しました。 そうそう、Java8がサポートしています。
GWT 3.0を楽しみにしています!