読者です 読者をやめる 読者になる 読者になる

音ゲーの出来ない雑魚のブログ

LWJGLとかのことをいうブログ

「moreover lwjgl」 9 現代的なテクスチャ表示(1) ~stbを使って~

「現代的(Modern)」というのは「OpenGL3.2以降の機能を使った」ということです。それは、OpenGL3.2以降からは固定機能を廃止して、プログラマーが自分でシェーダなどを用意しなければいけなくなったからです。*1

ということで、現代的なやり方?に必要なのが…

  • VEO(頂点エレメントオブジェクト)… 頂点を、指標を使って描画する方法。VAOをより効率よくした感じ
  • シェーダ … この前の通り

  • サンプラーオブジェクトとテクスチャオブジェクト … テクスチャを、テクスチャ画像自体と、それを描画するときの処理に分ける

こんな感じでしょうか?

また今回、LWJGL3に乗り込んできた、STB(シンプルなファイル(画像など)読み込みライブラリー)を使って画像を読み込むことにします。自作のTextureManagerクラスよりも数百倍簡単に読み込めます。


STB
github.com


とりあえず、ソースコードを…

このコードは、OpenGL3.3以降に対応,または、GL_ARB_sampler_objectsに対応していないと動きません

Test6_LoadTextureUsingSTB.java


V1.vert + F1.frag


解説

テクスチャの設定やシェーダの読み込みはすでに解説したので今回は解説しません。

44,50,54行目 : 頂点の座標、テクスチャ座標、番号の配列を用意します。

60行目 : 頂点ごとの属性の番号を入れる物を用意します。

179,180行目 : プログラム中のシェーダの中から、指定の頂点属性を探しだして、番号を代入します。

int glGetAttribLocation(int program,CharSequence name)

183行目 : プログラム中のシェーダの中から、指定のユニフォームを探しだして、値を代入します。

void glUniform1i(int location,int value)
int glGetUniformLocation(int program,CharSequence name)

ユニフォームや、ここでの処理の説明はシェーダの時にします。

214~217行目 : テクスチャ座標の配列をバッファーとして、VAOで使えるようにしています。

頂点座標のもそうですが、glVertexAttribPointerの第一引数は、頂点属性の番号にします。つまり、シェーダの中で操作する属性の番号と同じにして、シェーダ内の操作をそのままVAOで表示するようにします。

220~222行目 : 頂点の指標を設定しています。指標は、頂点描画時の表示順番の番号を指し示すものです。

GL_ELEMENT_ARRAY_BUFFER
頂点配列の指標のバッファーであることを示します。これに指定されたバッファーは、glDrawElementsの時の表示番号(指標)の時に使われます。

230~234行目 : テクスチャ画像に必要な、入れ物(ByteBuffer)と、幅、高さ、画像の構成成分を受け取る入れ物(IntBuffer)を用意します。

構成成分とは、その画像が、赤青緑成分だけなのか(R)、透明成分も合わせた赤緑青成分なのか(RGBA)、白黒なのか(Grey)などのことです。


compの値意味
1白黒(grey)
2透明有りの白黒(grey_alpha)
3赤青緑(rgb)
4透明有りの赤緑青(rgb_alpha)

236行目 : STBImageクラスのloadメソッドを使って、テクスチャ画像を読み込み、ByteBufferとして返します。

ByteBuffer stbi_load( . . . )

この時、幅、高さ、構成成分を受け取ることが出来ます。

第4引数のreq_compで構成成分をどのように描画するのかを要求(request)できます。ただし、RGBAの画像をGreyで表示しようとしても、何も映らなかったり、RGBAをRGBで移そうとすると、画像が斜めになってしまったりするので、実際の画像と同じ成分にした方がいいです。
指定には、STB_[grey/grey_alpha/rgb/rgb_alpha]と、STB_default(標準、画像の成分と同じになる)が使用できます。基本的に、defaultでいいと思います。

237~240行目 : 画像が読み込めなかったら、エラーメッセージを出して終了します。STBImageでは、読み込めなかった時は読み込み先をnullまたは壊れた状態にするそうです。

252行目 : 読み込んだ画像をメモリから消します。

void stbi_image_free(ByteBuffer img)

255行目 :
サンプラーオブジェクトを作成します。
int glGenSamplers()

サンプラーオブジェクトはテクスチャを描画時(拡大/縮小時、縦横の繰り返し)にどのように処理するかを個々に保持しておけるオブジェクトです。使うときにバインドすれば使えます。元々は、テクスチャオブジェクトに付いていたのですが、「シェーダによって処理を変えたい」と言ったことが出来るように、テクスチャオブジェクトと分けられました。これは、ドライバーがOpenGL3.3か"GL_ARB_sampler_objects"に対応していないと、使えません。その時は、テクスチャオブジェクトで設定しましょう。

256~259行目 : サンプラーオブジェクトの設定を指定します。テクスチャオブジェクトの時と同じです。

void glSamplerParameteri( . . . )

265行目 : テクスチャユニット"0"を有効にします。テクスチャユニットの説明も後で…

void glActiveTexture(int texture)

267行目 : サンプラーオブジェクトをバインドします。

void glBindSampler(int unitnumber,int sampler)

272行目 : 頂点の指標バッファー(Index Buffer)を有効にします。

274行目 : 指標にそって、頂点を属性を合わせて描画します。

void glDrawElements( . . . )

頂点の指標
指標を使えば、コードを簡単にすることが出来ます。例えば、次のような物体を描画したいときは…
f:id:cakkby:20160417174752p:plain
glDrawArraysでは、v6など何回も使う頂点を何回も処理しなければならず面倒ですが、glDrawElementsの場合は、指標(index)を使えば、頂点座標は一回書くだけで済み、とても楽です。

また、モデリングソフトを使うときには、glDrawElementsのように指標を使うものが多いので、それらを読み込むときにとても楽に読み込めます。

頂点座標と指標をまとめてVEO,EBOと略すことも有ります。

276~283行目 : バインドを解除したり、属性の使用を不可にしたりして、初期状態に戻しておきます。





シェーダの中身の解説などは、次回(2)にします。

*1:コアプロファイルの場合です。互換性ブロファイルだったらOpenGLのどのバージョンでも使えます。