質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3598閲覧

OpenGLの"glTexImage2D"で動的確保した多次元配列を受け渡したい

Unity-chan

総合スコア20

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/01/27 10:30

現在OpenGLで,動的確保を行い画素情報を格納した多次元配列をOpenGL側の"glTexImage2D"という関数に引き渡そうとしています.

静的に多次元配列を確保していた時は問題なく引き渡すことが出来ていました.

C++

1static char pictureData[HEIGHT][WIDTH][4]; 2 -------- 3glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pictureWidth, pictureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pictureData); 4 -------

しかし,毎回画像のサイズが一定ではないことから配列を動的確保したほうがよいと思いプログラムを書き換えました.その結果画面にデータが正常に表示されなくなりました.

C++

1static GLubyte***pictureData; 2pictureData= new GLubyte**[HEIGHT]; 3for (int i = 0; i < HEIGHT; i++){ 4 pictureData[i] = new GLubyte*[WIDTH]; 5 for (int j = 0; j < WIDTH; j++){ 6 pictureData[i][j] = new GLubyte[4]; 7 } 8} 9 -------- 10glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pictureWidth, pictureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pictureData); 11 -------

glTexImage2Dの引数が下のようになっているので,1重(?)ポインタに3重ポインタを渡しているのでそれが原因かなと思っています.
GLAPI void GLAPIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);

しかし,どのように解決すればいいかよくわかりませんでした.
今まで多次元配列を静的確保した場合も,動的確保した場合も違いがないと思っていたので・・・・.

C++

1//静的確保 2static char staticArray[HEIGHT][WIDTH][4]; 3//動的確保 4static char dynamicArray; 5dynamicArray= new char **[HEIGHT]; 6for (int i = 0; i < HEIGHT; i++){ 7 dynamicArray[i] = new char*[WIDTH]; 8 for (int j = 0; j < WIDTH; j++){ 9 dynamicArray[i][j] = new char[4]; 10 } 11}

解決策やアドバイスを頂けると幸いです.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

自己解決できました.
glTexImage2Dが想定しているデータの並び方に対して正しく動的確保を行えていないことが原因でした.

次の図はglTexImage2Dにおけるデータの読み込み方を示したものです.
ここで番地「1000」は引数として渡されたポインタのことです(基本的には配列の先頭アドレスになると思います).また今回の配列のデータ型はchar,色情報はRGBとしています.
イメージ説明
glTexImage2Dは,渡された先頭アドレスから1バイト単位でデータを読んでいきます.そのデータを3つ毎にピクセル値のRGB情報として扱い,「WIDTH」ピクセル分繰り返したら次の行に移るということを,「HEIGHT」行まで行います.

次の図では静的に多次元配列を確保した場合における,「配列のインデックス番号」「メモリ上の番地」「データ」の関係を示したものです.ここで,確保した配列は"staticArray[HEIGH][WIDTH][3]"であり,先頭アドレス"&staticArray[HEIGHT][WIDTH][3]"のアドレスは"1000"とします.また,「HEIGHTはピクセルの行番号」「WIDTHはピクセルの列番号」「3はRGB」を示しています.
イメージ説明
表を見てわかるとおり,静的確保をした場合すべてのデータがメモリ上へ順番に確保されます.
したがって,staticArrayの先頭アドレスをglTexImage2Dに引き渡した場合,先頭アドレスからのデータを順番に読み込んでくれるのでOpenGL上で想定通りにデータが読み込まれます.

一方次の図に,質問のように動的確保をした多次元配列の関係を示してみます.インデックス番号等の条件は静的確保した場合と同様です.
イメージ説明
表を見ると,"dynamicArray[0][0][0]"から"dynaicArray[0][0][2]"までは連続してメモリ上に格納されているにも関わらず,"dynamicArray[0][1][0]"では全く違うところに格納されていることがわかります.
もし,同じようにdynamicArrayの先頭アドレスをglTexImage2Dに渡した場合,最初の1ピクセル目までのデータは正常に読みこみますが,その次は番地"1003"を参照してしまうため2ピクセル目以降は想定した画素情報が読み込まれません(もちろんすべてのデータが順番にメモリ上へ格納されていれば問題ないですが…).

上記のことを踏まえると,動的確保する際にすべてのデータが順番にメモリ上へ格納されるように定義すればよいということがわかります.そうすれば,その先頭アドレスをglTexImage2Dに渡すだけであとはOpenGLが処理を行ってくれます.

C++

1// 必要なメモリ確保及び,各先頭アドレスの定義 2dynamicArray= new GLubyte**[HEIGHT]; 3dynamicArray[0] = new GLubyte*[HEIGHT * WIDTH]; 4dynamicArray[0][0] = new GLubyte[HEIGHT * WIDTH * 3]; 5 6// 先頭アドレスからの相対位置にすることで順番にメモリ上へ並ぶようにする 7for (int i = 0; i < HEIGHT; i++){ 8 dynamicArray[i] = dynamicArray[0] + (i * WIDTH); 9 for (int j = 0; j < WIDTH; j++){ 10 dynamicArray[i][j] = dynamicArray[0][0] + (i * CAMERA_WIDTH * 3) + (j * 3); 11 } 12} 13 14// 値の書き込みの例(緑一色) 15for (int i = 0; i < CAMERA_HEIGHT; i++){ 16 for (int j = 0; j < CAMERA_WIDTH; j++){ 17 dynamicArray[i][j][0] = 0; 18 dynamicArray[i][j][1] = 255; 19 dynamicArray[i][j][2] = 0; 20 } 21} 22 23// glTexImage2Dへの引き渡し("**dynamicArray"よりは"&dynamicArray[0][0][0]"のほうがぱっと見で分かりやすいかもしれない) 24glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, **dynamicArray);

投稿2017/01/28 08:42

Unity-chan

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

渡しているメモリの構造が異なりますよ。

static char pictureData[HEIGHT][WIDTH][4];

これは、連続した領域にHEIGHTWIDTH4バイトのメモリを確保します。
pictureDataはそのHEIGHTWIDTH4バイトのメモリの先頭アドレスを指します。

static GLubyte***pictureData;

pictureData= new GLubyte**[HEIGHT];

この場合、pictureDataは(GLubyte*)型を指すポインタHEIGHT個の配列を指しています。
32ビット・ビルドならポインタは4バイトなので、4*HEIGHTバイトの連続したメモリです。
メモリのサイズが異なりますね。

下記のようなイメージで獲得し、アクセスすれば良いですよ。

C++

1char* pictureData=new char[HEIGHT*WIDTH*4]; 2h, wピクセルのcチャネルは、pictureData[((h*WIDTH)+w)*4+c]

折角のC++なので、classを作ってメモリ獲得/解放やピクセル・アクセスを任せてしまうと良いですよ。
手抜きしまくりですが、下記のようなイメージです。

C++

1struct PictureData 2{ 3 size_t mHeight; 4 size_t mWidth; 5 char* mData; 6public: 7 PictureData(size_t iHeight, size_t iWidth) : 8 mHeight(iHeight), mWidth(iWidth), mData(new char[iHeight*iWidth*4]()) 9 { } 10 ~PictureData() 11 { 12 delete[] mData; 13 } 14 char& at(size_t h, size_t w, size_t c) 15 { 16 return mData[((h*mWidth)+w)*4+c]; 17 } 18};

at()は参照を返しているので、下記のような操作もできます。

PictureData aPictureData(200, 100);
aPictureData.at(0, 0, 2)=100;

glTexImage2Dへ渡す時は、&aPictureData.at(0, 0, 0)でmDataの先頭アドレスが渡ります。

投稿2017/01/27 12:10

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問