おもちゃラボ

Unityで遊びを作ってます

【OpenGLでゲームを作る】四角形のポリゴンにシェーダで色を塗る

前回は、OpenGLで四角形のポリゴンと円のポリゴンを描画する方法を紹介しました。ただ、ポリゴンの色は真っ白のままでした。

nn-hokuson.hatenablog.com

今回はそのポリゴンに色を塗ってみましょう。ポリゴンに色を塗るためにはシェーダの修正が必要になります。

色をつけるシェーダを作る

描画するポリゴンの色を決めるためには、次のような流れになります。

  1. まず頂点ごとに何色にするかをOpenGLで定義します。
  2. 定義した色情報をバーテックスシェーダに渡します。
  3. 受け取った値をそのままピクセルシェーダに渡して描画します。

f:id:nn_hokuson:20170221195845p:plain:w450

ポリゴンに色をつけるプログラムは次のようになります。

#include <GLFW/glfw3.h>
#include <iostream>
#include <fstream>

using namespace std;

const int g_width  = 640;
const int g_height = 480;

GLuint crateShader()
{
    //バーテックスシェーダのコンパイル
    GLuint vShaderId = glCreateShader(GL_VERTEX_SHADER);
    string vertexShader = R"#(
    attribute vec3 position;
    attribute vec4 color;
    varying vec4 vColor;
    void main(void){
        gl_Position = vec4(position, 1.0);
        vColor = color;
    }
    )#";
    const char* vs = vertexShader.c_str();
    glShaderSource(vShaderId, 1, &vs, NULL);
    glCompileShader(vShaderId);
    
    //フラグメントシェーダのコンパイル
    GLuint fShaderId = glCreateShader(GL_FRAGMENT_SHADER);
    string fragmentShader = R"#(
    varying vec4 vColor;
    void main(void){
        gl_FragColor = vColor;
    }
    )#";
    const char* fs = fragmentShader.c_str();
    glShaderSource(fShaderId, 1, &fs, NULL);
    glCompileShader(fShaderId);
    
    //プログラムオブジェクトの作成
    GLuint programId = glCreateProgram();
    glAttachShader(programId,vShaderId);
    glAttachShader(programId,fShaderId);
    
    // リンク
    glLinkProgram(programId);
    
    glUseProgram(programId);
    
    return programId;
}

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();
    
    // ゲームループ
    while (!glfwWindowShouldClose(window)) {
        
        // 画面の初期化
        glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glClearDepth(1.0);
        
        // 頂点データ
        float vertex_position[] = {
            0.5f, 0.5f,
            -0.5f, 0.5f,
            -0.5f, -0.5f,
            0.5f, -0.5f
        };
        
        // 色情報データ
        float vertex_color[] = {
            1.0, 0.0, 0.0, 1.0,
            0.0, 1.0, 0.0, 1.0,
            0.0, 0.0, 1.0, 1.0,
            1.0, 0.0, 1.0, 1.0,
        };
        
        // 何番目のattribute変数か
        int positionLocation = glGetAttribLocation(programId, "position");
        int colorLocation = glGetAttribLocation(programId, "color");

        // attribute属性を有効にする
        glEnableVertexAttribArray(positionLocation);
        glEnableVertexAttribArray(colorLocation);

        // attribute属性を登録
        glVertexAttribPointer(positionLocation, 2, GL_FLOAT, false, 0, vertex_position);
        glVertexAttribPointer(colorLocation, 4, GL_FLOAT, false, 0, vertex_color);
       
        // モデルの描画
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
        
        // バッファの入れ替え
        glfwSwapBuffers(window);
        
        // Poll for and process events
        glfwPollEvents();
    }
    
    glfwTerminate();
    
    return 0;
}

だんだんプログラムが複雑になってきましたが、やっていることは頂点情報をシェーダに渡した時と同様の方法で色情報をシェーダに渡しているだけです。

実行結果は次の通りです。

f:id:nn_hokuson:20170221194818p:plain:w400

ポリゴンに色をつけるシェーダの内容

まずは、バーテックスシェーダの部分から見ていきましょう。バーテックスシェーダではOpenGLから色情報を受け取るためattribute変数を定義しています。

f:id:nn_hokuson:20170221195304p:plain:w450

この色情報はバーテックスシェーダからフラグメントシェーダに渡したいので、シェーダ間の値の受け渡しのためにvarying変数を用意しています。

f:id:nn_hokuson:20170221195308p:plain:w450

バーテックスシェーダではOpenGLから受け取った色情報をvarying変数に代入して、フラグメントシェーダに渡しています。

    attribute vec3 position;
    attribute vec4 color;
    varying vec4 vColor;
    void main(void){
        gl_Position = vec4(position, 1.0);
        vColor = color;
    }


次にフラグメントシェーダの内容を見て見ましょう。フラグメントシェーダではバーテックスシェーダから渡される色情報を受け取るため、こちらでもvarying変数を宣言しています。

フラグメントシェーダの中ではバーテックスシェーダから受け取った値をgl_FragColorにそのまま代入して画面に出力しています。

    varying vec4 vColor;
    void main(void){
        gl_FragColor = vColor;
    }

シェーダに色情報を渡すプログラムを作る

最後にOpenGLからシェーダに頂点色情報を渡す部分を見ていきましょう。色情報はRGBAで0.0〜1.0で表現します。4頂点にそれぞれ異なる色を設定しています。

        float vertex_color[] = {
            1.0, 0.0, 0.0, 1.0,
            0.0, 1.0, 0.0, 1.0,
            0.0, 0.0, 1.0, 1.0,
            1.0, 0.0, 1.0, 1.0,
        };

この頂点ごとの色情報をシェーダに渡します。頂点情報を渡すプログラムと並べて書いて見ると、ほとんど同じことをしているのがわかると思います。必要なところだけを抜き出すと次のようになります。

  1. glGetAttribLocationでバーテックスシェーダで宣言されたどのattribute変数に代入するかを調べてます。
  2. glEnableVertexAttribArrayでattribute変数を有効にしています。
  3. glVertexAttribPointer関数で色情報を渡しています。第二引数が色の情報サイズである4になっていることに注意してください。
        // 何番目のattribute変数か
        int colorLocation = glGetAttribLocation(programId, "color");

        // attribute属性を有効にする
        glEnableVertexAttribArray(colorLocation);

        // attribute属性を登録
        glVertexAttribPointer(colorLocation, 4, GL_FLOAT, false, 0, vertex_color);

まとめ

ようやくポリゴンに色をつけるところまで到達しました。次はついにポリゴンにテクスチャを貼る方法を紹介します!