テクスチャマッピングをやっていて、ポインタについておさらいする羽目になりました。
テクスチャに用いる画像データはテクスチャバッファに格納します。
テクスチャバッファはピクセル単位で縦と横の二次元配列で表現しています。
そして、1画素(ピクセル)がR,G,B,αの4Byteデータで表されるので、テクスチャバッファは
テクスチャバッファはピクセル単位で縦と横の二次元配列で表現しています。
そして、1画素(ピクセル)がR,G,B,αの4Byteデータで表されるので、テクスチャバッファは
unsigned char texBuf1[横の画素数][縦の画素数][4];
で定義します。
※テクスチャバッファは横と縦の比が同じでかつ2のべき乗になっていなければなりません。
※テクスチャバッファは横と縦の比が同じでかつ2のべき乗になっていなければなりません。
配列を使う場合は宣言時に配列サイズを決めておかなければならないのはC言語のルールです。
しかし、サイズを予め決めておくとアプリケーションとして自由度が低下する恐れがあります。
なので、そのような場合、
実行中にサイズを計算して必要な配列のメモリを随時確保(メモリの動的確保)する手が考えられます。
しかし、サイズを予め決めておくとアプリケーションとして自由度が低下する恐れがあります。
なので、そのような場合、
実行中にサイズを計算して必要な配列のメモリを随時確保(メモリの動的確保)する手が考えられます。
しかし相手は多次元配列(三次元配列)です。これを動的に確保しようとするとかなりややこしくなります。
- 配列へのポインタ
- そしてその配列へのポインタを要素に持つ配列へのポインタ
- さらにその配列ポインタをを要素に持つ配列へのポインタ
で表現します。
unsigned char*** texBuf2; // 三次元配列を表す。
//メモリ確保の仕方 texBuf2 = (unsigned char***)malloc(横の画素数); for(i = 0; i < 横の画素数; i++) { texBuf2[i] = (unsigned char**)malloc(縦の画素数); for(j = 0; j < maxY; j++) { texBuf2[i][j] = (unsigned char*)malloc(4); } }
こうやってメモリを確保することで、
texBuf1もtexBuf2も同様にtexBuf○[10][10][2]というように要素にアクセスすることができます。
texBuf1もtexBuf2も同様にtexBuf○[10][10][2]というように要素にアクセスすることができます。
しかーし、両者は同様に要素の参照が可能で、見かけは似ていますがコンパイラは両者を完全に区別しています。
前者は確かにそのまま多次元配列と呼ばれるのですが、後者はディスプレイと呼ばれるらしいです。
前者は確かにそのまま多次元配列と呼ばれるのですが、後者はディスプレイと呼ばれるらしいです。
同違うのかっ。これが難しいんです。
詳しい説明は長くなるので、一番顕著な違いを(理由もなしに)言うと、
1次元配列で表せるか否かです。
texBuf1は1次元配列で表せれますがtextBuf2は表せれません。
この違いの影響がもろに出るのは関数の引数です。
OpenGLでテクスチャを当てはめる関数はglTexImage2D()という関数で、引数にテクスチャバッファを指定します。
どんな型で受け取るかというとGLVoid*型で受け取ります。
要は配列へのポインタを受け取りますという意味なんですが、
これだけでは中でどのように要素にアクセスしているのかわかりません。
けど一つ言えることは、どのように要素にアクセスしてもいいような配列のポインタを渡せということです。
(配列は色々アクセスの仕方があるのです)
どんな型で受け取るかというとGLVoid*型で受け取ります。
要は配列へのポインタを受け取りますという意味なんですが、
これだけでは中でどのように要素にアクセスしているのかわかりません。
けど一つ言えることは、どのように要素にアクセスしてもいいような配列のポインタを渡せということです。
(配列は色々アクセスの仕方があるのです)
で、つまりのところtextBuf2はアクセスの仕方によって正しい要素の値を取得できないので、
引数に与えることはナンセンスです。
引数に与えることはナンセンスです。
先にも言ったように1次元配列で表せれないからってことに関わってくるんですが、
それはなぜかというと、参照の仕方が違うからです。
例えば[]による参照ではなく、ポインタで参照していくと、
texBuf1の場合、*(texBuf1++)するごとにR→G→B→α→Rと連続して値を取得していけます。
しかし、texBuf2の場合、横成分、縦成分、RGBA成分を表す配列がばらばらにメモリ空間に存在します。
つまり、*(texBuf2++)をして取得できるのは横の一列のRの値だけです。
それはなぜかというと、参照の仕方が違うからです。
例えば[]による参照ではなく、ポインタで参照していくと、
texBuf1の場合、*(texBuf1++)するごとにR→G→B→α→Rと連続して値を取得していけます。
しかし、texBuf2の場合、横成分、縦成分、RGBA成分を表す配列がばらばらにメモリ空間に存在します。
つまり、*(texBuf2++)をして取得できるのは横の一列のRの値だけです。
うーん、難しいです。要はC言語で多次元配列の動的確保は極力避けるべきだということです。
なのでどーするかというと、
なのでどーするかというと、
unsigned char* texBuf3 = (unsigned char*)malloc(横の画素数 * 縦の画素数 * 4);
というように1次元配列で確保する必要があったわけです、はい。
とまぁ、改めてポインタの奥深さを知った一日でした。
- 「ディスプレイ」って言葉ははじめて聞きました。なるほど。 -- ぷん (2007-04-02 02:54:52)
- ぷんこなら常識、ぜたいはまんねー、とオモタ -- NZ-000 (2007-04-02 12:34:05)
- へーOpenGLなんてやってんのね。winじゃもうDirectXしか聞かないけど。 -- qutto (2007-04-05 00:32:34)
- キャストの型が合ってなくない? -- qutto (2007-04-05 00:32:44)
- まじ? -- NZ-000 (2007-04-05 00:48:56)
- 修正した。 -- NZ-000 (2007-04-07 03:04:46)