GLSL†OpenGL Shading Language GLSLプログラミング†GLSLプログラミングを行うためには実行プラットフォームが少なくとも, GL_ARB_vertex_shader,GL_ARB_fragment_shader拡張に対応している必要がある. OpenGL拡張の対応を調べたり,拡張を使えるようにするにはGLEWを用いるのが一番簡単である (参照:GLEWについて). GLSLシェーダはいくつかのOpenGL命令を用いてアプリケーション実行時にロード,コンパイル,リンクされる. シェーダのコンパイル†GLSLシェーダをOpenGLアプリケーション中でロード,コンパイルする手順は以下.
これらの手順を関数にまとめたものを以下に示す. /*! * 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など)に記述したシェーダコードを読み込んで文字列化する関数の例を以下に示す. /*! * 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; } ファイルから読み込むほかに,以下のようにしてソースコード中に直接記述する方法もある. #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プログラムにまとめる.
プログラムのリンクを行う関数の例を以下に示す. /*! * バーテックスとフラグメントシェーダで構成される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型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である. 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); 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の命令. 変数,定数を用いた受け渡し†
locationにglGetUniformLocationで取得した変数の位置,v0-v3に受け渡す値を指定する. f接尾辞の場合はfloat,vec2,vec3,vec4とその配列に, i接尾辞の場合はint,ivec2,ivec3,ivec4とその配列に値を渡すのに用いられる. 配列を用いた受け渡し†
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の配列のポインタでこの配列の中身が渡される. 型†ベクトル型†
行列型†
テクスチャ型†
ビルトイン変数†定数(attribute)†
変数(uniform)†シェーダー間のデータ受け渡しに使える.
TIPS†キャスト†warning C7503: OpenGL does not allow C-style casts Cとはキャストのやり方が違っているので,例えば, i = (int)x; ではなく, i = int(x); とする. |