macOSでOpenGLプログラミング（2-5. 複数テクスチャを組み合わせる）

[macOSでOpenGLプログラミングの目次に戻る](http://qiita.com/sazameki/items/d060f20f621c658046d9)

# はじめに

[前回](http://qiita.com/sazameki/items/857720471e1be85210f2)は、1枚のテクスチャを読み込んで表示するプログラムを書きました。

今回は、2枚のテクスチャを同時に使用して、時間によってどちらのテクスチャを大きく表示するかを切り替えながらアニメーションさせるようにしたいと思います。


# 1. ShaderProgramクラスを改良する

まずはShaderProgramクラスに、float型のuniform変数の値をセットできるように`SetUniform()`関数のオーバーロード関数を追加します。また、前回作成した`SetUniform()`関数と同じことをしている部分が極力少なくなるように、uniform変数の場所を取得するための`GetUniformLocation()`関数を別途追加します。

```cpp:Shader.hpp（一部）
class ShaderProgram
{
    /* 一部省略 */

public:
    GLint   GetUniformLocation(const std::string& name);
    void    SetUniform(const std::string& name, float value);
    void    SetUniform(const std::string& name, int value);
    void    Use();
};
```

これらの関数を実装するコードをShaderProgram.cppに書きます。ほとんどの行数はuniform変数の場所を問い合わせるためのコードなので、`GetUniformLocation()`関数を用意したことで、2つの`SetUniform()`関数の実装は2行ずつで済むようになりました。

```cpp:Shader.cpp（一部）
GLint ShaderProgram::GetUniformLocation(const std::string& name)
{
    GLint location;
    if (uniformLocationMap.find(name) == uniformLocationMap.end()) {
        location = glGetUniformLocation(program, name.c_str());
        if (location < 0) {
            throw GameError("ShaderProgram::SetUniform() Cannot locate a uniform value: %s", name.c_str());
        }
        uniformLocationMap[name] = location;
    } else {
        location = uniformLocationMap[name];
    }
    return location;
}

void ShaderProgram::SetUniform(const std::string& name, float value)
{
    GLint location = GetUniformLocation(name);
    glUniform1f(location, value);
}

void ShaderProgram::SetUniform(const std::string& name, int value)
{
    GLint location = GetUniformLocation(name);
    glUniform1i(location, value);
}
```

# 2. Gameクラスに変数を追加する

Game.hppを編集して、新しいテクスチャ変数`tex2`と、時間経過とともに値を変化させる変数`t`を追加します。

```cpp:Game.hpp（一部）
class Game
{
    /* ここまで省略 */
    Texture     *tex;
    Texture     *tex2;
    float       t;

    /* 以下、省略 */
};
```

Game.cppを編集して、コンストラクタで新しいテクスチャをnewするコードと、そのテクスチャを1番目のテクスチャバインドするコードを追加します（前回は0番目のテクスチャをバインドして使っていました）。また、変数`t`を0.0fで初期化します。

```cpp:Game.cpp（コンストラクタ）
Game::Game()
{
    /* 前半省略 */

    tex = new Texture("photo.jpg");
    tex2 = new Texture("photo2.jpg");
    glActiveTexture(GL_TEXTURE0);
    tex->Bind();
    glActiveTexture(GL_TEXTURE1);
    tex2->Bind();
    program->Use();
    program->SetUniform("tex", 0);
    program->SetUniform("tex2", 1);

    t = 0.5f;

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}
```

利用できるテクスチャの数は、2個だけではなく、GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, ...と定数が用意されていますので、3個でも4個でも同時に利用できます。

Gameクラスのデストラクタには、追加したテクスチャを削除するためのコードを忘れずに書き加えておきます。

```cpp:Gameクラスのデストラクタ
Game::~Game()
{
    delete program;
    delete tex;
    delete tex2;
    ...
}
```

続いて、`Game::Render()`関数の実装です。キーボードの左右の矢印キーで、変数`t`の値を0.0から1.0まで変化させます。

```cpp:Game.cpp（Render()関数）
void Game::Render()
{
    if (Input::GetKey(KeyCode::LeftArrow)) {
        t -= 0.5f * Time::deltaTime;
    }
    if (Input::GetKey(KeyCode::RightArrow)) {
        t += 0.5f * Time::deltaTime;
    }
    if (t < 0.0f) {
        t = 0.0f;
    } else if (t > 1.0f) {
        t = 1.0f;
    }

    program->Use();
    program->SetUniform("t", t);

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void *)0);
}
```

テクスチャの番号を示すsampler2D型の変数2つと異なり、変数`t`の値はずっと変化し続けますので、Render()関数の先頭で毎フレームセットし直します。


# 3. フラグメント・シェーダを書き換える

今回は頂点シェーダの変更は必要ありませんので、フラグメント・シェーダのみを、次のように書き換えます。

```glsl:myshader.fsh
#version 410

in vec4 color;
in vec2 uv;
uniform sampler2D tex;
uniform sampler2D tex2;
uniform float t;
layout (location=0) out vec4 frag_color;

void main()
{
    vec4 c1 = texture(tex, uv);
    vec4 c2 = texture(tex2, uv);
    frag_color = (uv.x < t? c1: c2);
}
```

sampler2D型のuniform変数`tex`を1つ増やし、float型の値を受け取るためのuniform変数`t`も追加しました。

そして`tex`変数を使用して0番目のテクスチャ画像から、`tex2`変数を使用して1番目のテクスチャ画像から、UV座標に対応した色の値を取得して`c1`と`c2`に格納します。最後に3項演算子を使用して、UV座標のX座標が0.0〜1.0の`t`の値よりも小さければ`c1`の値を、そうでなければ`c2`の値を使用するように`frag_color`変数への代入命令を書きます。


# 4. テクスチャ画像を追加する

最後に、前回と同様にして、新しい画像をプロジェクトに追加します。「photo2.jpg」という名前の画像ファイルを追加しました。

　<img width="420" src="https://qiita-image-store.s3.amazonaws.com/0/166309/dfbb0aee-e575-3134-40b8-9765a37c6853.jpeg">

　<img width="220" src="https://qiita-image-store.s3.amazonaws.com/0/166309/f0ab1853-b23b-9153-df1e-4afe1d90cdd4.png">


# 5. まとめ

アプリケーションを実行すると、左右の矢印キーの操作に応じて、X軸方向に2枚のテクスチャ画像が大きさを変えながら表示されるようになったのが分かります。

　<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/166309/92eb86fe-3661-5a07-c874-73b6aec88540.png">

ここまでのプロジェクト：[MyGLGame_step2-5.zip](http://sazameki.jp/temp/MyGLGame_step2-5.zip)

---
次の記事：[macOSでOpenGLプログラミング（2-6. 頂点データにGLKitの構造体を使う）](https://qiita.com/sazameki/items/aa632b89c5006b158b01)