はじめに
前回は、1枚のテクスチャを読み込んで表示するプログラムを書きました。
今回は、2枚のテクスチャを同時に使用して、時間によってどちらのテクスチャを大きく表示するかを切り替えながらアニメーションさせるようにしたいと思います。
1. ShaderProgramクラスを改良する
まずはShaderProgramクラスに、float型のuniform変数の値をセットできるようにSetUniform()
関数のオーバーロード関数を追加します。また、前回作成したSetUniform()
関数と同じことをしている部分が極力少なくなるように、uniform変数の場所を取得するためのGetUniformLocation()
関数を別途追加します。
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行ずつで済むようになりました。
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
を追加します。
class Game
{
/* ここまで省略 */
Texture *tex;
Texture *tex2;
float t;
/* 以下、省略 */
};
Game.cppを編集して、コンストラクタで新しいテクスチャをnewするコードと、そのテクスチャを1番目のテクスチャバインドするコードを追加します(前回は0番目のテクスチャをバインドして使っていました)。また、変数t
を0.0fで初期化します。
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クラスのデストラクタには、追加したテクスチャを削除するためのコードを忘れずに書き加えておきます。
Game::~Game()
{
delete program;
delete tex;
delete tex2;
...
}
続いて、Game::Render()
関数の実装です。キーボードの左右の矢印キーで、変数t
の値を0.0から1.0まで変化させます。
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. フラグメント・シェーダを書き換える
今回は頂点シェーダの変更は必要ありませんので、フラグメント・シェーダのみを、次のように書き換えます。
#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」という名前の画像ファイルを追加しました。
5. まとめ
アプリケーションを実行すると、左右の矢印キーの操作に応じて、X軸方向に2枚のテクスチャ画像が大きさを変えながら表示されるようになったのが分かります。
ここまでのプロジェクト:MyGLGame_step2-5.zip