頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


Metalで描画したものを後で動画として表示する


↑衝突後に衝突の様子を動画として中央に表示させてるんだけど、ここではどうやってるかのお話。
こういうのをGIF動画にしてツイートってよくあるけど、ここではそういうのは無しでとりあえず動画を表示するってことで。

上の動画で言うと左目用、右目用とさらに別視点での録画用のrenderPassを用意してやって録画用はテクスチャに保存するように設定。

そのテクスチャを id<MTLTexture> *texture に対して

- (void)getBytes:(void *)pixelBytes bytesPerRow:(NSUInteger)bytesPerRow fromRegion:(MTLRegion)region mipmapLevel:(NSUInteger)mipmapLevel


ってメソッドでデータの保存ができる。

保存先はあらかじめ必要な枚数分メモリを確保しておく。
ちなみに上の動画はデータのサイズが横172x縦129、20FPSで表示してるんだけど、まあこれくらいならいいかなって感じ。

{
    MTLRegion region = MTLRegionMake2D(0, 0, ImageWidth, ImageHeight);//172x129
    
    int bytesPerPixel = 4;
    int bytesPerRow = bytesPerPixel * ImageWidth;

    UInt8 *recordingBuffer = (UInt8 *)&imageBuffer[unitSize * imageIndex];

    [texture getBytes:recordingBuffer bytesPerRow:bytesPerRow fromRegion:region mipmapLevel:0];
}
ここでunitSizeは画像1枚あたりのバイト数で、imageIndexにより保存先をどんどん変えていくようになってる。もちろん無限に保存するわけにもいかないのでimageIndexが決められた最大数を超えるとゼロになって最初に戻り、古いのに上書きしていく。
MTLTextureの内容を取得する型はUInt8でOK。

以上の処理が毎フレームで行われて、いざ表示となるとその時点で全ての保存してるデータを再度 id<MTLTexture> のテクスチャに戻してる。保存データをUIImageにするコードはこんな感じ。この後UIImageオブジェクトからMTLTextureにしてる。

+(UIImage *)makeImageFromUInt8Data:(UInt8 *)buffer size:(CGSize)size
{
    size_t width = size.width;
    size_t height = size.height;
    size_t bitsPerComponent = 8;
    size_t bitsPerPixel = 32;
    size_t bytesPerRow = 4*width;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
    bool shouldInterpolate = 1;
    CGColorRenderingIntent intent = 0;
    
    CFDataRef data = CFDataCreate(NULL, buffer, width*height*4);
    CGDataProviderRef   dataProvider = CGDataProviderCreateWithCFData(data);
    
    CGImageRef cgimage = CGImageCreate(
                            width, height,
                            bitsPerComponent, bitsPerPixel, bytesPerRow,
                            colorSpace, bitmapInfo, dataProvider,
                            NULL, shouldInterpolate, intent);
    
    UIImage *image = [UIImage imageWithCGImage:cgimage];

    CGImageRelease(cgimage);
    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    CFRelease(data);
    
    return image;
}



ベクトル二つでカメラ姿勢を決めるのは不十分

スクショ1

↑こういう感じのVRアプリを今作ってるんだけど、当然FPSみたいに自分の目から見たような画像が表示されてる。このゲーム、障害物との衝突でGame Overになるんだけど、その時の様子を理解するために、自分から少し離れたところから自分を見た画像が欲しいと思ったのよ。
ロボット(自分)から離れたところにカメラがあって、ロボットの方をカメラが向くわけだけど、この時のカメラの姿勢はどうやって求める?ってお話。

カメラの姿勢はクォータニオンでもマトリックスでもいいんだけどクォータニオンでいうなら GLKQuaternionIdentity での姿勢でカメラが (0,0,-1)方向を向いてるとして、ロボットが(xr,yr,zr)って方向にあるとして、この二つのベクトルからクォータニオンを求めたらいいんじゃね?そういや、前にやったよな。ってことで自分専用フレームワークのコードを使って画面右側に別視点からの画像を表示させてみたら、、、

スクショ2

↑そうだよな、そうなるよな。傾くかもしれないよな(右の画像ね)。参考までにコードのベースはこちら↓
math - Finding quaternion representing the rotation from one vector to another - Stack Overflow

カメラの向きをちゃんと指定しないとダメだよな、そういやそういうのGLKitにあったんじゃね?
と思って調べたらあったよ!
GLKMatrix4MakeLookAt
ですわ。かすかに覚えていた自分を褒めてあげたい。

これでカメラの上方向を指定して、、、

{
        GLKMatrix4 matrix = GLKMatrix4MakeLookAt(
                                            camera.x, camera.y, camera.z, //eye
                                            robot.x, robot.y, robot.z, // lookAt
                                            0.0, 1.0, 0.0);
}
てな感じでマトリックスを得るとOK。

スクショ3

↑空と陸の境目が平らになったよね。

動画だとこんな感じ↓。



ひとまずめでたし、めでたし。


三角形の内外判定

iPhoneでVRっぽいアプリを作ろうとしてるんだけど、VRゴーグルにiPhoneを入れちゃうので画面をタップできない。でもボタンの機能が欲しかったので、画面中央に十字を表示させてそれがボタンの枠内に入っているのを検出しようとしたのよ。
ボタンのエリア自体は長方形としても、それが画面に投影された時には任意の四角形になってるだろうからそれを三角形二つと考えて、ある点が三角形の内側にあるか?って判定をすればいいことになる。

こういうのを「内外判定」って言うそうな。

点と三角形の当たり判定( 内外判定 )
外積
参考にしたのは↑これらのページなど。
内外判定してくれる関数とか元々iOSのフレームワークにあるんじゃないのと思ったけど、調べても見つからないし、こうやって計算できるよなんてページが出てくる(下のリンクとか)。
algorithm - How to determine if a point is in a 2D triangle? - Stack Overflow

ボタンの四隅の(世界座標系での)座標を変換して行って、xy平面の画面(xyとも-1から1)に投影した時の座標を得ることができれば後は三角形の内外判定を2回するだけ。
実際には外積を計算する必要はなく、外積によるベクトルの向きが揃うかが問題なので、そのz成分を計算するだけでOK。

こんな感じに。左側に画面中央を示す十字マークがある(ちょっと薄くて見づらいな、要修正)。
スタートボタン上に十字マークが乗ったのを検出してる。




そう言えば、iPhoneのVRアプリで射撃するようなゲームがあってそれも画面中央を示すマークが(左右どちらかは忘れたけど)片側の画面にしかなかったんよ。
マークも左右両方にある方がいいやん、と思い最初左右それぞれの画面中央に表示させたんだけど、そうすると十字マークは無限遠に位置することになって遠くを見てる時はいいんだけど、近くを見ると十字マークにピントが合わない状態になりうっとおしい!ということになった。射撃ゲームで片側しかマークがなかったのはそういうことだったのかとこの時に気付いたわ。


休暇村南淡路へキャンプに行ってきた

夏休みってことで今年のキャンプ第二弾。家族三人。

【キャンプ場についてのメモ】

行き先は淡路島の南の方にある休暇村。

キャンプ|休暇村南淡路

休暇村のホテルっぽい宿泊施設は高台にあって車で1〜2分かかるので、海のすぐ側にあるキャンプのエリアから歩いて行けなくもない距離だけど上り下りがしんどそう。夕食の後に温泉に行くような場合は車がオススメ。


休暇村南淡路のキャンプ場

↑キャンプ場の場所はチェックインの早いもの順らしく、うちらが行った2時すぎでは残り3つほどしか空いてなかった(画像は公式サイトより)。人気は木陰があるところなのかな?


各サイトの炊事場

↑このキャンプ場、各サイトに水と電気がある(電気は別途有料)。お隣さんは明々とライトを使ってたし、そのお隣は扇風機を使ってた。キャンプ初の前回では水が近くになく、それがあたりまえと思ってたけど、野菜切ったり、歯を磨いたりするのがテントのすぐ近くでできるって楽なんだなってことを知ってしまったよ。


テントサイト

↑テントを設置するところは土。細い金属のペグで固定。ただ、工事中のエリアのところは綺麗な芝生になってた。


釣り情報

↑キャンプ場は海のすぐ側で釣り糸を垂れてる人が多かったんだわ。公式サイトにはレンタルもやってるとあるけど、いざキャンプ場でレンタルしようとしたら「今はやってない」とのこと。竿、餌、仕掛けなどの販売はしてた。そう書き直しといてくれたら竿の一本でも持って行ったのに。


共有炊事棟のかまど

↑共有炊事棟のかまどで薪を使って米とカレーを作る。こういうのは流行らないのかかまどを使ったのはウチらだけだった。まあ確かに面倒だわな。


キャンプ場のトイレは和式、洋式あり、ウォッシュレットなし。


だだっ広い芝生のエリアがあるので何か遊べるものを持参すれば楽しめる。ウチらはフリスビーを楽しんだよ。ここで花火もしたし。


夕食後に車で休暇村の本館(?)の温泉へ。キャンプに泊まる人は別途有料。
夜8時から海ほたるの観察会に参加。海へ行くんじゃなくて、休暇村で飼ってるのが光るのを見せてくれれる。無料。大人も子供も楽しめる。
引き続き、星を見る「スターウォッチング」というのにも参加。これも無料。
この日は天気も良く、でっかい天体望遠鏡で土星、小さめ(と言っても個人じゃ買わないような大きなもの)の望遠鏡で火星を観察。
最後まで残ってた人はおまけでベガ(織姫)をでっかい望遠鏡で。点じゃなくてダイヤモンドっぽく見えるのがわかった。
人数が多かったので待ち時間が長かったけど待ってる間も空の星を眺めて流れ星や人工衛星だのを見て楽しめる。


福良湾から出て行く漁船

↑早朝港から出て行く漁船の音が結構近くで聞こえる。うるさいと思う人もいるかも。


阿万海岸海水浴場|休暇村南淡路
↑チェックアウト後に近くの阿万海岸海水浴場へ。決して遠浅な砂浜ではないので注意。
日差しがきつくて、砂浜も熱い状態なのに、なぜか海水が冷たい。冷たい!という声があちこちから聞こえてくる。海に入りたくないのに息子が遊びたがるから付き合いで入ってたけど、これ、もう拷問か修行してるみたいな感じだったわ。


南あわじリフレッシュ交流ハウスゆーぷる - 南あわじ市ホームページ
↑甘海水浴場から近く、休暇村からも近いところにあるゆーぷるって温泉施設に。
日焼けのせいで最初は温泉に浸かってられなかったんだけど、温泉の効能のせいか(?)いつの間にか入れるように。偶数日と奇数日で男湯、女湯が入れ替わるらしいけど、うちらが行った時の男湯には露天風呂のところにすべり台があって小学生男子どもが嬉々としてすべっていた。



【私的メモ】

テントの設営、撤収は2回目なので問題なし(と言っても説明書見ながらやったけど)。

薪の火で調理中

↑夕食は定番(らしい)のカレーに初挑戦。先日サビサビのダッチオーブンをもらったので使ってみようということもあって。
ダッチオーブンでカレーなんて作るもんなのかと調べると無水カレーというレシピを見つけた。蓋の重みがあるので圧力鍋のように野菜の水だけでカレーになるそうな。
焦げることも野菜が固すぎるとかもなく、なかなか美味しくできた(完成時の写真を撮り忘れた)。

カレーなので米が必要、ということで丸型飯盒なるものを購入。説明書通りにやってみたんだが、、、
沸騰すると蓋が浮かぶので石を乗せるはずが、全然蓋が浮いてこない?
一度だけ蓋から湯気が出てるのを確認したんだが石を乗せるまでもなく静か。
いつまでこのままでいいんだろ???と悩んだ挙句、適当なタイミングで火から降ろして逆さまにしといた。
30分くらい蒸らした後で開けてみたら、、、おこげもわずかで美味しくできてた。まさにまぐれの極み。今度同じように炊けと言われてもできないわ。
ただ、炊く前に結構長時間米を水に浸しておいたのはよかったとは思う。


カセットコンロ

↑朝食用に今回はカセットコンロを持ってきた。せっかくのキャンプなのに雰囲気ないわって感じだけど、やっぱり楽ちん。
調理器具セットのフライパンを使ってハンバーグを焼いて、さらにバターたっぷり溶かして食パンを焼いた。
フライパンで食パン焼くとおいしいと聞いたのでやってみたらホント、外はカリッとしてて中は柔らかくていいんだわ、これ。
その食パンにチーズ、ハンバーグをのせて美味しくいただきました、、、と言いたいんだけど、ハンバーグも食パンも少し焦がしてしまい、息子には渋い顔をされてしまった。
屋外の明るいとこだとカセットコンロの火が見えなくて、強さがよくわからなかったせいかな。


阪神高速などルートマップ

↑阪神高速湾岸線4号から淡路島へ高速で行くのなかなか難易度高い。
Appleのマップアプリ通りに走ったんだが指示したルートは
4号湾岸線→5号湾岸線→ハーバーハイウェイ(ETC不可)→3号神戸線→第二神明→5号湾岸線(?)→垂水JCT→神戸淡路鳴門自動車道→淡路島へ
って感じ。予習してなかったので途中、下道を走るとは思ってなかったので焦ったわ。


  TopPage  



Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.