GLSL

OpenGL Shading Language

GLSLプログラミング

GLSLプログラミングを行うためには実行プラットフォームが少なくとも, GL_ARB_vertex_shader,GL_ARB_fragment_shader拡張に対応している必要がある. OpenGL拡張の対応を調べたり,拡張を使えるようにするにはGLEWを用いるのが一番簡単である (参照:GLEWについて).

GLSLシェーダはいくつかのOpenGL命令を用いてアプリケーション実行時にロード,コンパイル,リンクされる.

シェーダのコンパイル

GLSLシェーダをOpenGLアプリケーション中でロード,コンパイルする手順は以下.

  1. glCreateShader()でシェーダオブジェクトを生成する.
    GLuint glCreateShader(GLenum shaderType);
    shaderTypeにはGL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADERのいずれかを指定する.
  2. シェーダオブジェクトにシェーダソースコード(文字列)をセットする.
    void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
    shaderはシェーダオブジェクト(glCreateShaderの返値),countは文字列の数,stringがソースコードを格納した文字列配列, lengthはstringの長さを示し,NULLを指定すればstringがnull terminatedであることを示す.
  3. シェーダをコンパイルする.
    void glCompileShader(GLuint shader);

これらの手順を関数にまとめたものを以下に示す.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
/*!
 * GLSLシェーダコンパイル
 * @param[in] target ターゲット(GL_VERTEX_SHADER,GL_FRAGMENT_SHADER)
 * @param[in] shader シェーダコード
 * @return GLSLオブジェクト
 */
inline GLuint CompileGLSLShader(GLenum target, const char* shader)
{
    // GLSLオブジェクト作成
    GLuint object = glCreateShader(target);
 
    if(!object) return 0;
 
    glShaderSource(object, 1, &shader, NULL);
    glCompileShader(object);
 
    // コンパイル状態の確認
    GLint compiled = 0;
    glGetShaderiv(object, GL_COMPILE_STATUS, &compiled);
 
    if(!compiled){
        char temp[256] = "";
        glGetShaderInfoLog( object, 256, NULL, temp);
        fprintf(stderr, " Compile failed:\n%s\n", temp);
 
        glDeleteShader(object);
        return 0;
    }
 
    return object;
}

CompileGLSLShader()にはソースコード文字列を引数として渡さなければならない. ファイル(*.vs,*.gs,*.fsなど)に記述したシェーダコードを読み込んで文字列化する関数の例を以下に示す.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
/*!
 * GLSLシェーダコンパイル
 * @param[in] target ターゲット(GL_VERTEX_SHADER,GL_FRAGMENT_SHADER)
 * @param[in] fn シェーダファイルパス
 * @return GLSLオブジェクト
 */
inline GLuint CompileGLSLShaderFromFile(GLenum target, const char* fn)
{
    FILE *fp;
 
    // バイナリとしてファイル読み込み
    fp = fopen(fn, "rb");
    if(fp == NULL) return 0;
 
    // ファイルの末尾に移動し現在位置(ファイルサイズ)を取得
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
 
    fseek(fp, 0, SEEK_SET);
 
    // シェーダの内容格納
    char *text = new char[size+1];
    fread(text, size, 1, fp);
    text[size] = '\0';
 
    fclose(fp);
 
    // シェーダコンパイル
    printf("Compile %s\n", fn);
    GLuint object = CompileGLSLShader(target, text);
 
    delete [] text;
 
    return object;
}

ファイルから読み込むほかに,以下のようにしてソースコード中に直接記述する方法もある.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
#define RXSTR(A) #A
 
//! Phong 頂点シェーダ
const char phong_vs[] = RXSTR(
// フラグメントシェーダに値を渡すための変数
varying vec3 vPos;
varying vec3 vNrm;
 
void main(void)
{
    // 頂点位置と法線
    vPos = (gl_ModelViewMatrix*gl_Vertex).xyz;
    vNrm = gl_NormalMatrix*gl_Normal;
 
    // 描画頂点位置
    gl_Position = ftransform();
}
);

シェーダのリンク

必要なシェーダ(バーテックス,ジオメトリ,フラグメント)をコンパイル後,それらをリンクして, 1つのGLSLプログラムにまとめる.

  1. glCreateProgram()でGLSLプログラムオブジェクトを生成.
    GLuint glCreateProgram(void);
    ここで生成したプログラムオブジェクトはGLSLでの描画切替にも用いるので,OpenGL描画関数にこの変数を渡せるようにしておくこと.
  2. glAttachShader()でシェーダオブジェクトをプログラムオブジェクトに関連付ける.
    void glAttachShader(GLuint program, GLuint shader);
    programはプログラムオブジェクト(glCreateProgramの返値),shaderは関連付けたいシェーダオブジェクトを指定する.
  3. glLinkProgram()でプログラムをリンクする.
    void glLinkProgram(GLuint program);
    リンクがうまくいったかどうかは,glGetProgramiv()にプログラムオブジェクトとGL_LINK_STATUSを渡すことで調べることができる.

プログラムのリンクを行う関数の例を以下に示す.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
/*!
 * バーテックスとフラグメントシェーダで構成されるGLSLプログラム作成
 * @param[in] vs バーテックスシェーダオブジェクト
 * @param[in] fs フラグメントシェーダオブジェクト
 * @return GLSLプログラムオブジェクト
 */
inline GLuint LinkGLSLProgram(GLuint vs, GLuint fs)
{
    // プログラムオブジェクト作成
    GLuint program = glCreateProgram();
 
    // シェーダオブジェクトを登録
    glAttachShader(program, vs);
    glAttachShader(program, fs);
 
    // プログラムのリンク
    glLinkProgram(program);
 
    // エラー出力
    GLint charsWritten, infoLogLength;
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
 
    char * infoLog = new char[infoLogLength];
    glGetProgramInfoLog(program, infoLogLength, &charsWritten, infoLog);
    printf(infoLog);
    delete [] infoLog;
 
    // リンカテスト
    GLint linkSucceed = GL_FALSE;
    glGetProgramiv(program, GL_LINK_STATUS, &linkSucceed);
    if(linkSucceed == GL_FALSE){
        glDeleteProgram(program);
        return 0;
    }
 
    return program;
}

シェーダソースファイルからコンパイル,リンクを行う関数の例を以下に示す. 返値はrxGLSL型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
struct rxGLSL
{
    string VertProg;    //!< 頂点プログラムファイル名
    string GeomProg;    //!< ジオメトリプログラムファイル名
    string FragProg;    //!< フラグメントプログラムファイル名
    string Name;        //!< シェーダ名
    GLuint Prog;        //!< シェーダID
};
 
/*!
 * GLSLのコンパイル・リンク(ファイルより)
 * @param[in] vs 頂点シェーダファイルパス
 * @param[in] fs フラグメントシェーダファイルパス
 * @param[in] name プログラム名
 * @return GLSLオブジェクト
 */
inline rxGLSL CreateGLSLFromFile(const string &vs, const string &fs, const string &name)
{
    rxGLSL glsl;
    glsl.VertProg = vs;
    glsl.FragProg = fs;
    glsl.Name = name;
 
    GLuint v, f;
    v = CompileGLSLShaderFromFile(GL_VERTEX_SHADER, vs.c_str());
    f = CompileGLSLShaderFromFile(GL_FRAGMENT_SHADER, fs.c_str());
 
    glsl.Prog = LinkGLSLProgram(v, f);
 
    return glsl;
}

シェーダによる描画

上記の関数を用いてアプリケーション初期時に以下のようにシェーダプログラムをビルドした後,

rxGLSL g_GLSL = CreateGLSLFromFile("phong.vs", "phong.fs", "phong");

描画時にglUseProgram()を用いてGLSL描画に切り替える.

void glUseProgram(GLuint program);
  1
  2
  3
  4
  5
  6
    glUseProgram(g_GLSL.Prog);
    glUniform3f(glGetUniformLocation(g_GLSL.Prog, "eyePosition"), eye_pos[0], eye_pos[1], eye_pos[2]);
 
    glutSolidTeapot(0.5);
 
    glUseProgram(0);

glUniform*()はGLSLシェーダのuniform変数に値を渡す命令であり, その種類は下記参照.

glUniform

シェーダ内のuniform変数に値を渡すのに用いるOpenGLの命令.

変数,定数を用いた受け渡し

  • 浮動小数点数
    void glUniform1f(GLint location, GLfloat v0);
    void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
    void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
    void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
  • 整数
    void glUniform1i(GLint location, GLint v0);
    void glUniform2i(GLint location, GLint v0, GLint v1);
    void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2);
    void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);

locationにglGetUniformLocationで取得した変数の位置,v0-v3に受け渡す値を指定する. f接尾辞の場合はfloat,vec2,vec3,vec4とその配列に, i接尾辞の場合はint,ivec2,ivec3,ivec4とその配列に値を渡すのに用いられる.

配列を用いた受け渡し

  • 浮動小数点数
    void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
    void glUniform2fv(GLint location, GLsizei count, const GLfloat *value);
    void glUniform3fv(GLint location, GLsizei count, const GLfloat *value);
    void glUniform4fv(GLint location, GLsizei count, const GLfloat *value);
  • 整数
    void glUniform1iv(GLint location, GLsizei count, const GLint *value);
    void glUniform2iv(GLint location, GLsizei count, const GLint *value);
    void glUniform3iv(GLint location, GLsizei count, const GLint *value);
    void glUniform4iv(GLint location, GLsizei count, const GLint *value);

locationにglGetUniformLocationで取得した変数の位置,countは修正する要素の数(GLSL側のuniform変数が配列でないならば1でなければならない), valueは要素数countの配列のポインタでこの配列の中身が渡される.

行列の値の受け渡し

void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);

上の3つは正方行列用でそれぞれ,2x2,3x3,4x4行列の値(要素数4,9,16)を書き換える.

locationはglGetUniformLocationで取得した変数の位置,countは修正する要素の数(GLSL側のuniform変数が配列でないならば1でなければならない), transposeにGL_TRUEを指定した場合,行列の要素はrow majorで格納され,GL_FALSEを指定すればcolumn majorで格納される. valueは要素数countの配列のポインタでこの配列の中身が渡される.

ベクトル型

型名説明
vec2,vec3,vec42D,3D,4D浮動小数点数ベクトル(float)
ivec2,ivec3,ivec42D,3D,4D整数ベクトル(int)
bvec2,bvec3,bvec42D,3D,4Dブール値ベクトル(bool)

行列型

型名説明
mat2,mat3,mat42x2,3x3,4x4行列(float)

テクスチャ型

型名説明
sampler1D,sampler2D,sampler3D1D,2D,3Dテクスチャ(GL_TEXTURE_1D,2D,3Dに対応)
samplerCubeキューブマップテクスチャ(TEXTURE_CUBE_MAPに対応)
sampler1Dshadow,sampler2Dshadow1D,2Dデプステクスチャ(GL_TEXTURE_1D,2DでGL_DEPTH_COMPONENTのものに対応)
sampler2DRect2のn乗以外の大きさのテクスチャ(GL_TEXTURE_RECTANGLEに対応,要 GL_ARB_texture_rectangle)
sampler2DRectShadow2のn乗以外の大きさのデプステクスチャ(要 GL_ARB_texture_rectangle)
sampler1DArray, sampler2DArray1D,2Dテクスチャ配列(TEXTURE_1D,2D_ARRAYに対応,要 GL_EXT_texture_array)
sampler1DArrayShadow,sampler2DArrayShadow1D,2Dデプステクスチャ配列(要 GL_EXT_texture_array)

ビルトイン変数

定数(attribute)

変数名説明
gl_Vertexvec4頂点座標
gl_Normalvec3頂点法線
gl_Colorvec4頂点色
gl_MultiTexCoord*vec4テクスチャ*のテクスチャ座標
gl_ModelViewMatrixmat4モデルビュー行列
gl_ProjectionMatrixmat4プロジェクション行列
gl_ModelViewProjectionMatrixmat4モデルビューとプロジェクション行列の積
gl_NormalMatrixmat3モデルビュー行列の逆行列(法線変換用)

変数(uniform)

シェーダー間のデータ受け渡しに使える.

変数名説明
gl_FrontColorvec4表面の色
gl_BackColorvec4背面の色
gl_TexCoord[]vec4テクスチャ座標
gl_Positionvec4頂点位置(頂点シェーダのみ)
gl_FragColorvec4フレームバッファに書き込まれる色(フラグメントシェーダのみ)
gl_FragDepthfloatフレームバッファに書き込まれるデプス値(フラグメントシェーダのみ)

TIPS

キャスト

warning C7503: OpenGL does not allow C-style casts

Cとはキャストのやり方が違っているので,例えば,

i = (int)x;

ではなく,

i = int(x);

とする.


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2011-10-27 (木) 15:09:14 (3208d)