ぬるぽを見かけたら 全力でぶっ叩くのみ


by Denullpo Smasher Hammerson
カレンダー
S M T W T F S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

2D,3Dプログラミング向け我流数学 其之弐

[過去記事]
2D,3Dプログラミング向け我流数学 其之壹

ベクトルとマトリクスの乗算はベクトルをどちら側に置くかでマトリクスの意味合いが
変わってくるが、プログラミングにおいてはそれだけではなく、さらに重要な違いが
顕れる。それはハードウェアの性質に起因する要素であり、どう扱うかで計算効率が
大きく変わる。3DCGみたいに大量のベクトル演算を要する場合、これらを考慮して
設計しなければならない。

法則1: データをメモリ上に置くときは、何次元のデータだろうと必ず1次元に展開される
例えば、2次元であるマトリクスをメモリ上に置くときは通常、行単位でまとめられ、
それを1次元に並べ直した状態となる。
a0101404_0415245.gif

はメモリ上に
xx xy xz xw yx yy yz yw zx zy zz zw wx wy wz ww
と配置される。

一方、ベクトルはどっちでも内部構造的には変わらない。数学的には表記が変わって
くるものだが、1次元に展開しちゃえば(X,Y,Z,W)が X Y Z W の順に連続して置かれる
のは一緒。




法則2: 連続したメモリ領域は、ある程度同時に読み書きできる
コンピュータがメモリを読み書きする際は仕様による違いこそあれ、まとめて読み書き
するときは常に連続したメモリ領域という点はほぼ共通。
で、今時のコンピュータはSSEなどといったベクトルプロセッサを持っていて、データの
読み書きや演算がベクトル単位で行えるようになっている。SSEの場合は32bitの
小数データを4つ同時に扱える。
つまり、上記のマトリクスデータは行ベクトル毎に扱うことになる。

ここで、1つの問題点が生じる。列ベクトルを扱うときはどうするのかというと、
マトリクスの中からスカラ値を個別に拾ってきてベクトルデータのコピーを生成する
という動作が発生する。書き込むときも、スカラ値を個別に書き込まなければならない。
そんなわけで、列ベクトルへのアクセスが頻繁なほど処理効率が落ちるということが
判ると思うですよ。まあ、ベクトルプロセッサなんか未来永劫使わないという前提なら
常に個別だからどっちでもいーけど。
(キャッシュ効率とか考えたら連続している方がいいということもあるが)

法則3: ベクトル同士の演算は、一度に行える
SSEを使った演算の場合、2つのベクトル(X,Y,Z,W) (x,y,z,w)を加算するときの
X+x Y+y Z+z W+w という4つの加算動作を一発で行うことができる。
乗算についても一緒。ベクトルやマトリクスの演算は通常、この2つの
組み合わせで行われる。
要するに、ベクトル同士で演算が行えるような配置が最適といえるのだが、
実際どちらが最適なのだろうかというと、ケースバイケースなところがあったり。
例えば、ベクトルに回転成分と平行移動成分を適用させるマトリクスは、こんな感じ。
回転成分xyは入力xが出力yに与える影響、tx,ty,tzは平行移動成分と意味を統一した。
a0101404_050457.gif
a0101404_0511512.gif

一見するとベクトルを右に持ってきた方が効率的に見える。
メモリ配置という観点ならば、右の方は一番下の行ベクトルが省略できて効率いい。
が、演算という観点ではちょっと微妙。

・左側の場合
result=(xx,xy,xz,0)*X+(yx,yy,yz,0)*Y+(zx,zy,zz,0)*Z+(tx,ty,tz,1)

・右側の場合
result=((xx,yx,zx,tx)·(X,Y,Z,1), (xy,yy,zy,ty)·(X,Y,Z,1), (xz,yz,zz,tz)·(X,Y,Z,1), 1)

…とまあ、左の方が効率的に見えてしまう。
ただし、実際はベクトルプロセッサの機能により変わり得る。
ベクトルとスカラの乗算ができないなら、左側の手数が増える。
積和演算ができるなら、左側の手数が減る。
内積演算ができるなら、右側の手数が減る。
SSEなら左側、DirectX頂点シェーダなら右側かな?
つまり、シェーダを使ったプログラミングの場合、通常は左側方式で処理を行い、
シェーダに送り込むときだけマトリクスを転置して右側方式に切り替えるのがよさげ。

(おそらく続く)
[PR]
by denullpo | 2008-04-08 00:58 | こっち関係