おもちゃラボ

Unityで遊びを作ってます

【OpenGLでゲームを作る】複数のテクスチャを表示する

前回はようやく四角形のポリゴンにテクスチャを貼り付けることができました。

nn-hokuson.hatenablog.com

通常、ゲームを作るときには一枚のテクスチャを使うのではなく、複数毎のテクスチャを使います。
そこで、今回はテクスチャを2枚表示するサンプルを作ってみましょう。

複数枚のテクスチャを表示する流れ

複数枚のテクスチャを表示する流れは、テクスチャを一枚表示する流れと変わらず、表示したいテクスチャの枚数ぶん下記の処理を繰り返します。

  1. テクスチャの読み込み
  2. テクスチャをGPUにアップロード
  3. 座標情報、UV情報をシェーダに渡す
  4. 使用するテクスチャのバインド
  5. 描画する

f:id:nn_hokuson:20170308205156p:plain:w550

複数枚のテクスチャを表示するプログラム

今回のプログラムは次のようになります。シェーダの内容やテクスチャの読み込みプログラムに変更はないので、main関数のみを載せています。
ソースコード全文はGitHubをご確認ください。

ソースコード全文はこちら
multiTexture.cpp · GitHub

int main()
{
    if( !glfwInit() ){
        return -1;
    }
    
    GLFWwindow* window = glfwCreateWindow(g_width, g_height, "Simple", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }
    
    // モニタとの同期
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    
    GLuint programId = crateShader();
    
    GLuint catTexID = loadTexture("cat.raw");
    GLuint skyTexID = loadTexture("sky.raw");
    
    // ゲームループ
    while (!glfwWindowShouldClose(window)) {
        
        // 画面の初期化
        glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glClearDepth(1.0);
        
        
        const GLfloat vertex_uv[] = {
            1, 0,
            0, 0,
            0, 1,
            1, 1,
        };
        
        // 何番目のattribute変数か
        int positionLocation = glGetAttribLocation(programId, "position");
        int uvLocation = glGetAttribLocation(programId, "uv");
        int textureLocation = glGetUniformLocation(programId, "texture");
        
        // attribute属性を有効にする
        glEnableVertexAttribArray(positionLocation);
        glEnableVertexAttribArray(uvLocation);

        // uniform属性の登録
        glUniform1i(textureLocation, 0);

        // 1枚目
        {
            // 頂点データ
            float vertex_position[] = {
                0.0f, 0.5f,
                -1.0f, 0.5f,
                -1.0f, -0.5f,
                0.0f, -0.5f
            };
            
            // attribute属性を登録
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, false, 0, vertex_position);
            glVertexAttribPointer(uvLocation, 2, GL_FLOAT, false, 0, vertex_uv);
          
            // モデルの描画
            glBindTexture(GL_TEXTURE_2D, catTexID);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
        }
        
        // 2枚目
        {
            // 頂点データ
            float vertex_position[] = {
                1.0f, 0.5f,
                0.0f, 0.5f,
                0.0f, -0.5f,
                1.0f, -0.5f
            };
            
            // attribute属性を登録
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, false, 0, vertex_position);
            glVertexAttribPointer(uvLocation, 2, GL_FLOAT, false, 0, vertex_uv);
            
            // モデルの描画
            glBindTexture(GL_TEXTURE_2D, skyTexID);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
        }
              
        // バッファの入れ替え
        glfwSwapBuffers(window);
        
        // Poll for and process events
        glfwPollEvents();
    }
    
    glfwTerminate();
    
    return 0;
}

実行結果は次のようになります。

f:id:nn_hokuson:20170308203829j:plain:w350

続けて複数枚テクスチャの表示プログラムを解説していきます。
テクスチャの表示の詳細については、次の「マルチプラットフォームのためのOpenGL ES入門 基礎編」で丁寧に解説されているので参考にしてみてください。

プログラムの解説

今回は猫のテクスチャと水のテクスチャを使いたいので、loadTexture関数を使って2つのファイルを読み込んでGPUにアップロードしています。
猫と水のテクスチャは、それぞれcatTexIDとwaterTexIDという変数で管理します。

GLuint catTexID = loadTexture("cat.raw");
GLuint waterTexID = loadTexture("water.raw");

各テクスチャとテクスチャIDの対応は次のようになります。

f:id:nn_hokuson:20170308212457p:plain:w300

シェーダとの変数のやりとり部分は前回と変わりありません。glGetAttribLocation関数を使ってattribute変数の位置を教えてもらい、glEnableVertexAttribArrayで指定したattribute変数を有効化しています。Uniform変数も同様の手順で有効化しています。

続いて1枚目のテクスチャを描画する部分です。最初にポリゴンの頂点座標を指定しています。ここでは画面左半分にポリゴンを表示するように、座標を指定しています。

// 頂点データ
float vertex_position[] = {
     0.0f, 0.5f,
     -1.0f, 0.5f,
     -1.0f, -0.5f,
     0.0f, -0.5f
};

続いてglVertexAttribPointer関数を使って座標情報とテクスチャのUV情報をシェーダに送ります。UV情報は1枚目のテクスチャも2枚目のテクスチャも変わらないので共用で使用しています。

// attribute属性を登録
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, false, 0, vertex_position);
glVertexAttribPointer(uvLocation, 2, GL_FLOAT, false, 0, vertex_uv);

最後にテクスチャを描画します。まずは描画したいテクスチャをバインドします。
描画したいテクスチャはテクスチャIDで指定します。ここでは猫のテクスチャを描画したいのでglBindTexture関数にcatTexIDを渡しています。

f:id:nn_hokuson:20170308212728p:plain:w300

最後にglDrawArraysでGPUに描画命令を出しています。

// モデルの描画
glBindTexture(GL_TEXTURE_2D, catTexID);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

今の手順と全く同様の手順で2枚目のテクスチャも描画します。
異なっているのは、画面右半分に四角形のポリゴンを描くように座標設定していることと、glBindTextureでバインドするテクスチャIDをwaterTexIDを指定している部分です。これにより、画面右半分には水のテクスチャが表示されます。

f:id:nn_hokuson:20170308212900p:plain:w300

// 頂点データ
float vertex_position[] = {
    1.0f, 0.5f,
    0.0f, 0.5f,
    0.0f, -0.5f,
    1.0f, -0.5f
};
            
// attribute属性を登録
glVertexAttribPointer(positionLocation, 2, GL_FLOAT, false, 0, vertex_position);
glVertexAttribPointer(uvLocation, 2, GL_FLOAT, false, 0, vertex_uv);
            
// モデルの描画
glBindTexture(GL_TEXTURE_2D, waterTexID);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

まとめ

今回は複数枚のテクスチャ表示を行いました。
これでようやくゲームが作れそう・・・と思いきや、まだ行列の話をしていませんでした。
次回は行列を使ったアスペクト比の修正を紹介します。