頭と尻尾はくれてやる!

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


iPhoneのMUSICでCDから入れた曲が聞けない!



↑この前こういうことがあってさ。
どこかに関連の設定があるのかと思って探すもわからず、、、結局iPhoneをMacにつないでiTunesで同期して復活。曲数の割に時間が短かったから楽曲のデータ自体はiPhoneに残っていたのかもしれない。

これ、旅先で発生してたら相当痛かったろうなあ(子供が泣き叫ぶのが容易に想像つく)。

ともかくiPhone 6に最新のOS入れててこれだから、iPhone 6ではAPPLE MUSICを使わなくなったわ。
設定アプリ内で
Apple Musicを表示
って項目をOFFにしてる。


*今朝だったか、MacのiTunesをアップデート(→12.2.1.16)したんだけど、上の不具合が発生したのはその前のバージョン時。そういや不具合発生前にMacにつないだんだよなあ、、、


Metalで鏡を表現してみる

頭と尻尾はくれてやる!/ Metalのステンシルバッファでマスクしてみる
↑ステンシルバッファを使ってマスクやってみたので次に鏡みたいな表現をやってみようとしたんだわ。
World of Tanksだと湖面に映る風景とかそういうの。WoTを最近やってなくてアプリも削除してるからスクショもないけどさ。

ところがいざ鏡を表現するって、、、どうするんだ?
普通に空間にあるオブジェクトを描くはいいとして、それが鏡の面に対象の位置に反対に描くだろ、それを鏡面でマスクするって流れなのかな、、、
ぼんやりしつつコード書き始めたんだけど、普通に表示するオブジェクトと鏡に映るオブジェクトは個別にデータ持つべき?シェーダも鏡に映る用のが必要?とか疑問が次々と。
調べても3D CGではどうするのが定石なのかなんて簡単に見つからなくてさ。そりゃそうだよね、もうこんな面倒なことするなら素直にUnityなりUnrealとかそういうゲームエンジン使う方がいいもんな。

最終的にどうなったかってのを書くと、結局シェーダもロボットの頂点などのデータも一つ。鏡に映ったオブジェクトを描くには元のデータを座標変換するための行列を準備しておけばOKというなんとも単純な話に落ち着いたわ。すごいね。



↑これ、今回のアウトプット。
Phongシェーダなので光源の位置が必要で鏡に映ったオブジェクトを表示する場合にはそれが変わるのでCPU側からは光源の位置をシェーダに渡せるようにしてる。


《 鏡に映す場合の行列 》

鏡に映す場合の変換用の行列ってどうすんのよ?って思って調べてみたんだわ。どっかにありそうなもんだよね?ところがそれが見つからなくてさ。結局自分で一つずつやったよ。面倒だけど、まあいろいろと勉強にはなったかな。

ここで必要なのが鏡面の法線ベクトルと座標。鏡面が水面とか洗面所の鏡みたいに軸に常に垂直としか使わない!って場合だと相当簡単になるけど、ここではとりあえず任意の方向を持てるってことでやってみた。

処理の流れはこんな感じで。

(1) 鏡の中心が原点になるように平行移動
(2) 鏡の法線ベクトルがy軸と同じ向きになるように回転
(3) y軸を反転
(4) 回転を逆にして戻す
(5) 平行移動を逆にして戻す

アーティスティックな(?)図解で説明するよ。

座標変換前
↑最初こんなのが、、、

座標変換途中
↑この状態ならy軸反転でいけるじゃないってこと。
あー、なんか原始的。3DCG界の偉大な先人たちはもっとうまい方法を持ってるんじゃないのって気がしないでもないんだけどねえ。

それじゃ順番に行こうかな。


(1) 鏡の中心が原点になるように平行移動
これを実現するマトリックスは :
{
    GLKMatrix4 matrix1 = GLKMatrix4Identity;
    matrix1 = GLKMatrix4Translate(matrix1, -mirror.position.x, -mirror.position.y, -mirror.position.z);
}
鏡を表示するためのmirrorオブジェクトってのがあって位置座標をpositionってプロパティで持たせてる。


(2) 鏡の法線ベクトルがy軸と同じ向きになるように回転
次のこれが、、、ちょっと面倒。
回転させるんだから二つのベクトルのなす角を求めるってあれだわ。ほら、外積を使えばなんとか求まりそうだよね。
ベクトルのなす角
これで二つのベクトルとそのなす角を得たとして、そのベクトルが一致するように回転させるには、、、って探してみつけたのがこれ。

回転行列の表現方法

ここの「任意の単位ベクトル(nx,ny,nz)まわりにθ回転する場合」っての。まさしくこれだよね。

数式

↑リンク先から拝借したまんまの画像。他のページで確認したけど誤字もないと思う。
実装するのは面倒といえば面倒だけど、難しくはないよね。


(3) y軸を反転
ロボットの頂点座標は上の二つの変換を施すとxz平面に鏡がある状態だからy軸を反転させれば鏡に映った位置を得ることができるはず、ってことでy軸の符号を逆転させる。
それを意味する行列はこれでOK :
{
    GLKMatrix4 matrix3 = GLKMatrix4Identity;
    matrix3.m11 = -1.0;
}


(4) 回転を逆にして戻す
y軸を(3)で反転させたので後は鏡が元の位置、姿勢になるように戻していく行列を求める。
回転を戻すだけだからここで使う行列は(2)の逆行列でOK。

ちなみに逆行列を得るにはGLKitの
GLKMatrix4Invert
ってのがあるのでこれで逆行列が求まる。
GLKMatrix4InvertAndTranspose
って似たようなのがあるけど、Transposeってのは転置行列(行と列を入れ替えたあれよ)のことで全然期待通りの結果を得られないので注意ね(と長時間ハマった過去の自分に言いたい)。


(5) 平行移動を逆にして戻す
これで完了。


(1)から(5)の行列5個ができたので、それを順にかけて一つの行列を求めておく。
GLKMatrix4 mirrorMatrix;
とでもしておくとして、、、
{
    mirrorMatrix = GLKMatrix4Multiply(matrix5 , matrix4);
    mirrorMatrix = GLKMatrix4Multiply(mirrorMatrix , matrix3);
    mirrorMatrix = GLKMatrix4Multiply(mirrorMatrix , matrix2);
    mirrorMatrix = GLKMatrix4Multiply(mirrorMatrix , matrix1);
}
この行列をロボットのmodel view matrixにかけてやれば鏡面に映った位置になるってすごくね?!ロボットの座標データもシェーダも同じもの使えるんだよ!そう気付いた時結構感動しちゃったよ、俺。

でもまだ半分なんだよな。


《 鏡の部分のみ表示する 》

そうなのよ、鏡に映った部分のみ表示させないとダメなのよ。まあステンシルバッファはすでにやったからなんとかなるんじゃね?ってなめてたら簡単に躓いちゃったよ。なめてちゃだめだね。
なにに引っかかったかというと、深度の扱いなんだわ。

ロボットを表示する時当然このロボットはdepth testが必要になるよね。これをやらないとロボットを構成する三角形が表示されたりされなかったりしちゃう。
同様に鏡に映るロボットも必要。
鏡部分はどう?鏡面にdepth testをやると鏡面より向こう側にあるオブジェクトが考慮外になっちゃう。depth testを無視すると鏡のオブジェクトが期待する位置に表示されないことになっちゃう。

そんなわけで、どうやるのが定石かわかんないんだけど、ステンシルバッファ書き込むための鏡面と鏡自体の二つをレンダリングすることで描いてる。
なかなか難しいもんだねえ。


  TopPage  



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