2015年5月22日金曜日

Bufferに注意!

プログラムのことを探すとみんな「バッファーが・・・」とか「このBuffer」にはとかよく使うけど、
そもそもBufferってなんだ?
なんとなく読んでいて「あーメモリーのことを言っているんだね」とか
「何か保存する場所なんだ」って感じがしますね。

バッファーを辞書で探してみると(英語)
a person or thing that prevents incompatible or antagonistic people or things from coming into contact with or harming each other.
・・・・まったくわかりませんね。

日本語辞書でみると
1(鉄道車両などの)緩衝器[装置].
2緩衝物.
3緩衝国.
4【電子計算機】 バッファー.
・・・・これも意味わかりませんね。

まぁー特に大した意味もないので、何となくわかればいいと思います。
みんなそういう意味で使ってるんだって感じで。

で、「注意」とタイトルに書いたんですが、いったいどういうことなのかみてみましょう。
ここではバッファーを「メモリ領域」の意味で使おうと思います。
CとC++にはいろんなメモリコピーの関数(機能)があります。
memcpyとかstrcpyとか・・・それらを使う上で、バグったりしたことはありませんか?
プログラムが止まったり、落ちたり、変な動きをしたり、いろいろとあると思います。
それについて考えてみましょう。

多くのバグはまさにバッファー管理の誤りで起きます。例えば、初期化がされなかったり、保存できる領域を超えたりすることが代表的なものですが、この保存できるところを超えるとどうなるかを詳しくみてみましょう。

バッファーって保存する領域なのにこの領域を超えて保存しようとしたらどうなるのか?
どう思いますか?

実はOS側でこれは「ルール違反」だとみなして、プログラムを強制的に止めます。

たまにプログラムを書いて実行してみたら、すごい警告ポップアップが出て「デバッグ」「再実行」のボタンがあるのをみたことありますか?
この警告ですが、大体メモリルール違反で出る警告です。

じゃーなんでこれをルール違反だとみなすか?

例えば、メモリーにこんな感じでプログラムがあるとします。
Aプログラム「1|2|3|1|3|2」Bプログラム「3|1|2|1|2|3」
Aプログラムの一番後ろのマスから3マスデータを書き込むとします。

3|1|2とか書いたらどうなりますかね?

Aプログラムと全然関係のないBプログラムのデータが書き換えられますね?
それではBプログラムがバグったり、落ちたりする問題が起こります。
これじゃ、Bプログラムを作った人があまりにも可哀そうじゃないですか。
それで、こういうことを禁止にしたんです。
この問題はプログラム同士でも変数同士でも起きます。

理由もなく変数の値が変わったりしたら、この問題の可能性が高いです。

で、なんでこんなことが起きるのか?
変数にちゃんと代入したらいいだろうが!って思いますよねw
実は初心者のプログラムではあんまりこの問題が起きません。
でも初心者から抜け中級者になるころぐらいにこの問題が多く起きます。

例えば、

int a[20];
for(int i = 0; i <= 20; i++){
a[i] = 0;
}

このようなことが多くないでしょうか。で、一生懸命探して直すのに他でバグったりしますね。
実はこのようなバグとかエラーを直すことはすごくいい経験です。
まだ、勉強するときにできるだけ多くのエラー、バグ、クラッシュ、警告に合ってくださいw

変に聞こえるかもしれませんが、これもまた経験です。
プログラムは「かけた時間」ですべてが決まります。
かけた時間つまり多くのプログラムを組んでみると多くのエラーとバグと遭遇するでしょう。
それらを直してみることであなたは確実にレベルアップできます。

実際、会社では一人ですべてのプログラムを書く場合は多くありません。
他人が書いたものを使う場合も多いでしょう。あなたのプログラうムが完璧だとしても、ほかの人のプログラムが問題を起こす場合も多くあります。
その時、まさにこのような経験が力を発揮します。
覚えてください。バグ、エラー等を直すことは絶対無駄にはなりません。

まぁー雑談交じりの内容だったのですが、すごく重要な部分です。次はメモリーについてもっと詳しく書いてみようと思います。

2015年5月20日水曜日

3D変換について

要点だけ抑えてみましょう。3D レンダリング(描画)とは3D っぽくみせることです。
例えば前後に動いている板があなたの前にあるとしましょう。

あなたはどう奥行き感じますか?

奥に行くと小さくなるように見えて、手前に来ると大きくなるように見えるでしょう。
パソコンの中の3D 表現とはまさにそういうことです。

とあるオブジェクトがカメラの前に近づくと大きくみせ、遠ざかると小さくみせるだけです。

え?じゃーどうやって?
この二つの絵をみてみましょう。実はすごくシンプルな考え方なんです。


 



真ん中のモンスターの大きさを変えるだけで、手前に来てるような感じがしますね。
(もちろん比較対象が少なすぎて遠近感があんまり感じられないんですけどw)
で?具体的にどんな変換があったのかみましょうか?

このキャラクターの3Dでの左上座標をみてみましょう。

左はXYZ -0.5, 0.5, 1.0
右はXYZ -0.5, 0.5, 5.0

Z座標が変わっていますね。
でも、これだけじゃよく仕組みがわかりませんね。では四角で囲ってみましょう。
で、それの画面上での左上の点の座標を比較してみましょう。

左のほうはX=243.00 Y=163.00で
右のほうはX=294.00 Y=214.00です。

どうでしょうか?なんかわかりますか?実は3D 描画というのは
「最終的に画面のどの位置にどのサイズで描画するか」を演算する処理を言います。
われわれが見ているパソコンのディスプレイは平面ですね。そこに奥行などありません。
ただ平いだけです。

つまり
3D座標→あらゆる変換用の数学式にかける→画面の2D座標
というシンプルな(変換用の数学式を誘導するのはかなり複雑です)ものです。
Openl GLes系はこの機能がついてませんが、DirectXは親切にすべての機能が揃えてあります。
これをネットで調べてみるとものすごい難しく説明します。(あらゆる数学の式で表現してあるので見るだけで嫌になりますw)

実は3D表現をするだけならその式を全部知る必要はありません。
最近のライブラリは優秀なので、あらかじめデバイスの中に
その機能を備えてあるものがほとんどです。

もちろん、高度なことをやろうとすれば、全部わかる必要はありますが、
「んなことじゃなくてとりあえずやってみたいんだ」って方は
今使っているデバイスでのやり方だけを調べてその関数を使うだけでOKだと思います。

例えば、DirectX9では

D3DXMATRIX World;
D3DXMATRIX Offset;
D3DXMATRIX View;
D3DXMATRIX Persp;
D3DXMatrixIdentity( &World ); //とりあえず、World行列をリセット

D3DXMatrixTranslation( &Offset, 描画したい位置);
D3DXMatrixMultiply( &World, &World, &Offset ); //描画したい位置をセット
D3DXMatrixLookAtLH(
                  &View,
                  &D3DXVECTOR3(カメラ位置),
                  &D3DXVECTOR3(0, 0, 0),
                  &D3DXVECTOR3(カメラが見ている方向)
                  );//カメラをセット
D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(視野角), 画面の横サイズ/画面の縦サイズ, 1.0f, 10000.0f);//視野角をセット

//描画デバイスにオブジェクトの描画位置を教える
描画デバイス->SetTransform(D3DTS_WORLD, &World);
//カメラ情報を教える。
描画デバイス->SetTransform(D3DTS_VIEW, &View);
//視野角情報を教える
描画デバイス->SetTransform(D3DTS_PROJECTION, &Persp);

描画式

上のやつで描画されます。Open GL自体にはD3DXMatrixTranslationとか
D3DXMatrixMultiplyとかD3DXMatrixLookAtLHとかD3DXMatrixPerspectiveFovLHとか
こういう便利な関数がないのですが、使っているデバイス(例えばiOS, Android)の
デバイスライブラリでこの機能があると思いますので、似たような名前か似たような
機能がありましたらそれを上のものに置き換えたら3D描画ができます。
SetTransformとかはデバイス依存の関数なのでこれも変えないといけませんねw