OpenGLの使い方:テクスチャ編

# OpenGLの使い方:テクスチャ編
## 目標
テクスチャを描画に使えるまでに必要なものを取り上げます．

## 画像の読み込み
自分自身が使いやすいものを探すと良いと思いますが，OpenGLアプリケーションに組み込みやすそうなものをピックアップしてみます．

### [stb_image.c](http://nothings.org/)
いくつかの画像フォーマットに対応．パブリックドメイン．
制限が多いけど取り扱いは簡単なので，試しにどうぞ．

### [SOIL](http://www.lonesock.net/soil.html)
stb_image.cをOpenGL向けに拡張したライブラリ．パブリックドメイン．
更新は止まっているみたいです．

### [GLI](http://www.g-truc.net/project-0024.html)
C++のDDSファイルの読み書きライブラリ．MITライセンス．
OpenGL向けの機能もあり，DDSファイルが用意できれば便利に使えます．

## テクスチャオブジェクト
テクスチャオブジェクトはテクスチャの諸々を管理するOpenGLオブジェクトです．
他の例に漏れず， **glGenTextures** で生成， **glBindTexture** で *target* にバインド，**glDeleteTextures** で破棄します．

```c:オブジェクト
// 生成
GLuint texture;
glGenTextures(1, &texture);

// バインド
GLenum target;
glBindTexture(target, texture);

// バインドを解除
glBindTexture(target, 0);

// 破棄
glDeleteTextures(1, &texture);
```

以下では特に明示しない限り， *target* にバインドされたテクスチャが対象になります．

## パラメータの設定
glTexParameter系はテクスチャオブジェクトが持つパラメータを変更します．


## 領域の確保
OpenGL4.2以上か[ARB_texture_storage](http://www.opengl.org/registry/specs/ARB/texture_storage.txt)が有効であれば， **glTexStorage1D** ， **glTexStorage2D** ， **glTexStorage3D** が使えます．
glTexStorage系は *levels* の値の数だけ，そのレベル数に応じたミップマップサイズの領域を確保します．つまりレベルが1増えるごとにサイズが半分になります．ただし配列の場合，レイヤ数だけは変化しません．

```c:確保
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系と同等の動作をする擬似コードは以下の通り．

```c:確保
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* にはピクセルの型を指定します．

```c:更新
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変数に対象とするテクスチャユニット番号を設定することで，サンプラとテクスチャユニットを関連付けます．

```c:前準備
// テクスチャオブジェクト側
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](http://www.opengl.org/registry/specs/ARB/sampler_objects.txt)が有効であれば，サンプラオブジェクトが使えます．サンプラオブジェクトはサンプリングに関するパラメータを保持するOpenGLオブジェクトです．