Sunday, June 7, 2015

Android NDKでC++11のstd::chronoを使った時間計測

今まで以下のような秒単位の時間計測関数を、Android用とWindows用にそれぞれ作って使っていました。


今更ながら、C++11のstd::chronoでどちらでも動く関数を書けることに気づき、以下のように書きなおしました。



static auto startが妙ですが、こうするとGetTimeの初回呼び出し時の時間をstartに記録できます。あとは毎回のGetTimeの呼び出しでstartからの経過時間を秒単位で返します。

#include <chrono> で "fatal error: chrono: No such file or directory"というエラーが出てしまう場合、build.gradleでgnustlを使うよう設定する必要があります。build.gradleの一部を抜粋します。


"stlport_static"ではだめで、"gnustl_static"を使う必要があるようです。android-ndk-r10d\sources\cxx-stl\stlport\stlportにはchronoが入っていないのに対し、android-ndk-r10d\sources\cxx-stl\gnu-libstdc++\4.9\includeには入っていました。

Monday, June 1, 2015

Evan WallaceさんのWebGL Waterのソースを読んでみた

https://github.com/evanw/webgl-water
このレポジトリをローカルに取ってきてじっくり読んでみました。

波紋の作り方


浮動小数点数テクスチャをグリッドとして使っています。RGBAそれぞれを別の意味で使います。Rは水面の高さ、Gは水面の上下方向の加速度、BAは法線を格納するのに使います。

各グリッドのシミュレーションは以下の2つのルールで行います。

  • 上下左右の隣接4グリッドの高さの平均が自分の高さと異なる場合、周囲に高さを合わせるように加速度を調節します。
  • 各グリッドは自身の加速度によってのみ上下します。

グリッドは当然四角形ですが、上のルールだけで放射状に広がる波紋が出来るのが不思議です。

水面の法線の格納方法


シミュレーションが終わったらレンダリング前に各グリッドの法線を求め、上と同じテクスチャのBに法線のX、Aに法線のZを格納します。Yが格納されていませんが、法線は長さが常に1なので、ピタゴラスの定理と同じ原理でXとZからYを推測できます。

この方法の欠点は符号を復元できないことで、ここにXやZではなくYを省略する理由があります。WebGL WaterではY軸が上方向を向いており、常に正の方向になります。対してXとZは符号も保存する必要があります。

コースティック


水面が一切波立っていない場合と、水面が波立っている場合に分けて太陽光を水面で屈折させ、底または壁との衝突点を求めます。これを水面のグリッド全体で行い、波立っていない場合に比べて隣接グリッドの衝突点と自分の衝突点の距離が変化するかを見ます。広がった場合は光の密度が減り、狭まった場合は光の密度が増えると判断します。

密度の計算ですが、dFdx関数とdFdy関数を使うのがユニークです。つまり、各グリッドの波立っている場合と波立っていない場合の衝突点の計算を頂点シェーダで行い、それぞれvaryingでフラグメントシェーダに渡します。隣接グリッドの情報を直接読みに行くのではなくvaryingの変化量を見るだけなので追加のテクスチャフェッチが発生せず、高速に動作しそうです。

WebGLの拡張機能


浮動小数点数のテクスチャを使うため、OES_texture_float 拡張を使っています。
また、dFdx関数とdFdy関数を使うため、OES_standard_derivatives 拡張を使っています。

まとめ


簡潔で無駄の無いコードで、こんなきれいなエフェクトが作れるのはすごいですね。

Saturday, May 23, 2015

Android StudioのGUIを使わずにビルド→インストール→起動

以下のようなバッチファイルをgradlew.batと同じ位置に作っておくことで、Android Studioを起動せずともコマンドライン又はエクスプローラからビルド、インストール、起動ができるようになります。NDKを使用している場合のビルドまで問題なく出来ました。

※ パッケージ名は、com.pinotnoir.adamasを、アクティビティ名はcommon.pinotnoir.glactivity.PinotGLActivityを置き換えてください。

ところで、"ADB server didn't ACK" というエラーに悩まされていたのですが、原因はNsight Tegra Visual Studio Editionでした。これが内部でADBをつかっているため衝突するようです。Visual Stuidio起動中に上のバッチファイルが使えない場合、Visual Studioを終了すると使えるようになりました。



Nsight Tegra Visual Studio Editionを一時停止することはできないようです。Visual StudioとAndroid Studioを同時に使いつつ、Nsight Tegraも使いたい場合、面倒なことになりそうです。Nsight Tegraをアンインストールすると衝突は起こらなくなりました。

Thursday, May 14, 2015

GitHubのDirectXTKをgit submoduleとして組み込む

MicrosoftのGitHubアカウントでDirectXTKが公開されています。gitのsubmoduleとして組み込めるので格段に管理しやすくなりそうです。早速組み込んでみました。

組み込み方


git bashからDirectXTKを組み込みたいプロジェクトのディレクトリに移動し、以下のように入力すればDirectXTKフォルダ以下にDirectXTKのプロジェクトファイルが生成され、DirectXTKを参照するコミットを生成できます。

$ git submodule init
$ git submodule add https://github.com/Microsoft/DirectXTK.git DirectXTK
$ git commit -m "DirectXTK added as a submodule"

submoduleの何が良いのか


自分のレポジトリに外部のgitレポジトリの参照を加えることで、(半自動的に)一緒にチェックアウトできるようになります。自分のレポジトリを肥大化せずに外部モジュールを取り込めるわけです。また、参照にはバージョンも含まれているのでバージョンを取り違えてビルドに失敗することもありません。何より外部モジュールの組み込み方をgitが標準化したという側面が大きいです。

当然ながら、取り込みたいモジュールがgitレポジトリとして公開されているのが前提条件になるのですが、MicrosoftもGitHubでプロジェクトを公開してくれるようになりました。ありがたいですね。

submoduleは以下が詳しいです。

→ Git のさまざまなツール - サブモジュール

レポジトリについて


→ https://github.com/yorung/dx11x

D3DXやDirectX SDKを使わないモダンなDirectX11で.xファイルを読み込んで表示するプログラムです。スキニングも含めてアニメーションもひととおり対応しています。




Saturday, May 2, 2015

OpenGL ES 2.0のvec4配列でUBOもどきを作る際の注意点

OpenGL ES 3.0ではUBOやVAOのような現状のハードウェアで効率的に動作するAPIが使えるようになります。しかし、開発者はモダンなAPIへの対応作業を迫られる一方、ES 2.0もサポートし続けなければいけません。https://developer.android.com/about/dashboards/index.html にもあるように未だに65%のデバイスはOpenGL ES 2.0であり、この状況は当分続きそうです。
「じゃあES2.0だけでいいや」では時代遅れになるので、ES2.0でもモダンなフリをしながら両バージョンでコードを共有する方法を模索してみます。

複数のuniform変数を配列化してひとつに


http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf の24ページ目でuniform block(UBO)を使えない場合に、複数のuniformを配列にまとめて使う方法が提案されています。これは効率的であるのみならず、C側では構造体で転送するデータを管理できるので、UBOを使ったES 3.0のコードとのアーキテクチャの差を吸収できそうです。
https://devtalk.nvidia.com/default/topic/777618/scenix/announcing-nvpro-pipeline-a-research-rendering-pipeline/ この掲示板に動画もありますね。

mat4かvec4かfloatか


複数のuniformをまとめる際、どのような型の配列として渡すか決めなければなりません。行列だけのバッファならmat4の配列、色やベクトルだけならvec4配列にします。

mat4とvec4の混合のバッファを作りたい場合もあるかもしれません。そういうときはvec4の配列とするとよさそうです。mat4配列でもよいのですがバッファサイズがfloat16個単位になってしまうので柔軟性に欠けるからです。次のようにしてGLSL側でvec4を4つまとめてmat4を復元します。


ならば、vec4配列よりfloat配列のほうが柔軟そうですが、float4つを1単位とする癖をつけたほうが良いかもしれません。以下のポストはDirectX11のもので、NVIDIAによるものなので他のGPU全てに当てはまるかはわかりませんが、float4つ単位にしなかった場合の速度低下の可能性を示唆しています。
https://developer.nvidia.com/content/understanding-structured-buffer-performance

UBOとuniform vec4配列の違い


上のように、ES3.0のUBOのようなものをES 2.0で実現してみましたが、同一ではありません。UBOはシェーダから独立したバッファで、複数シェーダで共有可能です。uniformはシェーダの一部であってC側から書き換え可能な変数です。あるuniform vec4配列を複数のシェーダ間で共有したい場合、シェーダを切り替える度にglUniform4fvでバッファを転送してあげなければなりません。

OpenGL ES特有のprecisionの問題


ES特有のつまづきそうなポイントです。uniformはシェーダ間で共有できませんが、同じprogramに属するvertex shaderとfragment shaderに限って同じ名前のuniform変数を宣言すると共有されます。しかし、precisionが違うと共有できません(glLinkProgramでエラーになります)。vertex shaderではhighp、fragment shaderではmediump、のような使い方をすることが多いと思うので注意します。(fragment shaderでhighpがエラーになる機種もある)

Monday, April 27, 2015

언리얼엔진 4, 소스코드로부터 APK 생성 후기

무료화된 언리얼 엔진4로 APK를 만들어 보았습니다. GitHub로 제공되는데 PRIVATE리포지토리라서 언리얼 엔진 공식 사이트에서 회원등록해야 리포지토리에 접근이 됩니다.

소스코드 빌드와 에디터 실행


빌드 방법은 GitHub의 Readme.md에 다 나와 있어서, 지시대로 VisualStudio 2013로 빌드하고 실행하면 에디터가 뜹니다.


샘플 실행중의 모습.


안드로이드 바이너리 만들기


공식  https://docs.unrealengine.com/latest/KOR/Platforms/Android/GettingStarted/index.html

안드로이드 APK로 패키지합니다. ETC1텍스쳐를 지정하면 단말기를 가리지 않고 실행 가능할 겁니다...



missing UE4Game binary이란 에러가 떴습니다. 사전에 Visual Studio로 안드로이드 바이너리를 빌드하지 않으면 이런 에러가 뜹니다.
(참고: 옛날 버전 얘기입니다. 세상이 좋아져서 최근 버전은 에디터로부터 바이너리 생성까지 다 해줍니다! 따라서 아래 Visual Studio작업도 필요 없습니다.)



UE4.sln에서 Solution Configurations에 "Development", Solution Platforms에 "Android"를 지정해서 빌드하면 안드로이드 바이너리가 생성됩니다. 다시 에디터에서 패키지해 보니 APK가 생성되었습니다. 단말기에 APK를 전송해 주는 배치파일까지 만들어 주는 센스!



APK가 25MB, Obb파일이 1GB나 되네요. 깔아 보면 단말기에서는 100MB정도가 됩니다.

결론


언리얼엔진은 자료가 아주 잘 정리되어 있어서 APK까지 쉽게 만들 수가 있었습니다. 단점은 엔진 자체 크기 때문에 빌드 시간이 아주 깁니다. 소스코드 빌드 시간이 걸리는 것은 어쩔 수 없는데, 간단한 APK 생성이 30분 정도 소용이 됩니다. 언리얼 개발을 위해서는 하이엔드PC부터 맞추어야 될 것 같습니다.

그리고 어떤 단말기에서 실행이 안 되었습니다. Acer Iconia B1-730HD에서는 돌아갔는데, 팬택Vega Racer에서 안 돌아갔습니다. 하지만 TAPPY CHICKEN는 Vega Racer에서 실행되었으니 조정을 잘하면 될 수도 있겠네요...

Tuesday, April 7, 2015

OpenGL ESでのUniform Bufferのバインドの仕方(DX11比較付き)

Unifrom BufferはDirectX11のConstant Bufferとほぼ同じものです。OpenGL ES 3.1では使い方もよく似ていて、以下のようにGLSLの"binding"で指定するbinding pointがほぼHLSLの"register"と同じ概念です。



C側からバッファを"register"に指定するID3D11DeviceContext::VSSetConstantBuffers に相当するのは、glBindBufferBaseです。



OpenGL ES 3.1はここまでで動作します。

GLSLで"binding"が使えないOpenGL ES 3.0では以下のそれ相当の処理をC側から行う必要があります。これはOpenGL ES 3.1でも有効で、"binding"で指定するのと同じ結果になります。更には、既に"binding"指定があってもC側から上書きすることもできます。