- 追加された行はこの色です。
- 削除された行はこの色です。
- GLSLについて へ行く。
----
#contents
----
*GLSL [#ga5e9b27]
OpenGL Shading Language
*GLSLプログラミング [#if3e7c73]
GLSLプログラミングを行うためには実行プラットフォームが少なくとも,
GL_ARB_vertex_shader,GL_ARB_fragment_shader拡張に対応している必要がある.
OpenGL拡張の対応を調べたり,拡張を使えるようにするにはGLEWを用いるのが一番簡単である
(参照:[[GLEWについて]]).
GLSLシェーダはいくつかのOpenGL命令を用いてアプリケーション実行時にロード,コンパイル,リンクされる.
***シェーダのコンパイル [#w4b7f675]
GLSLシェーダをOpenGLアプリケーション中でロード,コンパイルする手順は以下.
+glCreateShader()でシェーダオブジェクトを生成する.
GLuint glCreateShader(GLenum shaderType);
shaderTypeにはGL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADERのいずれかを指定する.
+シェーダオブジェクトにシェーダソースコード(文字列)をセットする.
void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
shaderはシェーダオブジェクト(glCreateShaderの返値),countは文字列の数,stringがソースコードを格納した文字列配列,
lengthはstringの長さを示し,NULLを指定すればstringがnull terminatedであることを示す.
+シェーダをコンパイルする.
void glCompileShader(GLuint shader);
これらの手順を関数にまとめたものを以下に示す.
#code(C){{
/*!
* 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など)に記述したシェーダコードを読み込んで文字列化する関数の例を以下に示す.
#code(C){{
/*!
* 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;
}
}}
ファイルから読み込むほかに,以下のようにしてソースコード中に直接記述する方法もある.
#code(C){{
#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();
}
);
}}
***シェーダのリンク [#b94c2cae]
必要なシェーダ(バーテックス,ジオメトリ,フラグメント)をコンパイル後,それらをリンクして,
1つのGLSLプログラムにまとめる.
+glCreateProgram()でGLSLプログラムオブジェクトを生成.
GLuint glCreateProgram(void);
ここで生成したプログラムオブジェクトはGLSLでの描画切替にも用いるので,OpenGL描画関数にこの変数を渡せるようにしておくこと.
+glAttachShader()でシェーダオブジェクトをプログラムオブジェクトに関連付ける.
void glAttachShader(GLuint program, GLuint shader);
programはプログラムオブジェクト(glCreateProgramの返値),shaderは関連付けたいシェーダオブジェクトを指定する.
+glLinkProgram()でプログラムをリンクする.
void glLinkProgram(GLuint program);
リンクがうまくいったかどうかは,glGetProgramiv()にプログラムオブジェクトとGL_LINK_STATUSを渡すことで調べることができる.
プログラムのリンクを行う関数の例を以下に示す.
#code(C){{
/*!
* バーテックスとフラグメントシェーダで構成される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型の変数となっており,シェーダファイル名,プログラムオブジェクトなどを格納する構造体である.
#code(C){{
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;
}
}}
***シェーダによる描画 [#f7624215]
上記の関数を用いてアプリケーション初期時に以下のようにシェーダプログラムをビルドした後,
rxGLSL g_GLSL = CreateGLSLFromFile("phong.vs", "phong.fs", "phong");
描画時に[[glUseProgram():http://www.opengl.org/sdk/docs/man/xhtml/glUseProgram.xml]]を用いてGLSL描画に切り替える.
void glUseProgram(GLuint program);
#code(C){{
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*():http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml]]はGLSLシェーダのuniform変数に値を渡す命令であり,
その種類は下記参照.
*glUniform [#v798e952]
シェーダ内のuniform変数に値を渡すのに用いるOpenGLの命令.
***変数,定数を用いた受け渡し [#p48f952a]
-浮動小数点数
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とその配列に値を渡すのに用いられる.
***配列を用いた受け渡し [#ffcf416f]
-浮動小数点数
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の配列のポインタでこの配列の中身が渡される.
***行列の値の受け渡し [#w7fb836e]
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の配列のポインタでこの配列の中身が渡される.
*型 [#dec344e5]
***ベクトル型 [#r7484283]
|型名|説明|
|vec2,vec3,vec4|2D,3D,4D浮動小数点数ベクトル(float)|
|ivec2,ivec3,ivec4|2D,3D,4D整数ベクトル(int)|
|bvec2,bvec3,bvec4|2D,3D,4Dブール値ベクトル(bool)|
***行列型 [#k08913dd]
|型名|説明|
|mat2,mat3,mat4|2x2,3x3,4x4行列(float)|
***テクスチャ型 [#tb04abd6]
|型名|説明|
|sampler1D,sampler2D,sampler3D|1D,2D,3Dテクスチャ(GL_TEXTURE_1D,2D,3Dに対応)|
|samplerCube|キューブマップテクスチャ(TEXTURE_CUBE_MAPに対応)|
|sampler1Dshadow,sampler2Dshadow|1D,2Dデプステクスチャ(GL_TEXTURE_1D,2DでGL_DEPTH_COMPONENTのものに対応)|
|sampler2DRect|2のn乗以外の大きさのテクスチャ(GL_TEXTURE_RECTANGLEに対応,要 [[GL_ARB_texture_rectangle:http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt]])|
|sampler2DRectShadow|2のn乗以外の大きさのデプステクスチャ(要 [[GL_ARB_texture_rectangle:http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt]])|
|sampler1DArray, sampler2DArray|1D,2Dテクスチャ配列(TEXTURE_1D,2D_ARRAYに対応,要 [[GL_EXT_texture_array:http://www.opengl.org/registry/specs/EXT/texture_array.txt]])|
|sampler1DArrayShadow,sampler2DArrayShadow|1D,2Dデプステクスチャ配列(要 [[GL_EXT_texture_array:http://www.opengl.org/registry/specs/EXT/texture_array.txt]])|
*ビルトイン変数 [#h208bef9]
***定数(attribute) [#o090f462]
|変数名|型|説明|
|gl_Vertex|vec4|頂点座標|
|gl_Normal|vec3|頂点法線|
|gl_Color|vec4|頂点色|
|gl_MultiTexCoord*|vec4|テクスチャ*のテクスチャ座標|
|>|>||
|gl_ModelViewMatrix|mat4|モデルビュー行列|
|gl_ProjectionMatrix|mat4|プロジェクション行列|
|gl_ModelViewProjectionMatrix|mat4|モデルビューとプロジェクション行列の積|
|gl_NormalMatrix|mat3|モデルビュー行列の逆行列(法線変換用)|
***変数(uniform) [#wc4b3785]
シェーダー間のデータ受け渡しに使える.
|変数名|型|説明|
|gl_FrontColor|vec4|表面の色|
|gl_BackColor|vec4|背面の色|
|gl_TexCoord[]|vec4|テクスチャ座標|
|>|>||
|gl_Position|vec4|頂点位置(頂点シェーダのみ)|
|gl_FragColor|vec4|フレームバッファに書き込まれる色(フラグメントシェーダのみ)|
|gl_FragDepth|float|フレームバッファに書き込まれるデプス値(フラグメントシェーダのみ)|
*TIPS [#r5fa7025]
**キャスト [#k76bf285]
warning C7503: OpenGL does not allow C-style casts
Cとはキャストのやり方が違っているので,例えば,
i = (int)x;
ではなく,
i = int(x);
とする.