TOTAL:256, TODAY:256

GLSLシェーダによる簡単な描画プログラム

やっとホームページのタイトル通り、GLSLを使用したサンプルプログラムをアップすることができました。最初、何を紹介しようかと迷ったのですが、GLSLそのものよりも、GLSLを読み込みんでコンパイルする方法にしました。現在、OpenGL2.1以降でオフラインコンパイラも検討されていますが、通常、実行時にシェーダをコンパイルするからです。

GLSLのロードとコンパイル

シェーダプログラムにはバーテックスシェーダ(頂点シェーダともいいます)とフラグメントシェーダ(ピクセルシェーダともいいます)の2種類があり、二つセットで使用します。簡単な流れとしては、二つのシェーダプログラム(ソース)をそれぞれ読み込み、コンパイルし、それらをリンクして、プログラムオブジェクトを作成します。言葉で説明するよりも、プログラムを見た方が早いと思います。太字の部分がgl関数です。

int FUTL_LoadShader(
    char *vtxShdName,    /* バーテックスシェーダファイル */
    char *frgShdName,    /* フラグメントシェーダファイル */
    GLuint *lpProg
)
{
    GLuint vtxShader;
    GLuint frgShader;
    GLuint prog;
    GLint linked;

    /* シェーダオブジェクトの作成 */
    vtxShader = glCreateShader(GL_VERTEX_SHADER);
    frgShader = glCreateShader(GL_FRAGMENT_SHADER);

    /* バーテックスシェーダのロードとコンパイル */
    if (loadShader(vtxShader, vtxShdName) < 0)
        return -1;

    /* フラグメントシェーダのロードとコンパイル */
    if (loadShader(frgShader, frgShdName) < 0)
        return -1;

    /* プログラムオブジェクトの作成 */
    prog = glCreateProgram();

    /* シェーダオブジェクトのシェーダプログラムへの登録 */
    glAttachShader(prog, vtxShader);
    glAttachShader(prog, frgShader);

    /* シェーダオブジェクトの削除 */
    glDeleteShader(vtxShader);
    glDeleteShader(frgShader);

    /* シェーダプログラムのリンク */
    glLinkProgram(prog);
    glGetProgramiv(prog, GL_LINK_STATUS, &linked);
    printProgramInfoLog(prog);    /* リンクログの出力 */
    if (linked == GL_FALSE)
        return -1;

    *lpProg = prog;

    return 0;
}

/* シェーダープログラムをロードし、コンパイル */
static int loadShader(
    GLuint shader, 
    char *name
)
{
    :   : 途中略 :   :

    /* シェーダオブジェクトにソースプログラムをセット */
    glShaderSource(shader, 1, (GLchar **)&buf, &size);
  
    /* シェーダのコンパイル */
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    printShaderInfoLog(shader);     /* コンパイルログの出力 */
    if (compiled == GL_FALSE)
        return -1;

    return 0;
}

英語になりますが、各関数の詳しい説明は、OpenGL2.1のマニュアルを見てください。上記のように、コンパイルやリンクをするので、エラーログを取得する関数もあります。例えば、コンパイルログは次のようにして取得します。

/* シェーダコンパイルエラーの出力 */
static void printShaderInfoLog(
    GLuint shader
)
{
    int logSize;
    int length;

    /* ログの長さは、最後のNULL文字も含む */
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &logSize);

    if (logSize > 1)
    {
        glGetShaderInfoLog(shader, MAX_SHADER_LOG_SIZE, 
            &length, s_logBuffer);
        fprintf(stderr, "Shader Info Log\n%s\n", s_logBuffer);
    }
}

GLSLシェーダプログラム

簡単なシェーダプログラムとして、正規化された法線(x, y, z)をRGBカラーに変換して描画するシェーダを作成しました。法線は-1.0~1.0の範囲であるため、(r, g, b) = 0.5 *(x, y, z) + 0.5として計算します。バーテックスシェーダは、次のようになります。

void main(void)
{
    // 位置座標を座標変換
    gl_Position = ftransform();
        
    // 法線から色を決定
    gl_FrontColor.rgb = 0.5 * gl_Normal.xyz + 0.5;
    gl_FrontColor.a = 1.0;
}

GLSLでは、バーテックスシェーダの中のgl_FrontColorが、頂点色としてフラグメントシェーダのgl_Colorに渡される仕組みになっています。今回、フラグメントシェーダはその色をそのまま出力するようにしています。そのままと言っても、三角形内部のピクセルは渡された頂点色で補間されます。

void main (void)
{
    gl_FragColor = gl_Color;
}

OpenGLを利用するプログラム側は、次のglUseProgramをコールした後、頂点配列や頂点バッファを用いて、gl描画関数(glDrawArrayやglDrawElements等)を呼び出せば、シェーダに頂点や法線が渡っていきます。

    /* シェーダプログラムの適用 */
    glUseProgram(shdProg);

    /* トーラスの描画 */
    if (drawVBO)
    {
        triangles = FUTL_DrawTorusVBO(count);
        vtxStr = vtxBufStr;
    }

シェーダを使わずに、これまでの通りの固定パイプライン描画を行う場合は、glUseProgram(0)としてコールすればOKです。
これで説明は終わりです。サンプルプログラムを動かせば、次のような絵が描画されると思います。ドーナツはシェーダで、文字はこれまで通りの描画です。

偶然ですが、個人的には割と綺麗な色になって気に入っています。食べたい色ではないですけどね。

最新の7件

OpenGL

電子工作

玄箱HG

ホームページ

日記

Copyright (C) 2007 Arakin , All rights reserved.