iPhone の GLKit で追加されたマトリクスについて書いてみようと思います。ちょっと特殊な感じがします。
さて、お題としてはある点 (1,2) について、原点を中心としてx方向に3倍、y方向に5倍。そのあとでx方向に+100、y方向に+200してみましょう。
拡大とオフセットをする変換行列を作り、これを利用して座標を移動させます。自然に書くと以下のようになると思います。(GLKVector3 でも良かったな)。
GLKVector4 vec = {1.0f, 2.0f, 0.0f, 1.0f}; GLKMatrix4 transform = GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f); transform = GLKMatrix4Translate(transform, 100.0f, 200.0f, 0.0f); vec = GLKMatrix4MultiplyVector4(transform, vec); NSLog(@"%@", NSStringFromGLKVector4(vec)); // out:{303, 1010, 0, 1}
思惑が外れ、 (+100, +200) のオフセットがされた後で座標が拡大されてしまいました。MakeTranslate → scale 変換というように書き換えると拡大→オフセットになります。
なんとなく直感的でないなあ、ということで行列の要素を調べてみました。
GLKMatrix4 scale = GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f); NSLog(@"%@", NSStringFromGLKMatrix4(scale)); // out:{ // {3, 0, 0, 0}, // {0, 5, 0, 0}, // {0, 0, 1, 0}, // {0, 0, 0, 1}}
GLKMatrix4 offset = GLKMatrix4MakeTranslation(100.0f, 200.0f, 0.0f); NSLog(@"%@", NSStringFromGLKMatrix4(offset)); // out:{ // {1, 0, 0, 0}, // {0, 1, 0, 0}, // {0, 0, 1, 0}, // {100, 200, 0, 1}}
さて、感の良い方なら4行目にオフセットの値があるのを見てあれれと思ったかもしれませんが、もう1つ。
GLKMatrix4 transform = GLKMatrix4MakeScale(3.0f, 5.0f, 1.0f); transform = GLKMatrix4Translate(transform, 100.0f, 200.0f, 0.0f); // 以上は以下と同義 transform = GLKMatrix4Multiply(scale, offset);
GLKMatrix4Translate() というのは実はただの行列の掛け算だったと。さて、この transform のマトリックスを NSLog で表示してみると。
// out:{ // {3, 0, 0, 0}, // {0, 5, 0, 0}, // {0, 0, 1, 0}, // {300, 1000, 0, 1}}
となっています。そこで各マトリックスの要素を付きあわせて考えると GLKMatrix4Multiply(scale, offset) の箇所は実際には offset * scale という順序で行列の掛け算が行われていることがわかります。scale * offset ではありません(交換法則は成り立たない)。
ちなみに GLKMatrix4MultiplyVector4(transform, vec) の箇所についても実際には vec * transform という順序の計算が行われています。
アップルさんそれはわかりづらいでしょう。と言いたいところですが、最終的に GLKVector4 型の座標を変換することなどを考えるとこちらの形(transform * vec ではなく vec * transform)のほうが処理速度の面からベター、というのをどこかで見かけました。
でもやっぱりわかりずらい。リファレンスを見てみると
GLKMatrix4 GLKMatrix4Multiply ( GLKMatrix4 matrixLeft, GLKMatrix4 matrixRight ); Parameters matrixLeft The multiplicand. // 被乗数(掛け算の前の数)の意味 matrixRight The multiplier. // 乗数(掛け算の後ろの数)の意味
と、なっています。でも実際の要素を見ると左右が逆なわけで、意味合いとしても乗数と被乗数も逆ではありませんか、という話になります。
というわけでリファレンスの Left と Right を鵜呑みにすると痛い目を見そうですね、という話でした。Multiply を使わないマトリックスの変換では、実際に変換する順序とは逆から値を設定していくようにするとうまく行きそうです。
アップルさん、箸を持つほうが右ですよ!え、何?左利き?そ、そうですか・・・。