OpenGLの使い方:テクスチャ編
目標
テクスチャを描画に使えるまでに必要なものを取り上げます.
画像の読み込み
自分自身が使いやすいものを探すと良いと思いますが,OpenGLアプリケーションに組み込みやすそうなものをピックアップしてみます.
stb_image.c
いくつかの画像フォーマットに対応.パブリックドメイン.
制限が多いけど取り扱いは簡単なので,試しにどうぞ.
SOIL
stb_image.cをOpenGL向けに拡張したライブラリ.パブリックドメイン.
更新は止まっているみたいです.
GLI
C++のDDSファイルの読み書きライブラリ.MITライセンス.
OpenGL向けの機能もあり,DDSファイルが用意できれば便利に使えます.
テクスチャオブジェクト
テクスチャオブジェクトはテクスチャの諸々を管理するOpenGLオブジェクトです.
他の例に漏れず, glGenTextures で生成, glBindTexture で target にバインド,glDeleteTextures で破棄します.
// 生成
GLuint texture;
glGenTextures(1, &texture);
// バインド
GLenum target;
glBindTexture(target, texture);
// バインドを解除
glBindTexture(target, 0);
// 破棄
glDeleteTextures(1, &texture);
以下では特に明示しない限り, target にバインドされたテクスチャが対象になります.
パラメータの設定
glTexParameter系はテクスチャオブジェクトが持つパラメータを変更します.
領域の確保
OpenGL4.2以上かARB_texture_storageが有効であれば, glTexStorage1D , glTexStorage2D , glTexStorage3D が使えます.
glTexStorage系は levels の値の数だけ,そのレベル数に応じたミップマップサイズの領域を確保します.つまりレベルが1増えるごとにサイズが半分になります.ただし配列の場合,レイヤ数だけは変化しません.
GLsizei levels;
GLenum internal_format;
GLsizei width, height, depth;
glTexStorage1D(target, levels, internal_format, width);
glTexStorage2D(target, levels, internal_format, width, height);
glTexStorage3D(target, levels, internal_format, width, height, depth);
glTexStorage系が使えない場合は, glTexImage1D , glTexImage2D , glTexImage3D で代用することができます.glTexImage系ではレベルごとに領域を確保する必要があります.また,glTexImage系では format ,type ,pixels を指定することで領域の確保と同時に領域を初期化することができます.
glTexStorage系と同等の動作をする擬似コードは以下の通り.
GLint level;
GLint internal_format;
GLsizei width, height, depth;
GLenum format = GL_RGBA;
GLenum type = GL_UNSIGNED_BYTE;
const GLvoid* pixels = NULL;
for (level = 0; level < levels; ++level) {
glTexImage1D(target, level, internal_format, width, height, 0, format, type, pixels);
glTexImage2D(target, level, internal_format, width, height, 0, format, type, pixels);
glTexImage3D(target, level, internal_format, width, height, 0, format, type, pixels);
width = max(1, width >> 1);
height = max(1, height >> 1);
depth = max(1, depth >> 1);
}
// レベルの有効範囲を指定
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1); // 最大レベル
領域の更新
glTexSubImage1D , glTexSubImage2D , glTexSubImage3D は level のレベルを指定の矩形で書き換えることができます. data には指定の大きさを持つピクセルデータを渡し, format には渡すピクセルデータのフォーマットを, type にはピクセルの型を指定します.
GLint level;
GLint xoffset, yoffset, zoffset;
GLsizei width, height, depth;
GLenum format;
Glenum type;
const GLvoid* data;
glTexSubImage1D(target, level, xoffset, width, format, type, data);
glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data);
glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data);
描画の前準備
テクスチャオブジェクトはテクスチャユニットを介してシェーダと関連付けます.
テクスチャオブジェクト側は glActiveTexture で対象とするテクスチャユニット番号を選択し,次いでテクスチャオブジェクトをバインドすることで,テクスチャオブジェクトとテクスチャユニットを関連付けます.
シェーダ側はサンプラにあたるUniform変数に対象とするテクスチャユニット番号を設定することで,サンプラとテクスチャユニットを関連付けます.
// テクスチャオブジェクト側
GLint index; // 対象とするテクスチャユニット番号
glActiveTexture(GL_TEXTURE0 + index);
glBindTexture(target, texture);
// シェーダオブジェクト側
GLuint program; // リンク済みシェーダプログラム
const GLchar* name = "sampler_name"; // サンプラ名
GLint location = glGetUniformLocation(program, name); // サンプラの位置を取得
glUniform1i(location, 0); // 0番のテクスチャユニットを見に行くように設定
メモ
ミップマップの自動生成
OpenGL3.0以上なら, glGenerateMipmap が使えます.glGenerateMipmap は GL_TEXTURE_BASE_LEVEL の領域をもとに,それ以下の領域のミップマップを生成してくれます.なお,ミップマップを生成するためには事前に領域を確保しておく必要があります.対象となる範囲は GL_TEXTURE_BASE_LEVEL から GLTEXTURE_MAX_LEVEL までのようです.
デフォルトのパラメータによる不具合
テクスチャがミップマップを持たない場合,デフォルトのパラメータでは正常に描画ができないかもしれません.そのときは GL_TEXTURE_MAX_LEVEL を0にしたり,GL_TEXTURE_MIN_FILTER をミップマップを使わないもの( GL_NEAREST や GL_LINEAR )にすると正常に描画できるかもしれません.
サンプラオブジェクト
OpenGL3.3以上かARB_sampler_objectsが有効であれば,サンプラオブジェクトが使えます.サンプラオブジェクトはサンプリングに関するパラメータを保持するOpenGLオブジェクトです.